diff --git a/Net461/.vs/ICSharpCode.TextEditor/DesignTimeBuild/.dtbcache.v2 b/Net461/.vs/ICSharpCode.TextEditor/DesignTimeBuild/.dtbcache.v2 new file mode 100644 index 0000000..5dd65da Binary files /dev/null and b/Net461/.vs/ICSharpCode.TextEditor/DesignTimeBuild/.dtbcache.v2 differ diff --git a/GitExtensions.settings b/Net461/GitExtensions.settings similarity index 100% rename from GitExtensions.settings rename to Net461/GitExtensions.settings diff --git a/ICSharpCode.TextEditor.Sample/ICSharpCode.TextEditor.Sample.csproj b/Net461/ICSharpCode.TextEditor.Sample/ICSharpCode.TextEditor.Sample.csproj similarity index 100% rename from ICSharpCode.TextEditor.Sample/ICSharpCode.TextEditor.Sample.csproj rename to Net461/ICSharpCode.TextEditor.Sample/ICSharpCode.TextEditor.Sample.csproj diff --git a/ICSharpCode.TextEditor.Sample/Program.cs b/Net461/ICSharpCode.TextEditor.Sample/Program.cs similarity index 100% rename from ICSharpCode.TextEditor.Sample/Program.cs rename to Net461/ICSharpCode.TextEditor.Sample/Program.cs diff --git a/ICSharpCode.TextEditor.Sample/Properties/AssemblyInfo.cs b/Net461/ICSharpCode.TextEditor.Sample/Properties/AssemblyInfo.cs similarity index 100% rename from ICSharpCode.TextEditor.Sample/Properties/AssemblyInfo.cs rename to Net461/ICSharpCode.TextEditor.Sample/Properties/AssemblyInfo.cs diff --git a/ICSharpCode.TextEditor.Sample/SampleForm.Designer.cs b/Net461/ICSharpCode.TextEditor.Sample/SampleForm.Designer.cs similarity index 100% rename from ICSharpCode.TextEditor.Sample/SampleForm.Designer.cs rename to Net461/ICSharpCode.TextEditor.Sample/SampleForm.Designer.cs diff --git a/ICSharpCode.TextEditor.Sample/SampleForm.cs b/Net461/ICSharpCode.TextEditor.Sample/SampleForm.cs similarity index 100% rename from ICSharpCode.TextEditor.Sample/SampleForm.cs rename to Net461/ICSharpCode.TextEditor.Sample/SampleForm.cs diff --git a/ICSharpCode.TextEditor.Sample/SampleForm.resx b/Net461/ICSharpCode.TextEditor.Sample/SampleForm.resx similarity index 100% rename from ICSharpCode.TextEditor.Sample/SampleForm.resx rename to Net461/ICSharpCode.TextEditor.Sample/SampleForm.resx diff --git a/ICSharpCode.TextEditor.Sample/app.config b/Net461/ICSharpCode.TextEditor.Sample/app.config similarity index 100% rename from ICSharpCode.TextEditor.Sample/app.config rename to Net461/ICSharpCode.TextEditor.Sample/app.config diff --git a/ICSharpCode.TextEditor.sln b/Net461/ICSharpCode.TextEditor.sln similarity index 100% rename from ICSharpCode.TextEditor.sln rename to Net461/ICSharpCode.TextEditor.sln diff --git a/Project/ICSharpCode.TextEditor.csproj b/Net461/Project/ICSharpCode.TextEditor.csproj similarity index 100% rename from Project/ICSharpCode.TextEditor.csproj rename to Net461/Project/ICSharpCode.TextEditor.csproj diff --git a/Project/Properties/AssemblyInfo.cs b/Net461/Project/Properties/AssemblyInfo.cs similarity index 100% rename from Project/Properties/AssemblyInfo.cs rename to Net461/Project/Properties/AssemblyInfo.cs diff --git a/Project/Resources/ANTLR.xshd b/Net461/Project/Resources/ANTLR.xshd similarity index 100% rename from Project/Resources/ANTLR.xshd rename to Net461/Project/Resources/ANTLR.xshd diff --git a/Project/Resources/ASPX.xshd b/Net461/Project/Resources/ASPX.xshd similarity index 100% rename from Project/Resources/ASPX.xshd rename to Net461/Project/Resources/ASPX.xshd diff --git a/Project/Resources/ActionScript.xshd b/Net461/Project/Resources/ActionScript.xshd similarity index 100% rename from Project/Resources/ActionScript.xshd rename to Net461/Project/Resources/ActionScript.xshd diff --git a/Project/Resources/Ada.xshd b/Net461/Project/Resources/Ada.xshd similarity index 100% rename from Project/Resources/Ada.xshd rename to Net461/Project/Resources/Ada.xshd diff --git a/Project/Resources/Assembly.xshd b/Net461/Project/Resources/Assembly.xshd similarity index 100% rename from Project/Resources/Assembly.xshd rename to Net461/Project/Resources/Assembly.xshd diff --git a/Project/Resources/AutoHotkey.xshd b/Net461/Project/Resources/AutoHotkey.xshd similarity index 100% rename from Project/Resources/AutoHotkey.xshd rename to Net461/Project/Resources/AutoHotkey.xshd diff --git a/Project/Resources/Batch.xshd b/Net461/Project/Resources/Batch.xshd similarity index 100% rename from Project/Resources/Batch.xshd rename to Net461/Project/Resources/Batch.xshd diff --git a/Project/Resources/Boo.xshd b/Net461/Project/Resources/Boo.xshd similarity index 100% rename from Project/Resources/Boo.xshd rename to Net461/Project/Resources/Boo.xshd diff --git a/Project/Resources/C#.xshd b/Net461/Project/Resources/C#.xshd similarity index 100% rename from Project/Resources/C#.xshd rename to Net461/Project/Resources/C#.xshd diff --git a/Project/Resources/C++.xshd b/Net461/Project/Resources/C++.xshd similarity index 100% rename from Project/Resources/C++.xshd rename to Net461/Project/Resources/C++.xshd diff --git a/Project/Resources/C.xshd b/Net461/Project/Resources/C.xshd similarity index 100% rename from Project/Resources/C.xshd rename to Net461/Project/Resources/C.xshd diff --git a/Project/Resources/CSS.xshd b/Net461/Project/Resources/CSS.xshd similarity index 100% rename from Project/Resources/CSS.xshd rename to Net461/Project/Resources/CSS.xshd diff --git a/Project/Resources/Ceylon.xshd b/Net461/Project/Resources/Ceylon.xshd similarity index 100% rename from Project/Resources/Ceylon.xshd rename to Net461/Project/Resources/Ceylon.xshd diff --git a/Project/Resources/ChucK.xshd b/Net461/Project/Resources/ChucK.xshd similarity index 100% rename from Project/Resources/ChucK.xshd rename to Net461/Project/Resources/ChucK.xshd diff --git a/Project/Resources/Clojure.xshd b/Net461/Project/Resources/Clojure.xshd similarity index 100% rename from Project/Resources/Clojure.xshd rename to Net461/Project/Resources/Clojure.xshd diff --git a/Project/Resources/Cocoa.xshd b/Net461/Project/Resources/Cocoa.xshd similarity index 100% rename from Project/Resources/Cocoa.xshd rename to Net461/Project/Resources/Cocoa.xshd diff --git a/Project/Resources/CoffeeScript.xshd b/Net461/Project/Resources/CoffeeScript.xshd similarity index 100% rename from Project/Resources/CoffeeScript.xshd rename to Net461/Project/Resources/CoffeeScript.xshd diff --git a/Project/Resources/Cool.xshd b/Net461/Project/Resources/Cool.xshd similarity index 100% rename from Project/Resources/Cool.xshd rename to Net461/Project/Resources/Cool.xshd diff --git a/Project/Resources/D.xshd b/Net461/Project/Resources/D.xshd similarity index 100% rename from Project/Resources/D.xshd rename to Net461/Project/Resources/D.xshd diff --git a/Project/Resources/Dart.xshd b/Net461/Project/Resources/Dart.xshd similarity index 100% rename from Project/Resources/Dart.xshd rename to Net461/Project/Resources/Dart.xshd diff --git a/Project/Resources/Delphi.xshd b/Net461/Project/Resources/Delphi.xshd similarity index 100% rename from Project/Resources/Delphi.xshd rename to Net461/Project/Resources/Delphi.xshd diff --git a/Project/Resources/Eiffel.xshd b/Net461/Project/Resources/Eiffel.xshd similarity index 100% rename from Project/Resources/Eiffel.xshd rename to Net461/Project/Resources/Eiffel.xshd diff --git a/Project/Resources/Elixir.xshd b/Net461/Project/Resources/Elixir.xshd similarity index 100% rename from Project/Resources/Elixir.xshd rename to Net461/Project/Resources/Elixir.xshd diff --git a/Project/Resources/Erlang.xshd b/Net461/Project/Resources/Erlang.xshd similarity index 100% rename from Project/Resources/Erlang.xshd rename to Net461/Project/Resources/Erlang.xshd diff --git a/Project/Resources/F#.xshd b/Net461/Project/Resources/F#.xshd similarity index 100% rename from Project/Resources/F#.xshd rename to Net461/Project/Resources/F#.xshd diff --git a/Project/Resources/Falcon.xshd b/Net461/Project/Resources/Falcon.xshd similarity index 100% rename from Project/Resources/Falcon.xshd rename to Net461/Project/Resources/Falcon.xshd diff --git a/Project/Resources/Fantom.xshd b/Net461/Project/Resources/Fantom.xshd similarity index 100% rename from Project/Resources/Fantom.xshd rename to Net461/Project/Resources/Fantom.xshd diff --git a/Project/Resources/Fortran95.xshd b/Net461/Project/Resources/Fortran95.xshd similarity index 100% rename from Project/Resources/Fortran95.xshd rename to Net461/Project/Resources/Fortran95.xshd diff --git a/Project/Resources/Go.xshd b/Net461/Project/Resources/Go.xshd similarity index 100% rename from Project/Resources/Go.xshd rename to Net461/Project/Resources/Go.xshd diff --git a/Project/Resources/Groovy.xshd b/Net461/Project/Resources/Groovy.xshd similarity index 100% rename from Project/Resources/Groovy.xshd rename to Net461/Project/Resources/Groovy.xshd diff --git a/Project/Resources/Gui4Cli.xshd b/Net461/Project/Resources/Gui4Cli.xshd similarity index 100% rename from Project/Resources/Gui4Cli.xshd rename to Net461/Project/Resources/Gui4Cli.xshd diff --git a/Project/Resources/HTML.xshd b/Net461/Project/Resources/HTML.xshd similarity index 100% rename from Project/Resources/HTML.xshd rename to Net461/Project/Resources/HTML.xshd diff --git a/Project/Resources/Haskell.xshd b/Net461/Project/Resources/Haskell.xshd similarity index 100% rename from Project/Resources/Haskell.xshd rename to Net461/Project/Resources/Haskell.xshd diff --git a/Project/Resources/Haxe.xshd b/Net461/Project/Resources/Haxe.xshd similarity index 100% rename from Project/Resources/Haxe.xshd rename to Net461/Project/Resources/Haxe.xshd diff --git a/Project/Resources/ICSharpCode.TextEditor.snk b/Net461/Project/Resources/ICSharpCode.TextEditor.snk similarity index 100% rename from Project/Resources/ICSharpCode.TextEditor.snk rename to Net461/Project/Resources/ICSharpCode.TextEditor.snk diff --git a/Project/Resources/ILYC.xshd b/Net461/Project/Resources/ILYC.xshd similarity index 100% rename from Project/Resources/ILYC.xshd rename to Net461/Project/Resources/ILYC.xshd diff --git a/Project/Resources/INI.xshd b/Net461/Project/Resources/INI.xshd similarity index 100% rename from Project/Resources/INI.xshd rename to Net461/Project/Resources/INI.xshd diff --git a/Project/Resources/Icon.xshd b/Net461/Project/Resources/Icon.xshd similarity index 100% rename from Project/Resources/Icon.xshd rename to Net461/Project/Resources/Icon.xshd diff --git a/Project/Resources/Io.xshd b/Net461/Project/Resources/Io.xshd similarity index 100% rename from Project/Resources/Io.xshd rename to Net461/Project/Resources/Io.xshd diff --git a/Project/Resources/JSON.xshd b/Net461/Project/Resources/JSON.xshd similarity index 100% rename from Project/Resources/JSON.xshd rename to Net461/Project/Resources/JSON.xshd diff --git a/Project/Resources/Java.xshd b/Net461/Project/Resources/Java.xshd similarity index 100% rename from Project/Resources/Java.xshd rename to Net461/Project/Resources/Java.xshd diff --git a/Project/Resources/JavaScript.xshd b/Net461/Project/Resources/JavaScript.xshd similarity index 100% rename from Project/Resources/JavaScript.xshd rename to Net461/Project/Resources/JavaScript.xshd diff --git a/Project/Resources/Julia.xshd b/Net461/Project/Resources/Julia.xshd similarity index 100% rename from Project/Resources/Julia.xshd rename to Net461/Project/Resources/Julia.xshd diff --git a/Project/Resources/Just BASIC.xshd b/Net461/Project/Resources/Just BASIC.xshd similarity index 100% rename from Project/Resources/Just BASIC.xshd rename to Net461/Project/Resources/Just BASIC.xshd diff --git a/Project/Resources/KiXtart.xshd b/Net461/Project/Resources/KiXtart.xshd similarity index 100% rename from Project/Resources/KiXtart.xshd rename to Net461/Project/Resources/KiXtart.xshd diff --git a/Project/Resources/Kotlin.xshd b/Net461/Project/Resources/Kotlin.xshd similarity index 100% rename from Project/Resources/Kotlin.xshd rename to Net461/Project/Resources/Kotlin.xshd diff --git a/Project/Resources/Lean.xshd b/Net461/Project/Resources/Lean.xshd similarity index 100% rename from Project/Resources/Lean.xshd rename to Net461/Project/Resources/Lean.xshd diff --git a/Project/Resources/Lisp.xshd b/Net461/Project/Resources/Lisp.xshd similarity index 100% rename from Project/Resources/Lisp.xshd rename to Net461/Project/Resources/Lisp.xshd diff --git a/Project/Resources/Lua.xshd b/Net461/Project/Resources/Lua.xshd similarity index 100% rename from Project/Resources/Lua.xshd rename to Net461/Project/Resources/Lua.xshd diff --git a/Project/Resources/Markdown.xshd b/Net461/Project/Resources/Markdown.xshd similarity index 100% rename from Project/Resources/Markdown.xshd rename to Net461/Project/Resources/Markdown.xshd diff --git a/Project/Resources/Mode.xsd b/Net461/Project/Resources/Mode.xsd similarity index 100% rename from Project/Resources/Mode.xsd rename to Net461/Project/Resources/Mode.xsd diff --git a/Project/Resources/Nemerle.xshd b/Net461/Project/Resources/Nemerle.xshd similarity index 100% rename from Project/Resources/Nemerle.xshd rename to Net461/Project/Resources/Nemerle.xshd diff --git a/Project/Resources/Nim.xshd b/Net461/Project/Resources/Nim.xshd similarity index 100% rename from Project/Resources/Nim.xshd rename to Net461/Project/Resources/Nim.xshd diff --git a/Project/Resources/OCaml.xshd b/Net461/Project/Resources/OCaml.xshd similarity index 100% rename from Project/Resources/OCaml.xshd rename to Net461/Project/Resources/OCaml.xshd diff --git a/Project/Resources/Objective-C.xshd b/Net461/Project/Resources/Objective-C.xshd similarity index 100% rename from Project/Resources/Objective-C.xshd rename to Net461/Project/Resources/Objective-C.xshd diff --git a/Project/Resources/PHP.xshd b/Net461/Project/Resources/PHP.xshd similarity index 100% rename from Project/Resources/PHP.xshd rename to Net461/Project/Resources/PHP.xshd diff --git a/Project/Resources/ParaSail.xshd b/Net461/Project/Resources/ParaSail.xshd similarity index 100% rename from Project/Resources/ParaSail.xshd rename to Net461/Project/Resources/ParaSail.xshd diff --git a/Project/Resources/Pascal.xshd b/Net461/Project/Resources/Pascal.xshd similarity index 100% rename from Project/Resources/Pascal.xshd rename to Net461/Project/Resources/Pascal.xshd diff --git a/Project/Resources/Patch-Mode.xshd b/Net461/Project/Resources/Patch-Mode.xshd similarity index 100% rename from Project/Resources/Patch-Mode.xshd rename to Net461/Project/Resources/Patch-Mode.xshd diff --git a/Project/Resources/Pike.xshd b/Net461/Project/Resources/Pike.xshd similarity index 100% rename from Project/Resources/Pike.xshd rename to Net461/Project/Resources/Pike.xshd diff --git a/Project/Resources/PowerShell.xshd b/Net461/Project/Resources/PowerShell.xshd similarity index 100% rename from Project/Resources/PowerShell.xshd rename to Net461/Project/Resources/PowerShell.xshd diff --git a/Project/Resources/Prolog.xshd b/Net461/Project/Resources/Prolog.xshd similarity index 100% rename from Project/Resources/Prolog.xshd rename to Net461/Project/Resources/Prolog.xshd diff --git a/Project/Resources/PureScript.xshd b/Net461/Project/Resources/PureScript.xshd similarity index 100% rename from Project/Resources/PureScript.xshd rename to Net461/Project/Resources/PureScript.xshd diff --git a/Project/Resources/Python.xshd b/Net461/Project/Resources/Python.xshd similarity index 100% rename from Project/Resources/Python.xshd rename to Net461/Project/Resources/Python.xshd diff --git a/Project/Resources/R.xshd b/Net461/Project/Resources/R.xshd similarity index 100% rename from Project/Resources/R.xshd rename to Net461/Project/Resources/R.xshd diff --git a/Project/Resources/Registry.xshd b/Net461/Project/Resources/Registry.xshd similarity index 100% rename from Project/Resources/Registry.xshd rename to Net461/Project/Resources/Registry.xshd diff --git a/Project/Resources/Resource.xshd b/Net461/Project/Resources/Resource.xshd similarity index 100% rename from Project/Resources/Resource.xshd rename to Net461/Project/Resources/Resource.xshd diff --git a/Project/Resources/Rexx.xshd b/Net461/Project/Resources/Rexx.xshd similarity index 100% rename from Project/Resources/Rexx.xshd rename to Net461/Project/Resources/Rexx.xshd diff --git a/Project/Resources/RightArrow.cur b/Net461/Project/Resources/RightArrow.cur similarity index 100% rename from Project/Resources/RightArrow.cur rename to Net461/Project/Resources/RightArrow.cur diff --git a/Project/Resources/Rust.xshd b/Net461/Project/Resources/Rust.xshd similarity index 100% rename from Project/Resources/Rust.xshd rename to Net461/Project/Resources/Rust.xshd diff --git a/Project/Resources/SQF.xshd b/Net461/Project/Resources/SQF.xshd similarity index 100% rename from Project/Resources/SQF.xshd rename to Net461/Project/Resources/SQF.xshd diff --git a/Project/Resources/SQL.xshd b/Net461/Project/Resources/SQL.xshd similarity index 100% rename from Project/Resources/SQL.xshd rename to Net461/Project/Resources/SQL.xshd diff --git a/Project/Resources/Scala.xshd b/Net461/Project/Resources/Scala.xshd similarity index 100% rename from Project/Resources/Scala.xshd rename to Net461/Project/Resources/Scala.xshd diff --git a/Project/Resources/Scheme.xshd b/Net461/Project/Resources/Scheme.xshd similarity index 100% rename from Project/Resources/Scheme.xshd rename to Net461/Project/Resources/Scheme.xshd diff --git a/Project/Resources/Solidity.xshd b/Net461/Project/Resources/Solidity.xshd similarity index 100% rename from Project/Resources/Solidity.xshd rename to Net461/Project/Resources/Solidity.xshd diff --git a/Project/Resources/Spike.xshd b/Net461/Project/Resources/Spike.xshd similarity index 100% rename from Project/Resources/Spike.xshd rename to Net461/Project/Resources/Spike.xshd diff --git a/Project/Resources/Swift.xshd b/Net461/Project/Resources/Swift.xshd similarity index 100% rename from Project/Resources/Swift.xshd rename to Net461/Project/Resources/Swift.xshd diff --git a/Project/Resources/SyntaxModes.xml b/Net461/Project/Resources/SyntaxModes.xml similarity index 100% rename from Project/Resources/SyntaxModes.xml rename to Net461/Project/Resources/SyntaxModes.xml diff --git a/Project/Resources/TCL.xshd b/Net461/Project/Resources/TCL.xshd similarity index 100% rename from Project/Resources/TCL.xshd rename to Net461/Project/Resources/TCL.xshd diff --git a/Project/Resources/Tex-Mode.xshd b/Net461/Project/Resources/Tex-Mode.xshd similarity index 100% rename from Project/Resources/Tex-Mode.xshd rename to Net461/Project/Resources/Tex-Mode.xshd diff --git a/Project/Resources/TextEditorControl.bmp b/Net461/Project/Resources/TextEditorControl.bmp similarity index 100% rename from Project/Resources/TextEditorControl.bmp rename to Net461/Project/Resources/TextEditorControl.bmp diff --git a/Project/Resources/Thrift.xshd b/Net461/Project/Resources/Thrift.xshd similarity index 100% rename from Project/Resources/Thrift.xshd rename to Net461/Project/Resources/Thrift.xshd diff --git a/Project/Resources/TypeScript.xshd b/Net461/Project/Resources/TypeScript.xshd similarity index 100% rename from Project/Resources/TypeScript.xshd rename to Net461/Project/Resources/TypeScript.xshd diff --git a/Project/Resources/VB.NET.xshd b/Net461/Project/Resources/VB.NET.xshd similarity index 100% rename from Project/Resources/VB.NET.xshd rename to Net461/Project/Resources/VB.NET.xshd diff --git a/Project/Resources/VBScript.xshd b/Net461/Project/Resources/VBScript.xshd similarity index 100% rename from Project/Resources/VBScript.xshd rename to Net461/Project/Resources/VBScript.xshd diff --git a/Project/Resources/VHDL.xshd b/Net461/Project/Resources/VHDL.xshd similarity index 100% rename from Project/Resources/VHDL.xshd rename to Net461/Project/Resources/VHDL.xshd diff --git a/Project/Resources/VS Solution.xshd b/Net461/Project/Resources/VS Solution.xshd similarity index 100% rename from Project/Resources/VS Solution.xshd rename to Net461/Project/Resources/VS Solution.xshd diff --git a/Project/Resources/Vala.xshd b/Net461/Project/Resources/Vala.xshd similarity index 100% rename from Project/Resources/Vala.xshd rename to Net461/Project/Resources/Vala.xshd diff --git a/Project/Resources/Verilog.xshd b/Net461/Project/Resources/Verilog.xshd similarity index 100% rename from Project/Resources/Verilog.xshd rename to Net461/Project/Resources/Verilog.xshd diff --git a/Project/Resources/Volt.xshd b/Net461/Project/Resources/Volt.xshd similarity index 100% rename from Project/Resources/Volt.xshd rename to Net461/Project/Resources/Volt.xshd diff --git a/Project/Resources/X10.xshd b/Net461/Project/Resources/X10.xshd similarity index 100% rename from Project/Resources/X10.xshd rename to Net461/Project/Resources/X10.xshd diff --git a/Project/Resources/XC.xshd b/Net461/Project/Resources/XC.xshd similarity index 100% rename from Project/Resources/XC.xshd rename to Net461/Project/Resources/XC.xshd diff --git a/Project/Resources/XML.xshd b/Net461/Project/Resources/XML.xshd similarity index 100% rename from Project/Resources/XML.xshd rename to Net461/Project/Resources/XML.xshd diff --git a/Project/Resources/Xtend.xshd b/Net461/Project/Resources/Xtend.xshd similarity index 100% rename from Project/Resources/Xtend.xshd rename to Net461/Project/Resources/Xtend.xshd diff --git a/Project/Src/Actions/BookmarkActions.cs b/Net461/Project/Src/Actions/BookmarkActions.cs similarity index 100% rename from Project/Src/Actions/BookmarkActions.cs rename to Net461/Project/Src/Actions/BookmarkActions.cs diff --git a/Project/Src/Actions/CaretActions.cs b/Net461/Project/Src/Actions/CaretActions.cs similarity index 100% rename from Project/Src/Actions/CaretActions.cs rename to Net461/Project/Src/Actions/CaretActions.cs diff --git a/Project/Src/Actions/ClipBoardActions.cs b/Net461/Project/Src/Actions/ClipBoardActions.cs similarity index 100% rename from Project/Src/Actions/ClipBoardActions.cs rename to Net461/Project/Src/Actions/ClipBoardActions.cs diff --git a/Project/Src/Actions/FoldActions.cs b/Net461/Project/Src/Actions/FoldActions.cs similarity index 100% rename from Project/Src/Actions/FoldActions.cs rename to Net461/Project/Src/Actions/FoldActions.cs diff --git a/Project/Src/Actions/FormatActions.cs b/Net461/Project/Src/Actions/FormatActions.cs similarity index 100% rename from Project/Src/Actions/FormatActions.cs rename to Net461/Project/Src/Actions/FormatActions.cs diff --git a/Project/Src/Actions/HomeEndActions.cs b/Net461/Project/Src/Actions/HomeEndActions.cs similarity index 100% rename from Project/Src/Actions/HomeEndActions.cs rename to Net461/Project/Src/Actions/HomeEndActions.cs diff --git a/Project/Src/Actions/IEditAction.cs b/Net461/Project/Src/Actions/IEditAction.cs similarity index 100% rename from Project/Src/Actions/IEditAction.cs rename to Net461/Project/Src/Actions/IEditAction.cs diff --git a/Project/Src/Actions/MiscActions.cs b/Net461/Project/Src/Actions/MiscActions.cs similarity index 100% rename from Project/Src/Actions/MiscActions.cs rename to Net461/Project/Src/Actions/MiscActions.cs diff --git a/Project/Src/Actions/SelectionActions.cs b/Net461/Project/Src/Actions/SelectionActions.cs similarity index 100% rename from Project/Src/Actions/SelectionActions.cs rename to Net461/Project/Src/Actions/SelectionActions.cs diff --git a/Project/Src/Document/AbstractSegment.cs b/Net461/Project/Src/Document/AbstractSegment.cs similarity index 100% rename from Project/Src/Document/AbstractSegment.cs rename to Net461/Project/Src/Document/AbstractSegment.cs diff --git a/Project/Src/Document/BookmarkManager/Bookmark.cs b/Net461/Project/Src/Document/BookmarkManager/Bookmark.cs similarity index 100% rename from Project/Src/Document/BookmarkManager/Bookmark.cs rename to Net461/Project/Src/Document/BookmarkManager/Bookmark.cs diff --git a/Project/Src/Document/BookmarkManager/BookmarkEventHandler.cs b/Net461/Project/Src/Document/BookmarkManager/BookmarkEventHandler.cs similarity index 100% rename from Project/Src/Document/BookmarkManager/BookmarkEventHandler.cs rename to Net461/Project/Src/Document/BookmarkManager/BookmarkEventHandler.cs diff --git a/Project/Src/Document/BookmarkManager/BookmarkManager.cs b/Net461/Project/Src/Document/BookmarkManager/BookmarkManager.cs similarity index 100% rename from Project/Src/Document/BookmarkManager/BookmarkManager.cs rename to Net461/Project/Src/Document/BookmarkManager/BookmarkManager.cs diff --git a/Project/Src/Document/BookmarkManager/BookmarkManagerMemento.cs b/Net461/Project/Src/Document/BookmarkManager/BookmarkManagerMemento.cs similarity index 100% rename from Project/Src/Document/BookmarkManager/BookmarkManagerMemento.cs rename to Net461/Project/Src/Document/BookmarkManager/BookmarkManagerMemento.cs diff --git a/Project/Src/Document/DefaultDocument.cs b/Net461/Project/Src/Document/DefaultDocument.cs similarity index 100% rename from Project/Src/Document/DefaultDocument.cs rename to Net461/Project/Src/Document/DefaultDocument.cs diff --git a/Project/Src/Document/DefaultTextEditorProperties.cs b/Net461/Project/Src/Document/DefaultTextEditorProperties.cs similarity index 100% rename from Project/Src/Document/DefaultTextEditorProperties.cs rename to Net461/Project/Src/Document/DefaultTextEditorProperties.cs diff --git a/Project/Src/Document/DocumentEventArgs.cs b/Net461/Project/Src/Document/DocumentEventArgs.cs similarity index 100% rename from Project/Src/Document/DocumentEventArgs.cs rename to Net461/Project/Src/Document/DocumentEventArgs.cs diff --git a/Project/Src/Document/DocumentFactory.cs b/Net461/Project/Src/Document/DocumentFactory.cs similarity index 100% rename from Project/Src/Document/DocumentFactory.cs rename to Net461/Project/Src/Document/DocumentFactory.cs diff --git a/Project/Src/Document/FoldingStrategy/FoldMarker.cs b/Net461/Project/Src/Document/FoldingStrategy/FoldMarker.cs similarity index 100% rename from Project/Src/Document/FoldingStrategy/FoldMarker.cs rename to Net461/Project/Src/Document/FoldingStrategy/FoldMarker.cs diff --git a/Project/Src/Document/FoldingStrategy/FoldingManager.cs b/Net461/Project/Src/Document/FoldingStrategy/FoldingManager.cs similarity index 100% rename from Project/Src/Document/FoldingStrategy/FoldingManager.cs rename to Net461/Project/Src/Document/FoldingStrategy/FoldingManager.cs diff --git a/Project/Src/Document/FoldingStrategy/IFoldingStrategy.cs b/Net461/Project/Src/Document/FoldingStrategy/IFoldingStrategy.cs similarity index 100% rename from Project/Src/Document/FoldingStrategy/IFoldingStrategy.cs rename to Net461/Project/Src/Document/FoldingStrategy/IFoldingStrategy.cs diff --git a/Project/Src/Document/FoldingStrategy/IndentFoldingStrategy.cs b/Net461/Project/Src/Document/FoldingStrategy/IndentFoldingStrategy.cs similarity index 100% rename from Project/Src/Document/FoldingStrategy/IndentFoldingStrategy.cs rename to Net461/Project/Src/Document/FoldingStrategy/IndentFoldingStrategy.cs diff --git a/Project/Src/Document/FormattingStrategy/DefaultFormattingStrategy.cs b/Net461/Project/Src/Document/FormattingStrategy/DefaultFormattingStrategy.cs similarity index 100% rename from Project/Src/Document/FormattingStrategy/DefaultFormattingStrategy.cs rename to Net461/Project/Src/Document/FormattingStrategy/DefaultFormattingStrategy.cs diff --git a/Project/Src/Document/FormattingStrategy/IFormattingStrategy.cs b/Net461/Project/Src/Document/FormattingStrategy/IFormattingStrategy.cs similarity index 100% rename from Project/Src/Document/FormattingStrategy/IFormattingStrategy.cs rename to Net461/Project/Src/Document/FormattingStrategy/IFormattingStrategy.cs diff --git a/Project/Src/Document/HighlightingStrategy/DefaultHighlightingStrategy.cs b/Net461/Project/Src/Document/HighlightingStrategy/DefaultHighlightingStrategy.cs similarity index 100% rename from Project/Src/Document/HighlightingStrategy/DefaultHighlightingStrategy.cs rename to Net461/Project/Src/Document/HighlightingStrategy/DefaultHighlightingStrategy.cs diff --git a/Project/Src/Document/HighlightingStrategy/FontContainer.cs b/Net461/Project/Src/Document/HighlightingStrategy/FontContainer.cs similarity index 100% rename from Project/Src/Document/HighlightingStrategy/FontContainer.cs rename to Net461/Project/Src/Document/HighlightingStrategy/FontContainer.cs diff --git a/Project/Src/Document/HighlightingStrategy/HighlightBackground.cs b/Net461/Project/Src/Document/HighlightingStrategy/HighlightBackground.cs similarity index 100% rename from Project/Src/Document/HighlightingStrategy/HighlightBackground.cs rename to Net461/Project/Src/Document/HighlightingStrategy/HighlightBackground.cs diff --git a/Project/Src/Document/HighlightingStrategy/HighlightColor.cs b/Net461/Project/Src/Document/HighlightingStrategy/HighlightColor.cs similarity index 100% rename from Project/Src/Document/HighlightingStrategy/HighlightColor.cs rename to Net461/Project/Src/Document/HighlightingStrategy/HighlightColor.cs diff --git a/Project/Src/Document/HighlightingStrategy/HighlightInfo.cs b/Net461/Project/Src/Document/HighlightingStrategy/HighlightInfo.cs similarity index 100% rename from Project/Src/Document/HighlightingStrategy/HighlightInfo.cs rename to Net461/Project/Src/Document/HighlightingStrategy/HighlightInfo.cs diff --git a/Project/Src/Document/HighlightingStrategy/HighlightRuleSet.cs b/Net461/Project/Src/Document/HighlightingStrategy/HighlightRuleSet.cs similarity index 100% rename from Project/Src/Document/HighlightingStrategy/HighlightRuleSet.cs rename to Net461/Project/Src/Document/HighlightingStrategy/HighlightRuleSet.cs diff --git a/Project/Src/Document/HighlightingStrategy/HighlightingColorNotFoundException.cs b/Net461/Project/Src/Document/HighlightingStrategy/HighlightingColorNotFoundException.cs similarity index 100% rename from Project/Src/Document/HighlightingStrategy/HighlightingColorNotFoundException.cs rename to Net461/Project/Src/Document/HighlightingStrategy/HighlightingColorNotFoundException.cs diff --git a/Project/Src/Document/HighlightingStrategy/HighlightingDefinitionInvalidException.cs b/Net461/Project/Src/Document/HighlightingStrategy/HighlightingDefinitionInvalidException.cs similarity index 100% rename from Project/Src/Document/HighlightingStrategy/HighlightingDefinitionInvalidException.cs rename to Net461/Project/Src/Document/HighlightingStrategy/HighlightingDefinitionInvalidException.cs diff --git a/Project/Src/Document/HighlightingStrategy/HighlightingDefinitionParser.cs b/Net461/Project/Src/Document/HighlightingStrategy/HighlightingDefinitionParser.cs similarity index 100% rename from Project/Src/Document/HighlightingStrategy/HighlightingDefinitionParser.cs rename to Net461/Project/Src/Document/HighlightingStrategy/HighlightingDefinitionParser.cs diff --git a/Project/Src/Document/HighlightingStrategy/HighlightingManager.cs b/Net461/Project/Src/Document/HighlightingStrategy/HighlightingManager.cs similarity index 100% rename from Project/Src/Document/HighlightingStrategy/HighlightingManager.cs rename to Net461/Project/Src/Document/HighlightingStrategy/HighlightingManager.cs diff --git a/Project/Src/Document/HighlightingStrategy/HighlightingStrategyFactory.cs b/Net461/Project/Src/Document/HighlightingStrategy/HighlightingStrategyFactory.cs similarity index 100% rename from Project/Src/Document/HighlightingStrategy/HighlightingStrategyFactory.cs rename to Net461/Project/Src/Document/HighlightingStrategy/HighlightingStrategyFactory.cs diff --git a/Project/Src/Document/HighlightingStrategy/IHighlightingStrategy.cs b/Net461/Project/Src/Document/HighlightingStrategy/IHighlightingStrategy.cs similarity index 100% rename from Project/Src/Document/HighlightingStrategy/IHighlightingStrategy.cs rename to Net461/Project/Src/Document/HighlightingStrategy/IHighlightingStrategy.cs diff --git a/Project/Src/Document/HighlightingStrategy/NextMarker.cs b/Net461/Project/Src/Document/HighlightingStrategy/NextMarker.cs similarity index 100% rename from Project/Src/Document/HighlightingStrategy/NextMarker.cs rename to Net461/Project/Src/Document/HighlightingStrategy/NextMarker.cs diff --git a/Project/Src/Document/HighlightingStrategy/PrevMarker.cs b/Net461/Project/Src/Document/HighlightingStrategy/PrevMarker.cs similarity index 100% rename from Project/Src/Document/HighlightingStrategy/PrevMarker.cs rename to Net461/Project/Src/Document/HighlightingStrategy/PrevMarker.cs diff --git a/Project/Src/Document/HighlightingStrategy/Span.cs b/Net461/Project/Src/Document/HighlightingStrategy/Span.cs similarity index 100% rename from Project/Src/Document/HighlightingStrategy/Span.cs rename to Net461/Project/Src/Document/HighlightingStrategy/Span.cs diff --git a/Project/Src/Document/HighlightingStrategy/SpanStack.cs b/Net461/Project/Src/Document/HighlightingStrategy/SpanStack.cs similarity index 100% rename from Project/Src/Document/HighlightingStrategy/SpanStack.cs rename to Net461/Project/Src/Document/HighlightingStrategy/SpanStack.cs diff --git a/Project/Src/Document/HighlightingStrategy/SyntaxModes/FileSyntaxModeProvider.cs b/Net461/Project/Src/Document/HighlightingStrategy/SyntaxModes/FileSyntaxModeProvider.cs similarity index 100% rename from Project/Src/Document/HighlightingStrategy/SyntaxModes/FileSyntaxModeProvider.cs rename to Net461/Project/Src/Document/HighlightingStrategy/SyntaxModes/FileSyntaxModeProvider.cs diff --git a/Project/Src/Document/HighlightingStrategy/SyntaxModes/ISyntaxModeFileProvider.cs b/Net461/Project/Src/Document/HighlightingStrategy/SyntaxModes/ISyntaxModeFileProvider.cs similarity index 100% rename from Project/Src/Document/HighlightingStrategy/SyntaxModes/ISyntaxModeFileProvider.cs rename to Net461/Project/Src/Document/HighlightingStrategy/SyntaxModes/ISyntaxModeFileProvider.cs diff --git a/Project/Src/Document/HighlightingStrategy/SyntaxModes/ResourceSyntaxModeProvider.cs b/Net461/Project/Src/Document/HighlightingStrategy/SyntaxModes/ResourceSyntaxModeProvider.cs similarity index 100% rename from Project/Src/Document/HighlightingStrategy/SyntaxModes/ResourceSyntaxModeProvider.cs rename to Net461/Project/Src/Document/HighlightingStrategy/SyntaxModes/ResourceSyntaxModeProvider.cs diff --git a/Project/Src/Document/HighlightingStrategy/SyntaxModes/SyntaxMode.cs b/Net461/Project/Src/Document/HighlightingStrategy/SyntaxModes/SyntaxMode.cs similarity index 100% rename from Project/Src/Document/HighlightingStrategy/SyntaxModes/SyntaxMode.cs rename to Net461/Project/Src/Document/HighlightingStrategy/SyntaxModes/SyntaxMode.cs diff --git a/Project/Src/Document/HighlightingStrategy/TextWord.cs b/Net461/Project/Src/Document/HighlightingStrategy/TextWord.cs similarity index 100% rename from Project/Src/Document/HighlightingStrategy/TextWord.cs rename to Net461/Project/Src/Document/HighlightingStrategy/TextWord.cs diff --git a/Project/Src/Document/IDocument.cs b/Net461/Project/Src/Document/IDocument.cs similarity index 100% rename from Project/Src/Document/IDocument.cs rename to Net461/Project/Src/Document/IDocument.cs diff --git a/Project/Src/Document/ISegment.cs b/Net461/Project/Src/Document/ISegment.cs similarity index 100% rename from Project/Src/Document/ISegment.cs rename to Net461/Project/Src/Document/ISegment.cs diff --git a/Project/Src/Document/ITextEditorProperties.cs b/Net461/Project/Src/Document/ITextEditorProperties.cs similarity index 100% rename from Project/Src/Document/ITextEditorProperties.cs rename to Net461/Project/Src/Document/ITextEditorProperties.cs diff --git a/Project/Src/Document/LineManager/DeferredEventList.cs b/Net461/Project/Src/Document/LineManager/DeferredEventList.cs similarity index 100% rename from Project/Src/Document/LineManager/DeferredEventList.cs rename to Net461/Project/Src/Document/LineManager/DeferredEventList.cs diff --git a/Project/Src/Document/LineManager/LineManager.cs b/Net461/Project/Src/Document/LineManager/LineManager.cs similarity index 100% rename from Project/Src/Document/LineManager/LineManager.cs rename to Net461/Project/Src/Document/LineManager/LineManager.cs diff --git a/Project/Src/Document/LineManager/LineManagerEventArgs.cs b/Net461/Project/Src/Document/LineManager/LineManagerEventArgs.cs similarity index 100% rename from Project/Src/Document/LineManager/LineManagerEventArgs.cs rename to Net461/Project/Src/Document/LineManager/LineManagerEventArgs.cs diff --git a/Project/Src/Document/LineManager/LineSegment.cs b/Net461/Project/Src/Document/LineManager/LineSegment.cs similarity index 100% rename from Project/Src/Document/LineManager/LineSegment.cs rename to Net461/Project/Src/Document/LineManager/LineSegment.cs diff --git a/Project/Src/Document/LineManager/LineSegmentTree.cs b/Net461/Project/Src/Document/LineManager/LineSegmentTree.cs similarity index 100% rename from Project/Src/Document/LineManager/LineSegmentTree.cs rename to Net461/Project/Src/Document/LineManager/LineSegmentTree.cs diff --git a/Project/Src/Document/MarkerStrategy/MarkerStrategy.cs b/Net461/Project/Src/Document/MarkerStrategy/MarkerStrategy.cs similarity index 100% rename from Project/Src/Document/MarkerStrategy/MarkerStrategy.cs rename to Net461/Project/Src/Document/MarkerStrategy/MarkerStrategy.cs diff --git a/Project/Src/Document/MarkerStrategy/TextMarker.cs b/Net461/Project/Src/Document/MarkerStrategy/TextMarker.cs similarity index 100% rename from Project/Src/Document/MarkerStrategy/TextMarker.cs rename to Net461/Project/Src/Document/MarkerStrategy/TextMarker.cs diff --git a/Project/Src/Document/Selection/ColumnRange.cs b/Net461/Project/Src/Document/Selection/ColumnRange.cs similarity index 100% rename from Project/Src/Document/Selection/ColumnRange.cs rename to Net461/Project/Src/Document/Selection/ColumnRange.cs diff --git a/Project/Src/Document/Selection/DefaultSelection.cs b/Net461/Project/Src/Document/Selection/DefaultSelection.cs similarity index 100% rename from Project/Src/Document/Selection/DefaultSelection.cs rename to Net461/Project/Src/Document/Selection/DefaultSelection.cs diff --git a/Project/Src/Document/Selection/ISelection.cs b/Net461/Project/Src/Document/Selection/ISelection.cs similarity index 100% rename from Project/Src/Document/Selection/ISelection.cs rename to Net461/Project/Src/Document/Selection/ISelection.cs diff --git a/Project/Src/Document/Selection/SelectionManager.cs b/Net461/Project/Src/Document/Selection/SelectionManager.cs similarity index 100% rename from Project/Src/Document/Selection/SelectionManager.cs rename to Net461/Project/Src/Document/Selection/SelectionManager.cs diff --git a/Project/Src/Document/TextAnchor.cs b/Net461/Project/Src/Document/TextAnchor.cs similarity index 100% rename from Project/Src/Document/TextAnchor.cs rename to Net461/Project/Src/Document/TextAnchor.cs diff --git a/Project/Src/Document/TextBufferStrategy/GapTextBufferStrategy.cs b/Net461/Project/Src/Document/TextBufferStrategy/GapTextBufferStrategy.cs similarity index 100% rename from Project/Src/Document/TextBufferStrategy/GapTextBufferStrategy.cs rename to Net461/Project/Src/Document/TextBufferStrategy/GapTextBufferStrategy.cs diff --git a/Project/Src/Document/TextBufferStrategy/ITextBufferStrategy.cs b/Net461/Project/Src/Document/TextBufferStrategy/ITextBufferStrategy.cs similarity index 100% rename from Project/Src/Document/TextBufferStrategy/ITextBufferStrategy.cs rename to Net461/Project/Src/Document/TextBufferStrategy/ITextBufferStrategy.cs diff --git a/Project/Src/Document/TextBufferStrategy/StringTextBufferStrategy.cs b/Net461/Project/Src/Document/TextBufferStrategy/StringTextBufferStrategy.cs similarity index 100% rename from Project/Src/Document/TextBufferStrategy/StringTextBufferStrategy.cs rename to Net461/Project/Src/Document/TextBufferStrategy/StringTextBufferStrategy.cs diff --git a/Project/Src/Document/TextLocation.cs b/Net461/Project/Src/Document/TextLocation.cs similarity index 100% rename from Project/Src/Document/TextLocation.cs rename to Net461/Project/Src/Document/TextLocation.cs diff --git a/Project/Src/Document/TextUtilities.cs b/Net461/Project/Src/Document/TextUtilities.cs similarity index 100% rename from Project/Src/Document/TextUtilities.cs rename to Net461/Project/Src/Document/TextUtilities.cs diff --git a/Project/Src/Gui/AbstractMargin.cs b/Net461/Project/Src/Gui/AbstractMargin.cs similarity index 100% rename from Project/Src/Gui/AbstractMargin.cs rename to Net461/Project/Src/Gui/AbstractMargin.cs diff --git a/Project/Src/Gui/BracketHighlighter.cs b/Net461/Project/Src/Gui/BracketHighlighter.cs similarity index 100% rename from Project/Src/Gui/BracketHighlighter.cs rename to Net461/Project/Src/Gui/BracketHighlighter.cs diff --git a/Project/Src/Gui/BrushRegistry.cs b/Net461/Project/Src/Gui/BrushRegistry.cs similarity index 100% rename from Project/Src/Gui/BrushRegistry.cs rename to Net461/Project/Src/Gui/BrushRegistry.cs diff --git a/Project/Src/Gui/Caret.cs b/Net461/Project/Src/Gui/Caret.cs similarity index 100% rename from Project/Src/Gui/Caret.cs rename to Net461/Project/Src/Gui/Caret.cs diff --git a/Project/Src/Gui/CompletionWindow/AbstractCompletionWindow.cs b/Net461/Project/Src/Gui/CompletionWindow/AbstractCompletionWindow.cs similarity index 100% rename from Project/Src/Gui/CompletionWindow/AbstractCompletionWindow.cs rename to Net461/Project/Src/Gui/CompletionWindow/AbstractCompletionWindow.cs diff --git a/Project/Src/Gui/CompletionWindow/CodeCompletionListView.cs b/Net461/Project/Src/Gui/CompletionWindow/CodeCompletionListView.cs similarity index 100% rename from Project/Src/Gui/CompletionWindow/CodeCompletionListView.cs rename to Net461/Project/Src/Gui/CompletionWindow/CodeCompletionListView.cs diff --git a/Project/Src/Gui/CompletionWindow/CodeCompletionWindow.cs b/Net461/Project/Src/Gui/CompletionWindow/CodeCompletionWindow.cs similarity index 100% rename from Project/Src/Gui/CompletionWindow/CodeCompletionWindow.cs rename to Net461/Project/Src/Gui/CompletionWindow/CodeCompletionWindow.cs diff --git a/Project/Src/Gui/CompletionWindow/DeclarationViewWindow.cs b/Net461/Project/Src/Gui/CompletionWindow/DeclarationViewWindow.cs similarity index 100% rename from Project/Src/Gui/CompletionWindow/DeclarationViewWindow.cs rename to Net461/Project/Src/Gui/CompletionWindow/DeclarationViewWindow.cs diff --git a/Project/Src/Gui/CompletionWindow/ICompletionData.cs b/Net461/Project/Src/Gui/CompletionWindow/ICompletionData.cs similarity index 100% rename from Project/Src/Gui/CompletionWindow/ICompletionData.cs rename to Net461/Project/Src/Gui/CompletionWindow/ICompletionData.cs diff --git a/Project/Src/Gui/CompletionWindow/ICompletionDataProvider.cs b/Net461/Project/Src/Gui/CompletionWindow/ICompletionDataProvider.cs similarity index 100% rename from Project/Src/Gui/CompletionWindow/ICompletionDataProvider.cs rename to Net461/Project/Src/Gui/CompletionWindow/ICompletionDataProvider.cs diff --git a/Project/Src/Gui/DrawableLine.cs b/Net461/Project/Src/Gui/DrawableLine.cs similarity index 100% rename from Project/Src/Gui/DrawableLine.cs rename to Net461/Project/Src/Gui/DrawableLine.cs diff --git a/Project/Src/Gui/FoldMargin.cs b/Net461/Project/Src/Gui/FoldMargin.cs similarity index 100% rename from Project/Src/Gui/FoldMargin.cs rename to Net461/Project/Src/Gui/FoldMargin.cs diff --git a/Project/Src/Gui/GutterMargin.cs b/Net461/Project/Src/Gui/GutterMargin.cs similarity index 100% rename from Project/Src/Gui/GutterMargin.cs rename to Net461/Project/Src/Gui/GutterMargin.cs diff --git a/Project/Src/Gui/HRuler.cs b/Net461/Project/Src/Gui/HRuler.cs similarity index 100% rename from Project/Src/Gui/HRuler.cs rename to Net461/Project/Src/Gui/HRuler.cs diff --git a/Project/Src/Gui/IconBarMargin.cs b/Net461/Project/Src/Gui/IconBarMargin.cs similarity index 100% rename from Project/Src/Gui/IconBarMargin.cs rename to Net461/Project/Src/Gui/IconBarMargin.cs diff --git a/Project/Src/Gui/Ime.cs b/Net461/Project/Src/Gui/Ime.cs similarity index 100% rename from Project/Src/Gui/Ime.cs rename to Net461/Project/Src/Gui/Ime.cs diff --git a/Project/Src/Gui/InsightWindow/IInsightDataProvider.cs b/Net461/Project/Src/Gui/InsightWindow/IInsightDataProvider.cs similarity index 100% rename from Project/Src/Gui/InsightWindow/IInsightDataProvider.cs rename to Net461/Project/Src/Gui/InsightWindow/IInsightDataProvider.cs diff --git a/Project/Src/Gui/InsightWindow/InsightWindow.cs b/Net461/Project/Src/Gui/InsightWindow/InsightWindow.cs similarity index 100% rename from Project/Src/Gui/InsightWindow/InsightWindow.cs rename to Net461/Project/Src/Gui/InsightWindow/InsightWindow.cs diff --git a/Project/Src/Gui/TextArea.cs b/Net461/Project/Src/Gui/TextArea.cs similarity index 100% rename from Project/Src/Gui/TextArea.cs rename to Net461/Project/Src/Gui/TextArea.cs diff --git a/Project/Src/Gui/TextAreaClipboardHandler.cs b/Net461/Project/Src/Gui/TextAreaClipboardHandler.cs similarity index 100% rename from Project/Src/Gui/TextAreaClipboardHandler.cs rename to Net461/Project/Src/Gui/TextAreaClipboardHandler.cs diff --git a/Project/Src/Gui/TextAreaControl.cs b/Net461/Project/Src/Gui/TextAreaControl.cs similarity index 100% rename from Project/Src/Gui/TextAreaControl.cs rename to Net461/Project/Src/Gui/TextAreaControl.cs diff --git a/Project/Src/Gui/TextAreaDragDropHandler.cs b/Net461/Project/Src/Gui/TextAreaDragDropHandler.cs similarity index 100% rename from Project/Src/Gui/TextAreaDragDropHandler.cs rename to Net461/Project/Src/Gui/TextAreaDragDropHandler.cs diff --git a/Project/Src/Gui/TextAreaMouseHandler.cs b/Net461/Project/Src/Gui/TextAreaMouseHandler.cs similarity index 100% rename from Project/Src/Gui/TextAreaMouseHandler.cs rename to Net461/Project/Src/Gui/TextAreaMouseHandler.cs diff --git a/Project/Src/Gui/TextAreaUpdate.cs b/Net461/Project/Src/Gui/TextAreaUpdate.cs similarity index 100% rename from Project/Src/Gui/TextAreaUpdate.cs rename to Net461/Project/Src/Gui/TextAreaUpdate.cs diff --git a/Project/Src/Gui/TextEditorControl.cs b/Net461/Project/Src/Gui/TextEditorControl.cs similarity index 100% rename from Project/Src/Gui/TextEditorControl.cs rename to Net461/Project/Src/Gui/TextEditorControl.cs diff --git a/Project/Src/Gui/TextEditorControlBase.cs b/Net461/Project/Src/Gui/TextEditorControlBase.cs similarity index 100% rename from Project/Src/Gui/TextEditorControlBase.cs rename to Net461/Project/Src/Gui/TextEditorControlBase.cs diff --git a/Project/Src/Gui/TextView.cs b/Net461/Project/Src/Gui/TextView.cs similarity index 100% rename from Project/Src/Gui/TextView.cs rename to Net461/Project/Src/Gui/TextView.cs diff --git a/Project/Src/Gui/ToolTipRequestEventArgs.cs b/Net461/Project/Src/Gui/ToolTipRequestEventArgs.cs similarity index 100% rename from Project/Src/Gui/ToolTipRequestEventArgs.cs rename to Net461/Project/Src/Gui/ToolTipRequestEventArgs.cs diff --git a/Project/Src/Undo/IUndoableOperation.cs b/Net461/Project/Src/Undo/IUndoableOperation.cs similarity index 100% rename from Project/Src/Undo/IUndoableOperation.cs rename to Net461/Project/Src/Undo/IUndoableOperation.cs diff --git a/Project/Src/Undo/UndoQueue.cs b/Net461/Project/Src/Undo/UndoQueue.cs similarity index 100% rename from Project/Src/Undo/UndoQueue.cs rename to Net461/Project/Src/Undo/UndoQueue.cs diff --git a/Project/Src/Undo/UndoStack.cs b/Net461/Project/Src/Undo/UndoStack.cs similarity index 100% rename from Project/Src/Undo/UndoStack.cs rename to Net461/Project/Src/Undo/UndoStack.cs diff --git a/Project/Src/Undo/UndoableDelete.cs b/Net461/Project/Src/Undo/UndoableDelete.cs similarity index 100% rename from Project/Src/Undo/UndoableDelete.cs rename to Net461/Project/Src/Undo/UndoableDelete.cs diff --git a/Project/Src/Undo/UndoableInsert.cs b/Net461/Project/Src/Undo/UndoableInsert.cs similarity index 100% rename from Project/Src/Undo/UndoableInsert.cs rename to Net461/Project/Src/Undo/UndoableInsert.cs diff --git a/Project/Src/Undo/UndoableReplace.cs b/Net461/Project/Src/Undo/UndoableReplace.cs similarity index 100% rename from Project/Src/Undo/UndoableReplace.cs rename to Net461/Project/Src/Undo/UndoableReplace.cs diff --git a/Project/Src/Util/AugmentableRedBlackTree.cs b/Net461/Project/Src/Util/AugmentableRedBlackTree.cs similarity index 100% rename from Project/Src/Util/AugmentableRedBlackTree.cs rename to Net461/Project/Src/Util/AugmentableRedBlackTree.cs diff --git a/Project/Src/Util/CheckedList.cs b/Net461/Project/Src/Util/CheckedList.cs similarity index 100% rename from Project/Src/Util/CheckedList.cs rename to Net461/Project/Src/Util/CheckedList.cs diff --git a/Project/Src/Util/FileReader.cs b/Net461/Project/Src/Util/FileReader.cs similarity index 100% rename from Project/Src/Util/FileReader.cs rename to Net461/Project/Src/Util/FileReader.cs diff --git a/Project/Src/Util/LoggingService.cs b/Net461/Project/Src/Util/LoggingService.cs similarity index 100% rename from Project/Src/Util/LoggingService.cs rename to Net461/Project/Src/Util/LoggingService.cs diff --git a/Project/Src/Util/LookupTable.cs b/Net461/Project/Src/Util/LookupTable.cs similarity index 100% rename from Project/Src/Util/LookupTable.cs rename to Net461/Project/Src/Util/LookupTable.cs diff --git a/Project/Src/Util/MouseWheelHandler.cs b/Net461/Project/Src/Util/MouseWheelHandler.cs similarity index 100% rename from Project/Src/Util/MouseWheelHandler.cs rename to Net461/Project/Src/Util/MouseWheelHandler.cs diff --git a/Project/Src/Util/RedBlackTreeIterator.cs b/Net461/Project/Src/Util/RedBlackTreeIterator.cs similarity index 100% rename from Project/Src/Util/RedBlackTreeIterator.cs rename to Net461/Project/Src/Util/RedBlackTreeIterator.cs diff --git a/Project/Src/Util/RtfWriter.cs b/Net461/Project/Src/Util/RtfWriter.cs similarity index 100% rename from Project/Src/Util/RtfWriter.cs rename to Net461/Project/Src/Util/RtfWriter.cs diff --git a/Project/Src/Util/TextUtility.cs b/Net461/Project/Src/Util/TextUtility.cs similarity index 100% rename from Project/Src/Util/TextUtility.cs rename to Net461/Project/Src/Util/TextUtility.cs diff --git a/Project/Src/Util/TipPainter.cs b/Net461/Project/Src/Util/TipPainter.cs similarity index 100% rename from Project/Src/Util/TipPainter.cs rename to Net461/Project/Src/Util/TipPainter.cs diff --git a/Project/Src/Util/TipPainterTools.cs b/Net461/Project/Src/Util/TipPainterTools.cs similarity index 100% rename from Project/Src/Util/TipPainterTools.cs rename to Net461/Project/Src/Util/TipPainterTools.cs diff --git a/Project/Src/Util/TipSection.cs b/Net461/Project/Src/Util/TipSection.cs similarity index 100% rename from Project/Src/Util/TipSection.cs rename to Net461/Project/Src/Util/TipSection.cs diff --git a/Project/Src/Util/TipSpacer.cs b/Net461/Project/Src/Util/TipSpacer.cs similarity index 100% rename from Project/Src/Util/TipSpacer.cs rename to Net461/Project/Src/Util/TipSpacer.cs diff --git a/Project/Src/Util/TipSplitter.cs b/Net461/Project/Src/Util/TipSplitter.cs similarity index 100% rename from Project/Src/Util/TipSplitter.cs rename to Net461/Project/Src/Util/TipSplitter.cs diff --git a/Project/Src/Util/TipText.cs b/Net461/Project/Src/Util/TipText.cs similarity index 100% rename from Project/Src/Util/TipText.cs rename to Net461/Project/Src/Util/TipText.cs diff --git a/Project/Src/Util/WeakCollection.cs b/Net461/Project/Src/Util/WeakCollection.cs similarity index 100% rename from Project/Src/Util/WeakCollection.cs rename to Net461/Project/Src/Util/WeakCollection.cs diff --git a/Project/Src/Util/Win32Util.cs b/Net461/Project/Src/Util/Win32Util.cs similarity index 100% rename from Project/Src/Util/Win32Util.cs rename to Net461/Project/Src/Util/Win32Util.cs diff --git a/Net461/README.md b/Net461/README.md new file mode 100644 index 0000000..d05e580 --- /dev/null +++ b/Net461/README.md @@ -0,0 +1,2 @@ +# ICSharpCode.TextEditor +ICSharpCode.TextEditor for WinForms from SharpDevelop 3.2 diff --git a/Sample.md b/Net461/Sample.md similarity index 100% rename from Sample.md rename to Net461/Sample.md diff --git a/Test/AssemblyInfo.cs b/Net461/Test/AssemblyInfo.cs similarity index 100% rename from Test/AssemblyInfo.cs rename to Net461/Test/AssemblyInfo.cs diff --git a/Test/BlockCommentTests.cs b/Net461/Test/BlockCommentTests.cs similarity index 100% rename from Test/BlockCommentTests.cs rename to Net461/Test/BlockCommentTests.cs diff --git a/Test/DocumentTests.cs b/Net461/Test/DocumentTests.cs similarity index 100% rename from Test/DocumentTests.cs rename to Net461/Test/DocumentTests.cs diff --git a/Test/FoldingManagerTests.cs b/Net461/Test/FoldingManagerTests.cs similarity index 100% rename from Test/FoldingManagerTests.cs rename to Net461/Test/FoldingManagerTests.cs diff --git a/Test/HighlightingManagerTests.cs b/Net461/Test/HighlightingManagerTests.cs similarity index 100% rename from Test/HighlightingManagerTests.cs rename to Net461/Test/HighlightingManagerTests.cs diff --git a/Test/ICSharpCode.TextEditor.Tests.csproj b/Net461/Test/ICSharpCode.TextEditor.Tests.csproj similarity index 100% rename from Test/ICSharpCode.TextEditor.Tests.csproj rename to Net461/Test/ICSharpCode.TextEditor.Tests.csproj diff --git a/Test/TextMarkerTests.cs b/Net461/Test/TextMarkerTests.cs similarity index 100% rename from Test/TextMarkerTests.cs rename to Net461/Test/TextMarkerTests.cs diff --git a/Test/packages.config b/Net461/Test/packages.config similarity index 100% rename from Test/packages.config rename to Net461/Test/packages.config diff --git a/packages/NUnit.2.6.1/NUnit.2.6.1.nupkg b/Net461/packages/NUnit.2.6.1/NUnit.2.6.1.nupkg similarity index 100% rename from packages/NUnit.2.6.1/NUnit.2.6.1.nupkg rename to Net461/packages/NUnit.2.6.1/NUnit.2.6.1.nupkg diff --git a/packages/NUnit.2.6.1/lib/nunit.framework.dll b/Net461/packages/NUnit.2.6.1/lib/nunit.framework.dll similarity index 100% rename from packages/NUnit.2.6.1/lib/nunit.framework.dll rename to Net461/packages/NUnit.2.6.1/lib/nunit.framework.dll diff --git a/Test/bin/UnitTests/nunit.framework.xml b/Net461/packages/NUnit.2.6.1/lib/nunit.framework.xml similarity index 100% rename from Test/bin/UnitTests/nunit.framework.xml rename to Net461/packages/NUnit.2.6.1/lib/nunit.framework.xml diff --git a/packages/NUnit.2.6.1/license.txt b/Net461/packages/NUnit.2.6.1/license.txt similarity index 100% rename from packages/NUnit.2.6.1/license.txt rename to Net461/packages/NUnit.2.6.1/license.txt diff --git a/packages/repositories.config b/Net461/packages/repositories.config similarity index 100% rename from packages/repositories.config rename to Net461/packages/repositories.config diff --git a/NetCore31/.vs/ICSharpCode.TextEditor.Core/DesignTimeBuild/.dtbcache.v2 b/NetCore31/.vs/ICSharpCode.TextEditor.Core/DesignTimeBuild/.dtbcache.v2 new file mode 100644 index 0000000..726fa58 Binary files /dev/null and b/NetCore31/.vs/ICSharpCode.TextEditor.Core/DesignTimeBuild/.dtbcache.v2 differ diff --git a/NetCore31/ICSharpCode.TextEditor.Core.sln b/NetCore31/ICSharpCode.TextEditor.Core.sln new file mode 100644 index 0000000..723f38a --- /dev/null +++ b/NetCore31/ICSharpCode.TextEditor.Core.sln @@ -0,0 +1,54 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{40D928F6-2A68-4BB2-A4BB-434B3E86BF09}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.TextEditor.Sample", "src\ICSharpCode.TextEditor.Sample\ICSharpCode.TextEditor.Sample.csproj", "{54DE57FF-E2FC-4A8B-9ED7-C72255709A5D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.TextEditor", "src\ICSharpCode.TextEditor\ICSharpCode.TextEditor.csproj", "{E592BED3-B404-4905-A6EE-F3E1BA20BF82}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {54DE57FF-E2FC-4A8B-9ED7-C72255709A5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {54DE57FF-E2FC-4A8B-9ED7-C72255709A5D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {54DE57FF-E2FC-4A8B-9ED7-C72255709A5D}.Debug|x64.ActiveCfg = Debug|Any CPU + {54DE57FF-E2FC-4A8B-9ED7-C72255709A5D}.Debug|x64.Build.0 = Debug|Any CPU + {54DE57FF-E2FC-4A8B-9ED7-C72255709A5D}.Debug|x86.ActiveCfg = Debug|Any CPU + {54DE57FF-E2FC-4A8B-9ED7-C72255709A5D}.Debug|x86.Build.0 = Debug|Any CPU + {54DE57FF-E2FC-4A8B-9ED7-C72255709A5D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {54DE57FF-E2FC-4A8B-9ED7-C72255709A5D}.Release|Any CPU.Build.0 = Release|Any CPU + {54DE57FF-E2FC-4A8B-9ED7-C72255709A5D}.Release|x64.ActiveCfg = Release|Any CPU + {54DE57FF-E2FC-4A8B-9ED7-C72255709A5D}.Release|x64.Build.0 = Release|Any CPU + {54DE57FF-E2FC-4A8B-9ED7-C72255709A5D}.Release|x86.ActiveCfg = Release|Any CPU + {54DE57FF-E2FC-4A8B-9ED7-C72255709A5D}.Release|x86.Build.0 = Release|Any CPU + {E592BED3-B404-4905-A6EE-F3E1BA20BF82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E592BED3-B404-4905-A6EE-F3E1BA20BF82}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E592BED3-B404-4905-A6EE-F3E1BA20BF82}.Debug|x64.ActiveCfg = Debug|Any CPU + {E592BED3-B404-4905-A6EE-F3E1BA20BF82}.Debug|x64.Build.0 = Debug|Any CPU + {E592BED3-B404-4905-A6EE-F3E1BA20BF82}.Debug|x86.ActiveCfg = Debug|Any CPU + {E592BED3-B404-4905-A6EE-F3E1BA20BF82}.Debug|x86.Build.0 = Debug|Any CPU + {E592BED3-B404-4905-A6EE-F3E1BA20BF82}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E592BED3-B404-4905-A6EE-F3E1BA20BF82}.Release|Any CPU.Build.0 = Release|Any CPU + {E592BED3-B404-4905-A6EE-F3E1BA20BF82}.Release|x64.ActiveCfg = Release|Any CPU + {E592BED3-B404-4905-A6EE-F3E1BA20BF82}.Release|x64.Build.0 = Release|Any CPU + {E592BED3-B404-4905-A6EE-F3E1BA20BF82}.Release|x86.ActiveCfg = Release|Any CPU + {E592BED3-B404-4905-A6EE-F3E1BA20BF82}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {54DE57FF-E2FC-4A8B-9ED7-C72255709A5D} = {40D928F6-2A68-4BB2-A4BB-434B3E86BF09} + {E592BED3-B404-4905-A6EE-F3E1BA20BF82} = {40D928F6-2A68-4BB2-A4BB-434B3E86BF09} + EndGlobalSection + diff --git a/NetCore31/MigrationReport/MigrationReport.html b/NetCore31/MigrationReport/MigrationReport.html new file mode 100644 index 0000000..96e3a07 --- /dev/null +++ b/NetCore31/MigrationReport/MigrationReport.html @@ -0,0 +1,55 @@ + + + + +Migration Report + + + + +

Migration Report: ICSharpCode.TextEditor.Core

+ + diff --git a/NetCore31/src/ICSharpCode.TextEditor.Sample/ICSharpCode.TextEditor.Sample.csproj b/NetCore31/src/ICSharpCode.TextEditor.Sample/ICSharpCode.TextEditor.Sample.csproj new file mode 100644 index 0000000..73ead7c --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor.Sample/ICSharpCode.TextEditor.Sample.csproj @@ -0,0 +1,11 @@ + + + netcoreapp3.1 + WinExe + false + true + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor.Sample/Program.cs b/NetCore31/src/ICSharpCode.TextEditor.Sample/Program.cs new file mode 100644 index 0000000..b1308ed --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor.Sample/Program.cs @@ -0,0 +1,16 @@ +using System; +using System.Windows.Forms; + +namespace ICSharpCode.TextEditor.Sample +{ + internal static class Program + { + [STAThread] + private static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(defaultValue: false); + Application.Run(new SampleForm()); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor.Sample/Properties/AssemblyInfo.cs b/NetCore31/src/ICSharpCode.TextEditor.Sample/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..4369489 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor.Sample/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ICSharpCode.TextEditor.Sample")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ICSharpCode.TextEditor.Sample")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(visibility: false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("7a53a84d-78b1-40b8-8d26-4724d829a756")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor.Sample/SampleForm.Designer.cs b/NetCore31/src/ICSharpCode.TextEditor.Sample/SampleForm.Designer.cs new file mode 100644 index 0000000..da3c98a --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor.Sample/SampleForm.Designer.cs @@ -0,0 +1,53 @@ +using System.ComponentModel; + +namespace ICSharpCode.TextEditor.Sample +{ + partial class SampleForm + { + private IContainer components = null; + + protected override void Dispose(bool disposing) + { + if (disposing) + { + components?.Dispose(); + } + + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + private void InitializeComponent() + { + this.textEditor = new ICSharpCode.TextEditor.TextEditorControl(); + this.SuspendLayout(); + // + // textEditor + // + this.textEditor.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textEditor.IsReadOnly = false; + this.textEditor.Location = new System.Drawing.Point(0, 0); + this.textEditor.Name = "textEditor"; + this.textEditor.Size = new System.Drawing.Size(989, 628); + this.textEditor.TabIndex = 0; + // + // Form1 + // + this.AutoScaleDimensions = new System.Drawing.SizeF(12F, 25F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(989, 628); + this.Controls.Add(this.textEditor); + this.Name = "SampleForm"; + this.Text = "ICSharpCode.TextEditor Sample"; + this.ResumeLayout(false); + + } + + #endregion + + private TextEditorControl textEditor; + } +} diff --git a/NetCore31/src/ICSharpCode.TextEditor.Sample/SampleForm.cs b/NetCore31/src/ICSharpCode.TextEditor.Sample/SampleForm.cs new file mode 100644 index 0000000..c7d6b66 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor.Sample/SampleForm.cs @@ -0,0 +1,14 @@ +using System.Windows.Forms; + +namespace ICSharpCode.TextEditor.Sample +{ + public sealed partial class SampleForm : Form + { + public SampleForm() + { + InitializeComponent(); + + textEditor.Document.TextEditorProperties.EnableFolding = false; + } + } +} diff --git a/NetCore31/src/ICSharpCode.TextEditor.Sample/SampleForm.resx b/NetCore31/src/ICSharpCode.TextEditor.Sample/SampleForm.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor.Sample/SampleForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/ICSharpCode.TextEditor.csproj b/NetCore31/src/ICSharpCode.TextEditor/ICSharpCode.TextEditor.csproj new file mode 100644 index 0000000..ead75b7 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/ICSharpCode.TextEditor.csproj @@ -0,0 +1,10 @@ + + + netcoreapp3.1 + false + true + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Properties/AssemblyInfo.cs b/NetCore31/src/ICSharpCode.TextEditor/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..30cb722 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Properties/AssemblyInfo.cs @@ -0,0 +1,45 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyCompany("ic#code")] +[assembly: AssemblyCopyright("2000-2010 AlphaSierraPapa")] +[assembly: NeutralResourcesLanguage("en-US")] + +[assembly: StringFreezing] + +[assembly: AssemblyTitle("ICSharpCode.TextEditor v4.0.2.6466")] +[assembly: AssemblyDescription("This is a GitExtensions fork of SharpDevelop .NET text editor control")] +[assembly: AssemblyConfiguration("")] + +[assembly: AssemblyProduct("GitExtensions")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("3.1.0")] +[assembly: AssemblyFileVersion("3.1.0")] +[assembly: AssemblyInformationalVersion("3.1.0")] + +// Disable CLS compliance. See https://github.com/gitextensions/gitextensions/issues/4710 +[assembly: CLSCompliant(isCompliant: false)] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/ANTLR.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/ANTLR.xshd new file mode 100644 index 0000000..8204bd8 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/ANTLR.xshd @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &~!@%^*()-+=|\#/{}[]:;"'<>,.? + + + // + + + + /* + */ + + + + " + " + + + + ' + ' + + + + [ + ] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/ASPX.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/ASPX.xshd new file mode 100644 index 0000000..c691f4b --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/ASPX.xshd @@ -0,0 +1,17 @@ + + + + + + <% + %> + + + + + + // + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/ActionScript.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/ActionScript.xshd new file mode 100644 index 0000000..176008c --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/ActionScript.xshd @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~!%^*()-+=|\#/{}[]:;"' , .? + + + // + + + + /* + */ + + + + " + " + + + + ' + ' + + + + # + + + # + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ~!%^*()-+=|\#/{}[]:;"'<> , .? + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Ada.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Ada.xshd new file mode 100644 index 0000000..390ee38 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Ada.xshd @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ~!@%^*()-+=|\/{}[]:;"'<>,.? + + + -- + + + + " + " + + + + ' + ' + + + ' + procedure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Assembly.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Assembly.xshd new file mode 100644 index 0000000..004dd4b --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Assembly.xshd @@ -0,0 +1,1449 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~!^*()+=|\/{}[]:;"', + + + ; + + + + %comment + %endcomment + + + + " + " + + + + ' + ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/AutoHotkey.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/AutoHotkey.xshd new file mode 100644 index 0000000..e4bc278 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/AutoHotkey.xshd @@ -0,0 +1,497 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{}@,.`=;+-*/~ &|^>< + + + ; + + + + /* + */ + + + + " + " + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Batch.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Batch.xshd new file mode 100644 index 0000000..d4effbd --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Batch.xshd @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @+*\/=|'<>?; + + + REM + + + + :: + + + + : + + + + echo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Boo.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Boo.xshd new file mode 100644 index 0000000..4f281bd --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Boo.xshd @@ -0,0 +1,256 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~!@$%^*()-+=|\#/{}[]:;"' , .? + + + """ + """ + + + + # + + + + // + + + + /* + */ + + + + " + " + + + + """ + """ + + + + ' + ' + + + + @@/ + / + + + + / + / + + + class + def + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~!@%^*()-+=|\#/{}[]:;"' , .? + + + /* + */ + + + + + &<>~!@%^*()-+=|\#/{}[]:;"' , .? + + + /* + */ + + + + + &<>~!@%^*()-+=|\#/{}[]:;"' , .? + + + ${ + } + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/C#.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/C#.xshd new file mode 100644 index 0000000..edef924 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/C#.xshd @@ -0,0 +1,238 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~!%^*()-+=|\#/{}[]:;"' , .? + + + ///@!/@ + + + + //@!/@ + + + //// + + + + /* + */ + + + + " + " + + + + @@" + " + + + + ' + ' + + + + # + + + class + interface + struct + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <>~!@%^*()-+=|\#/{}[]:;"' , .? + + + + + + + + + + + <>~!@%^*()-+=|\#/{}[]:;"' , .? + + + < + > + + + + + + + + + + + + &<>~!%^*()-+=|\#/{}[]:;"' , .? + + + + + + + + + + + + + + + + + + + /= + + + " + " + + + + ' + ' + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/C++.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/C++.xshd new file mode 100644 index 0000000..bee5772 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/C++.xshd @@ -0,0 +1,291 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ~!%^*()-+=|\/{}[]:;"'<> , .? + + + // + + + + /** + */ + + + + /* + */ + + + + " + " + + + + ' + ' + + + + #include + + + class + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ~!%^*()+=|\#/{}[];"'<> , .? + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/C.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/C.xshd new file mode 100644 index 0000000..ea3af64 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/C.xshd @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{},:.`=;+-*/%~ &|^>< + + + // + + + + /** + */ + + + + /* + */ + + + + " + " + + + + ' + ' + + + + #include + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ~!%^*()+=|\#/{}[];"'<> , .? + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/CSS.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/CSS.xshd new file mode 100644 index 0000000..b07942b --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/CSS.xshd @@ -0,0 +1,724 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + :#@.() + + + { + } + + + + /* + */ + + + + " + " + + + @ + # + + + + + + /* + */ + + + + " + " + + + + ' + ' + + + + + + + + + # + + + : + ; + + + + /* + */ + + + # + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Ceylon.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Ceylon.xshd new file mode 100644 index 0000000..a280459 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Ceylon.xshd @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{},:`=;+-_$#*@/%~ &|^>< + + + // + + + + /* + */ + + + + " + " + + + + ' + ' + + + class + import + @ + # + $ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ~!%^*()-+=|\#/{}[]:;"'<> , .? + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/ChucK.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/ChucK.xshd new file mode 100644 index 0000000..928a615 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/ChucK.xshd @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &~!@%^*()-+=|\#/{}[]:;"'<>,.? + + + // + + + + /* + */ + + + + " + " + + + + ' + ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Clojure.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Clojure.xshd new file mode 100644 index 0000000..f0d645c --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Clojure.xshd @@ -0,0 +1,295 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + `*()[]{}+=|\/:;"' + + + ; + + + + #" + " + + + + " + " + + + : + def + defn + defmacro + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Cocoa.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Cocoa.xshd new file mode 100644 index 0000000..e7de6db --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Cocoa.xshd @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~!@%^*()-+=|\#/{}[]:;"' , .? + + + // + + + + /* + */ + + + + COMPILER + TOKENNAMES + + + + " + " + + + + ' + ' + + + + < + > + + + + (. + .) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/CoffeeScript.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/CoffeeScript.xshd new file mode 100644 index 0000000..996c8f4 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/CoffeeScript.xshd @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{},.`#=;+-*@/%~ &|^>< + + + ### + ### + + + + # + + + + " + " + + + + ' + ' + + + @ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Cool.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Cool.xshd new file mode 100644 index 0000000..f8d48f3 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Cool.xshd @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{}#@!,:.`=;+-*/%~ &|^>< + + + -- + + + + (* + *) + + + + " + " + + + class + inherits + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/D.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/D.xshd new file mode 100644 index 0000000..5bfa2ab --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/D.xshd @@ -0,0 +1,357 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~!%^*()-+=|\#/{}[]:;"' , .? + + + // + + + + /* + */ + + + + ' + ' + + + + " + " + + + ! + class + void + function + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Dart.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Dart.xshd new file mode 100644 index 0000000..b3dfde6 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Dart.xshd @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{},:.`#=;+-*@/%~ &|^>< + + + // + + + + /* + */ + + + + " + " + + + + ''' + ''' + + + + ' + ' + + + void + @ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~!@%^*()-+=|\#/{}[]:;"' , .? + + + ${ + } + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Delphi.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Delphi.xshd new file mode 100644 index 0000000..532589b --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Delphi.xshd @@ -0,0 +1,772 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~!@%^*()-+=|\#/{}[]:;"' , .? + + + // + + + + { + } + + + + (* + *) + + + + ' + ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Eiffel.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Eiffel.xshd new file mode 100644 index 0000000..faf16bb --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Eiffel.xshd @@ -0,0 +1,489 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~!%^*()-+=|\#/{}[]:;"' , .? + + + -- + + + + " + " + + + + ' + ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Elixir.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Elixir.xshd new file mode 100644 index 0000000..50a54a2 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Elixir.xshd @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~%^*()-+=!|\/{}[]:;"' , ? + + + # + + + + ' + ' + + + + " + " + + + def + defp + defmodule + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Erlang.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Erlang.xshd new file mode 100644 index 0000000..1503278 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Erlang.xshd @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>@~%^*().+=!|\/{}[]:;"' , ? + + + % + + + + ' + ' + + + + " + " + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/F#.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/F#.xshd new file mode 100644 index 0000000..1392914 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/F#.xshd @@ -0,0 +1,232 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~!%^*()-+=|\#/{}[]:;"' , .? + + + ///@!/@ + + + + //@!/@ + + + + //// + + + + (* + *) + + + + """ + """ + + + + " + " + + + + @@" + " + + + module + # + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <>~!@%^*()-+=|\#/{}[]:;"' , .? + + + < + > + + + + + &<>~!@%^*()-+=|\#/{}[]:;"' , .? + + + (* + *) + + + + + <>~!@%^*()-+=|\#/{}[]:;"' , .? + + + " + " + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Falcon.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Falcon.xshd new file mode 100644 index 0000000..31e4de4 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Falcon.xshd @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{},:.@`=;+-*/%~ &|^>< + + + // + + + + /* + */ + + + + " + " + + + + ' + ' + + + + #! + + + class + function + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Fantom.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Fantom.xshd new file mode 100644 index 0000000..97486a5 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Fantom.xshd @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{},:.@`=;+-*/%~ &|^>< + + + // + + + + ** + + + + /* + */ + + + + " + " + + + + ` + ` + + + + #! + + + class + @ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~$!@%^*()-+=|\#/{}[]:;"' , ? + + + ${ + } + + + $ + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Fortran95.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Fortran95.xshd new file mode 100644 index 0000000..74dea46 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Fortran95.xshd @@ -0,0 +1,446 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~!%^*()-+=|\#/{}[]:;"' , ? + + + ! + + + + " + " + + + + ' + ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Go.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Go.xshd new file mode 100644 index 0000000..582c923 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Go.xshd @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{}@,.:`=;+-*%~ &|^>< + + + // + + + + /* + */ + + + + " + " + + + + ' + ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Groovy.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Groovy.xshd new file mode 100644 index 0000000..3e5c4d1 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Groovy.xshd @@ -0,0 +1,225 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{}@,.:`=;+-*%~ &|^>< + + + // + + + + /* + */ + + + + " + " + + + + /@! @@!/@ + / + + + + ' + ' + + + + #! + + + class + implements + def + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Gui4Cli.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Gui4Cli.xshd new file mode 100644 index 0000000..43e5105 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Gui4Cli.xshd @@ -0,0 +1,420 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~!^*()-+=|\/{}[];"' , ? + + + // + + + + /* + */ + + + + " + " + + + + " + " + + + + ' + ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/HTML.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/HTML.xshd new file mode 100644 index 0000000..64c9e10 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/HTML.xshd @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <!-- + --> + + + + <script> + </script> + + + + < + > + + + + & + ; + + + + + + + /= + + + " + " + + + + ' + ' + + + = + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Haskell.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Haskell.xshd new file mode 100644 index 0000000..5d1c5dc --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Haskell.xshd @@ -0,0 +1,511 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + =+-*/()`,#":;.@|^$><[]{} + + + -- + + + + {- + -} + + + + " + " + + + # + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Haxe.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Haxe.xshd new file mode 100644 index 0000000..228fc6b --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Haxe.xshd @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{},:.`#=;+-*/%~ &|^>< + + + // + + + + /* + */ + + + + " + " + + + + ' + ' + + + + # + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/ICSharpCode.TextEditor.snk b/NetCore31/src/ICSharpCode.TextEditor/Resources/ICSharpCode.TextEditor.snk new file mode 100644 index 0000000..a49f400 Binary files /dev/null and b/NetCore31/src/ICSharpCode.TextEditor/Resources/ICSharpCode.TextEditor.snk differ diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/ILYC.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/ILYC.xshd new file mode 100644 index 0000000..a996ce0 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/ILYC.xshd @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{},:.`=;+-*/%~ &|^>< + + + # + + + + " + " + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/INI.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/INI.xshd new file mode 100644 index 0000000..bbb89aa --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/INI.xshd @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &|\/"',;=:- + + + ; + + + + # + + + + " + " + + + + [ + ] + + + = + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Icon.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Icon.xshd new file mode 100644 index 0000000..9b20968 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Icon.xshd @@ -0,0 +1,251 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -+|#[]()',. + + + # + + + + " + " + + + + ' + ' + + + procedure + $if + $ifdef + $ifndef + $elif + $else + $endif + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Io.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Io.xshd new file mode 100644 index 0000000..75edd6d --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Io.xshd @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{}!@#$^,:.@`=;+-*/%~ &|^>< + + + // + + + + /* + */ + + + + " + " + + + + """ + """ + + + + # + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~$!@%^*()-+=|\#/{}[]:;"' , .? + + + #{ + } + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/JSON.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/JSON.xshd new file mode 100644 index 0000000..3b7de51 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/JSON.xshd @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~%^*()-+=!|\/{}[]:;"' , ? + + + // + + + + /* + */ + + + + " + " + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Java.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Java.xshd new file mode 100644 index 0000000..a244be0 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Java.xshd @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ~!%^*()-+=|\#/{}[]:;"'<> , .? + + + // + + + + /** + */ + + + + /* + */ + + + + " + " + + + + ' + ' + + + class + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ~!%^*()-+=|\#/{}[];"'<> , .? + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/JavaScript.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/JavaScript.xshd new file mode 100644 index 0000000..07788e2 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/JavaScript.xshd @@ -0,0 +1,341 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ~!%^&*{}[]()-+=/|;:,.?>< + + + // + + + + /** + */ + + + + /* + */ + + + + " + " + + + + ' + ' + + + + ` + ` + + + + / + / + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ~!%^*()+=|\#/{}[];"'<> , .? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Julia.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Julia.xshd new file mode 100644 index 0000000..6abac5b --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Julia.xshd @@ -0,0 +1,293 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{}#@!,.`'=:;+-*/%~ &|^>< + + + #= + =# + + + + # + + + + """ + """ + + + + " + " + + + + ' + ' + + + + ` + ` + + + @ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~!@%^*()-+=|\#/{}[]:;"' , .? + + + #= + =# + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Just BASIC.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Just BASIC.xshd new file mode 100644 index 0000000..aa0d0ec --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Just BASIC.xshd @@ -0,0 +1,250 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + =+-*/(),#";:.|^$><[] + + + ' + + + + " + " + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/KiXtart.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/KiXtart.xshd new file mode 100644 index 0000000..3abac4e --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/KiXtart.xshd @@ -0,0 +1,297 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~!$%^*()-+=|\#/{}[]:;"' , .? + + + ; + + + + " + " + + + + ' + ' + + + + : + + + $ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Kotlin.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Kotlin.xshd new file mode 100644 index 0000000..8132de2 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Kotlin.xshd @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~!@$%^*()-+=|\#/{}[]:;"' , .? + + + // + + + + /* + */ + + + + """ + """ + + + + " + " + + + + ' + ' + + + val + var + class + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~$!@%^*()-+=|\#/{}[]:;"' , .? + + + ${ + } + + + $ + + + + + + + + ~!%^*()+=|\#/{}[]:;"'<> , .? + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Lean.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Lean.xshd new file mode 100644 index 0000000..f49a01b --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Lean.xshd @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~!%^*()-+=|\/{}.:;"' , ? + + + -- + + + + /- + -/ + + + + " + " + + + axiom + definition + inductive + structure + theorem + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Lisp.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Lisp.xshd new file mode 100644 index 0000000..e8f39c1 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Lisp.xshd @@ -0,0 +1,253 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + `*()[]{}=|\/:;"' + + + ; + + + + #| + |# + + + + " + " + + + : + defun + defsetf + defmacro + defmethod + defconstant + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Lua.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Lua.xshd new file mode 100644 index 0000000..926239d --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Lua.xshd @@ -0,0 +1,419 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~!%^*()-+=|\/{}[]:;"' , ? + + + --[[ + ]] + + + + -- + + + + " + " + + + + ' + ' + + + + [[ + ]] + + + + $ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ~!%^*()-+=|\#/{}[]:;"'<> , .? + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Markdown.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Markdown.xshd new file mode 100644 index 0000000..44521c7 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Markdown.xshd @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + /= + + + " + " + + + + ' + ' + + + = + + + + + + + + + + + + + [ + ] + + + + (http + ) + + + + #### + + + ### + + + + ## + + + + # + + + + ** + ** + + + + __ + __ + + + + _ + _ + + + + + + + > + + + + < + > + + + + ``` + ``` + + + + ` + ` + + + + + + + + \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Mode.xsd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Mode.xsd new file mode 100644 index 0000000..b021b5e --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Mode.xsd @@ -0,0 +1,297 @@ + + + + + + This schema defines the syntax for mode definitions in SharpDevelop. + The schema can be simplified quite a bit but it does the job as is. + + + If you are using this file as a reference it is probably easiest to scroll to + the botton to find the definition of the root element called SyntaxDefinition and + then unwind the different type definitions and refernces. + + Note on coloring: + Many tags define how some symbol should be colored. If a specific symbol + can not be matched onto either a Span definition, Keyword, or a Digit/Number it + will be rendered in the current default color. Which is the default color of the + current span or the default color of the mode as a whole if no span has been entered. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Nemerle.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Nemerle.xshd new file mode 100644 index 0000000..7f1aa47 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Nemerle.xshd @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{},:.`=;+-*/%~ &|^>< + + + // + + + + /* + */ + + + + @@" + " + + + + " + " + + + class + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Nim.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Nim.xshd new file mode 100644 index 0000000..bd358ea --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Nim.xshd @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{},:.`=;+-*/%~ &|^>< + + + # + + + + " + " + + + + ' + ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/OCaml.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/OCaml.xshd new file mode 100644 index 0000000..16ff10f --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/OCaml.xshd @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~!%^*()-+=|\#/{}[]:;"' , ? + + + (* + *) + + + + " + " + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~!@%^*()-+=|\#/{}[]:;"' , .? + + + (* + *) + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Objective-C.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Objective-C.xshd new file mode 100644 index 0000000..3b5ac48 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Objective-C.xshd @@ -0,0 +1,231 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{},:.`=;+-*/%~ &|^>< + + + // + + + + /** + */ + + + + /* + */ + + + + " + " + + + + @@" + " + + + + ' + ' + + + + # + + + class + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ~!%^*()+=|\#/{}[];"'<> , .? + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/PHP.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/PHP.xshd new file mode 100644 index 0000000..0b4a2c0 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/PHP.xshd @@ -0,0 +1,4562 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ~!%^*()-+=|\#/{}[]:;$"' , . + + + //@!/@ + + + + # + + + + /* + */ + + + + " + " + + + + @@" + " + + + + ' + ' + + + + class + trait + interface + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ~!%^*()-+=|\#/{}[]:;"'<> , .? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/ParaSail.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/ParaSail.xshd new file mode 100644 index 0000000..50d6c45 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/ParaSail.xshd @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{},:.`#=;+-*@/%~ &|^>< + + + // + + + + /* + */ + + + + " + " + + + void + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Pascal.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Pascal.xshd new file mode 100644 index 0000000..4b0efcf --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Pascal.xshd @@ -0,0 +1,303 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~!@%^*()-+=|\#/{}[]:;"' , ? + + + // + + + + { + } + + + + (* + *) + + + + ' + ' + + + ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Patch-Mode.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Patch-Mode.xshd new file mode 100644 index 0000000..1f7100e --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Patch-Mode.xshd @@ -0,0 +1,37 @@ + + + + + + + + ~!%^*()-+=|\#/{}[]:;"'<> , .? + + + Index: + + + == + + + --- + + + +++ + + + @@ + + + - + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Pike.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Pike.xshd new file mode 100644 index 0000000..2202f48 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Pike.xshd @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{}@!,:.`=;+-*/%~ &|^>< + + + // + + + + /* + */ + + + + " + " + + + + ' + ' + + + + # + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/PowerShell.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/PowerShell.xshd new file mode 100644 index 0000000..30df088 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/PowerShell.xshd @@ -0,0 +1,650 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{}@,.:$`=;+*%~ &|^>< + + + # + + + + <# + #> + + + + " + " + + + + @@" + "@@ + + + + ' + ' + + + $ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ~!%^*()-+=|\#/{}[];"'<> , ? + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Prolog.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Prolog.xshd new file mode 100644 index 0000000..b72d270 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Prolog.xshd @@ -0,0 +1,802 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + =+-*/`,#":;.@|^$><[]{}() + + + % + + + + /* + */ + + + + " + " + + + + ' + ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/PureScript.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/PureScript.xshd new file mode 100644 index 0000000..a20351b --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/PureScript.xshd @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + =+-*/`,#";.@|^$><[]{}() + + + -- + + + + {- + -} + + + + """ + """ + + + + " + " + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~!@%^*()-+=|\#/{}[]:;"' , .? + + + {- + -} + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Python.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Python.xshd new file mode 100644 index 0000000..59540d4 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Python.xshd @@ -0,0 +1,209 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@^*()-+=\/[]{}"' ,:;. + + + """ + """ + + + + ''' + ''' + + + + # + + + + " + " + + + + ' + ' + + + + @@ + + + def + class + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/R.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/R.xshd new file mode 100644 index 0000000..ce1f1d9 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/R.xshd @@ -0,0 +1,430 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~!%^*()-+=|\#/{}[]:;"' , ? + + + # + + + + " + " + + + + ' + ' + + + + % + % + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Registry.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Registry.xshd new file mode 100644 index 0000000..1805123 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Registry.xshd @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &|\/()[]"',;@=:- + + + ; + + + + " + " + + + + [ + ] + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Resource.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Resource.xshd new file mode 100644 index 0000000..d2a93f5 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Resource.xshd @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~!%^*()-+=|\#/{}[]:;"' , .? + + + // + + + + /* + */ + + + + " + " + + + + ' + ' + + + + # + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Rexx.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Rexx.xshd new file mode 100644 index 0000000..c20b3e7 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Rexx.xshd @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + =+-*/`,#":;.@|^$><[]{}() + + + /* + */ + + + + " + " + + + + ' + ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~!@%^*()-+=|\#/{}[]:;"' , .? + + + /* + */ + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/RightArrow.cur b/NetCore31/src/ICSharpCode.TextEditor/Resources/RightArrow.cur new file mode 100644 index 0000000..5691efb Binary files /dev/null and b/NetCore31/src/ICSharpCode.TextEditor/Resources/RightArrow.cur differ diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Rust.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Rust.xshd new file mode 100644 index 0000000..a02a0fc --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Rust.xshd @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <>&~!%^*()-_+=|\#/{}[]:;"' , ? + + + // + + + + /* + */ + + + + " + " + + + ! + ' + enum + type + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/SQF.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/SQF.xshd new file mode 100644 index 0000000..13971fb --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/SQF.xshd @@ -0,0 +1,2022 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{},:.`=;+-*/%~ &|^>< + + + // + + + + /* + */ + + + + " + " + + + + ' + ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/SQL.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/SQL.xshd new file mode 100644 index 0000000..3e283ec --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/SQL.xshd @@ -0,0 +1,695 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~!@%^*()-+=|\#/{}[]:;"' , .? + + + -- + + + + # + + + + /* + */ + + + + ' + ' + + + + " + " + + + @ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Scala.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Scala.xshd new file mode 100644 index 0000000..87524e5 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Scala.xshd @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{}@!,:.`=;+-*/%~ &|^>< + + + // + + + + /* + */ + + + + """ + """ + + + + " + " + + + + ' + ' + + + + # + + + def + val + var + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Scheme.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Scheme.xshd new file mode 100644 index 0000000..1c66018 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Scheme.xshd @@ -0,0 +1,306 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + `()[]{}|\:;"' + + + ; + + + + #| + |# + + + + " + " + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Solidity.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Solidity.xshd new file mode 100644 index 0000000..a98bfa3 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Solidity.xshd @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &<>~!@$%^*()-+=|\#/{}[]:;"' , .? + + + /// + + + + // + + + + /** + */ + + + + /* + */ + + + + " + " + + + + ' + ' + + + contract + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ~!%^*()+=|\#/{}[]:;"'<> , .? + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Spike.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Spike.xshd new file mode 100644 index 0000000..c9603ba --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Spike.xshd @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{}#@!,:.`=;+-*/%~ &|^>< + + + // + + + + /* + */ + + + + " + " + + + + #! + + + : + class + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Swift.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Swift.xshd new file mode 100644 index 0000000..78f02ea --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Swift.xshd @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{}@,:`=;+-*/%~ &|^><? + + + // + + + + /* + */ + + + + " + " + + + @ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ~!%^*()-+=|\#/{}[];"'<> , .? + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/SyntaxModes.xml b/NetCore31/src/ICSharpCode.TextEditor/Resources/SyntaxModes.xml new file mode 100644 index 0000000..8452b62 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/SyntaxModes.xml @@ -0,0 +1,361 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/TCL.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/TCL.xshd new file mode 100644 index 0000000..5a8f0e1 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/TCL.xshd @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{}$,.@`=;+-*/%~ &|^>< + + + # + + + + " + " + + + $ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Tex-Mode.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Tex-Mode.xshd new file mode 100644 index 0000000..91083b0 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Tex-Mode.xshd @@ -0,0 +1,108 @@ + + + + + + + + + + &~!@%^*()-+=|\#/{}[]:;"'<> , .? + + + % + + + + $$ + $$ + + + \[ + \] + + + + \ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &~!@%^*()-+=|\#/{}[]:;"'<> , .? + + + + % + + + + \ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/TextEditorControl.bmp b/NetCore31/src/ICSharpCode.TextEditor/Resources/TextEditorControl.bmp new file mode 100644 index 0000000..4bd8280 Binary files /dev/null and b/NetCore31/src/ICSharpCode.TextEditor/Resources/TextEditorControl.bmp differ diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Thrift.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Thrift.xshd new file mode 100644 index 0000000..db0948d --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Thrift.xshd @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{},:.@`=;+-*/%~ &|^>< + + + // + + + + /** + */ + + + + /* + */ + + + + " + " + + + + ' + ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ~!%^*()-+=|\#/{}[];"'<> , .? + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/TypeScript.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/TypeScript.xshd new file mode 100644 index 0000000..0d2c79e --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/TypeScript.xshd @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{},:.@`=;+-*/%~ &|^>< + + + // + + + + /* + */ + + + + " + " + + + + ' + ' + + + class + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/VB.NET.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/VB.NET.xshd new file mode 100644 index 0000000..86d78b4 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/VB.NET.xshd @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ~!@%^*()-+=|\#/{}[]:;"'<>,.? + + + ' + + + + REM@C + + + + " + " + + + + # + + + + # + # + + + class + interface + module + structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ~!@%^*()-+=|\#/{}[]:;"'<>,.? + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/VBScript.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/VBScript.xshd new file mode 100644 index 0000000..42054cc --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/VBScript.xshd @@ -0,0 +1,341 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ~!@%^*()-+=|\#/{}[]:;"'<>,.? + + + ' + + + + REM + + + + " + " + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/VHDL.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/VHDL.xshd new file mode 100644 index 0000000..001cb79 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/VHDL.xshd @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &~!@%^*()-+=|\#/{}[]:;"'<>,? + + + -- + + + + " + " + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/VS Solution.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/VS Solution.xshd new file mode 100644 index 0000000..f4a6420 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/VS Solution.xshd @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ~!@%^*()-+=|\#/{}[]:;"'<>,.? + + + # + + + + " + " + + + + { + } + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Vala.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Vala.xshd new file mode 100644 index 0000000..eea67cc --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Vala.xshd @@ -0,0 +1,190 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{}#@!,:.`=;+-*/%~ &|^>< + + + // + + + + /* + */ + + + + """ + """ + + + + " + " + + + + @@" + " + + + + ' + ' + + + # + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ~!%^*()-+=|\#/{}[];"'<> , .? + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Verilog.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Verilog.xshd new file mode 100644 index 0000000..67c0117 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Verilog.xshd @@ -0,0 +1,279 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &~!@%^*()-+=|\#/{}[]:;"'<>,.? + + + // + + + + /* + */ + + + + " + " + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Volt.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Volt.xshd new file mode 100644 index 0000000..4d08158 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Volt.xshd @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{},:.@`=;+-!#$*/%~ &|^>< + + + // + + + + /* + */ + + + + " + " + + + + ` + ` + + + + ' + ' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/X10.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/X10.xshd new file mode 100644 index 0000000..d8aca0c --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/X10.xshd @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{},:.@`=;+-*/%~ &|^>< + + + // + + + + /* + */ + + + + " + " + + + class + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/XC.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/XC.xshd new file mode 100644 index 0000000..1f6e976 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/XC.xshd @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ()[]{},:.@`=;+-*/%~ &|^>< + + + // + + + + /* + */ + + + + #include + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/XML.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/XML.xshd new file mode 100644 index 0000000..b2209a7 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/XML.xshd @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <!-- + --> + + + + <![CDATA[ + ]]> + + + + <!DOCTYPE + > + + + + <? + ?> + + + + < + > + + + + & + ; + + + + + /= + + + " + " + + + + ' + ' + + + = + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Resources/Xtend.xshd b/NetCore31/src/ICSharpCode.TextEditor/Resources/Xtend.xshd new file mode 100644 index 0000000..347de21 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Resources/Xtend.xshd @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ~@!%^*()-+=|\#/{}[]:;"'<> , .? + + + // + + + + /* + */ + + + + " + " + + + + ''' + ''' + + + + ' + ' + + + class + @ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Actions/BookmarkActions.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Actions/BookmarkActions.cs new file mode 100644 index 0000000..25e6252 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Actions/BookmarkActions.cs @@ -0,0 +1,81 @@ +// +// +// +// +// $Revision$ +// + +using System; +using ICSharpCode.TextEditor.Document; + +namespace ICSharpCode.TextEditor.Actions +{ + public class ToggleBookmark : AbstractEditAction + { + public override void Execute(TextArea textArea) + { + textArea.Document.BookmarkManager.ToggleMarkAt(textArea.Caret.Position); + textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.SingleLine, textArea.Caret.Line)); + textArea.Document.CommitUpdate(); + } + } + + public class GotoPrevBookmark : AbstractEditAction + { + private readonly Predicate predicate; + + public GotoPrevBookmark(Predicate predicate) + { + this.predicate = predicate; + } + + public override void Execute(TextArea textArea) + { + var mark = textArea.Document.BookmarkManager.GetPrevMark(textArea.Caret.Line, predicate); + if (mark != null) + { + textArea.Caret.Position = mark.Location; + textArea.SelectionManager.ClearSelection(); + textArea.SetDesiredColumn(); + } + } + } + + public class GotoNextBookmark : AbstractEditAction + { + private readonly Predicate predicate; + + public GotoNextBookmark(Predicate predicate) + { + this.predicate = predicate; + } + + public override void Execute(TextArea textArea) + { + var mark = textArea.Document.BookmarkManager.GetNextMark(textArea.Caret.Line, predicate); + if (mark != null) + { + textArea.Caret.Position = mark.Location; + textArea.SelectionManager.ClearSelection(); + textArea.SetDesiredColumn(); + } + } + } + + public class ClearAllBookmarks : AbstractEditAction + { + private readonly Predicate predicate; + + public ClearAllBookmarks(Predicate predicate) + { + this.predicate = predicate; + } + + public override void Execute(TextArea textArea) + { + textArea.Document.BookmarkManager.RemoveMarks(predicate); + textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.WholeTextArea)); + textArea.Document.CommitUpdate(); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Actions/CaretActions.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Actions/CaretActions.cs new file mode 100644 index 0000000..3d029ea --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Actions/CaretActions.cs @@ -0,0 +1,227 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Drawing; +using ICSharpCode.TextEditor.Document; + +namespace ICSharpCode.TextEditor.Actions +{ + public class CaretLeft : AbstractEditAction + { + public override void Execute(TextArea textArea) + { + var position = textArea.Caret.Position; + var foldings = textArea.Document.FoldingManager.GetFoldedFoldingsWithEnd(position.Y); + FoldMarker justBeforeCaret = null; + foreach (var fm in foldings) + if (fm.EndColumn == position.X) + { + justBeforeCaret = fm; + break; // the first folding found is the folding with the smallest Startposition + } + + if (justBeforeCaret != null) + { + position.Y = justBeforeCaret.StartLine; + position.X = justBeforeCaret.StartColumn; + } + else + { + if (position.X > 0) + { + --position.X; + } + else if (position.Y > 0) + { + var lineAbove = textArea.Document.GetLineSegment(position.Y - 1); + position = new TextLocation(lineAbove.Length, position.Y - 1); + } + } + + textArea.Caret.Position = position; + textArea.SetDesiredColumn(); + } + } + + public class CaretRight : AbstractEditAction + { + public override void Execute(TextArea textArea) + { + var curLine = textArea.Document.GetLineSegment(textArea.Caret.Line); + var position = textArea.Caret.Position; + var foldings = textArea.Document.FoldingManager.GetFoldedFoldingsWithStart(position.Y); + FoldMarker justBehindCaret = null; + foreach (var fm in foldings) + if (fm.StartColumn == position.X) + { + justBehindCaret = fm; + break; + } + + if (justBehindCaret != null) + { + position.Y = justBehindCaret.EndLine; + position.X = justBehindCaret.EndColumn; + } + else + { + // no folding is interesting + if (position.X < curLine.Length || textArea.TextEditorProperties.AllowCaretBeyondEOL) + { + ++position.X; + } + else if (position.Y + 1 < textArea.Document.TotalNumberOfLines) + { + ++position.Y; + position.X = 0; + } + } + + textArea.Caret.Position = position; + textArea.SetDesiredColumn(); + } + } + + public class CaretUp : AbstractEditAction + { + public override void Execute(TextArea textArea) + { + var position = textArea.Caret.Position; + var lineNr = position.Y; + var visualLine = textArea.Document.GetVisibleLine(lineNr); + if (visualLine > 0) + { + var pos = new Point( + textArea.TextView.GetDrawingXPos(lineNr, position.X), + textArea.TextView.DrawingPosition.Y + (visualLine - 1)*textArea.TextView.FontHeight - textArea.TextView.TextArea.VirtualTop.Y); + textArea.Caret.Position = textArea.TextView.GetLogicalPosition(pos); + textArea.SetCaretToDesiredColumn(); + } + +// if (textArea.Caret.Line > 0) { +// textArea.SetCaretToDesiredColumn(textArea.Caret.Line - 1); +// } + } + } + + public class CaretDown : AbstractEditAction + { + public override void Execute(TextArea textArea) + { + var position = textArea.Caret.Position; + var lineNr = position.Y; + var visualLine = textArea.Document.GetVisibleLine(lineNr); + if (visualLine < textArea.Document.GetVisibleLine(textArea.Document.TotalNumberOfLines)) + { + var pos = new Point( + textArea.TextView.GetDrawingXPos(lineNr, position.X), + textArea.TextView.DrawingPosition.Y + + (visualLine + 1)*textArea.TextView.FontHeight + - textArea.TextView.TextArea.VirtualTop.Y); + textArea.Caret.Position = textArea.TextView.GetLogicalPosition(pos); + textArea.SetCaretToDesiredColumn(); + } + +// if (textArea.Caret.Line + 1 < textArea.Document.TotalNumberOfLines) { +// textArea.SetCaretToDesiredColumn(textArea.Caret.Line + 1); +// } + } + } + + public class WordRight : CaretRight + { + public override void Execute(TextArea textArea) + { + var line = textArea.Document.GetLineSegment(textArea.Caret.Position.Y); + var oldPos = textArea.Caret.Position; + TextLocation newPos; + if (textArea.Caret.Column >= line.Length) + { + newPos = new TextLocation(column: 0, textArea.Caret.Line + 1); + } + else + { + var nextWordStart = TextUtilities.FindNextWordStart(textArea.Document, textArea.Caret.Offset); + newPos = textArea.Document.OffsetToPosition(nextWordStart); + } + + // handle fold markers + var foldings = textArea.Document.FoldingManager.GetFoldingsFromPosition(newPos.Y, newPos.X); + foreach (var marker in foldings) + if (marker.IsFolded) + { + if (oldPos.X == marker.StartColumn && oldPos.Y == marker.StartLine) + newPos = new TextLocation(marker.EndColumn, marker.EndLine); + else + newPos = new TextLocation(marker.StartColumn, marker.StartLine); + break; + } + + textArea.Caret.Position = newPos; + textArea.SetDesiredColumn(); + } + } + + public class WordLeft : CaretLeft + { + public override void Execute(TextArea textArea) + { + var oldPos = textArea.Caret.Position; + if (textArea.Caret.Column == 0) + { + base.Execute(textArea); + } + else + { + //var line = textArea.Document.GetLineSegment(textArea.Caret.Position.Y); + + var prevWordStart = TextUtilities.FindPrevWordStart(textArea.Document, textArea.Caret.Offset); + + var newPos = textArea.Document.OffsetToPosition(prevWordStart); + + // handle fold markers + var foldings = textArea.Document.FoldingManager.GetFoldingsFromPosition(newPos.Y, newPos.X); + foreach (var marker in foldings) + if (marker.IsFolded) + { + if (oldPos.X == marker.EndColumn && oldPos.Y == marker.EndLine) + newPos = new TextLocation(marker.StartColumn, marker.StartLine); + else + newPos = new TextLocation(marker.EndColumn, marker.EndLine); + break; + } + + textArea.Caret.Position = newPos; + textArea.SetDesiredColumn(); + } + } + } + + public class ScrollLineUp : AbstractEditAction + { + public override void Execute(TextArea textArea) + { + textArea.AutoClearSelection = false; + + textArea.MotherTextAreaControl.VScrollBar.Value = Math.Max( + textArea.MotherTextAreaControl.VScrollBar.Minimum, + textArea.VirtualTop.Y - textArea.TextView.FontHeight); + } + } + + public class ScrollLineDown : AbstractEditAction + { + public override void Execute(TextArea textArea) + { + textArea.AutoClearSelection = false; + textArea.MotherTextAreaControl.VScrollBar.Value = Math.Min( + textArea.MotherTextAreaControl.VScrollBar.Maximum, + textArea.VirtualTop.Y + textArea.TextView.FontHeight); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Actions/ClipBoardActions.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Actions/ClipBoardActions.cs new file mode 100644 index 0000000..6d22717 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Actions/ClipBoardActions.cs @@ -0,0 +1,38 @@ +// +// +// +// +// $Revision$ +// + +namespace ICSharpCode.TextEditor.Actions +{ + public class Cut : AbstractEditAction + { + public override void Execute(TextArea textArea) + { + if (textArea.Document.ReadOnly) + return; + textArea.ClipboardHandler.Cut(sender: null, e: null); + } + } + + public class Copy : AbstractEditAction + { + public override void Execute(TextArea textArea) + { + textArea.AutoClearSelection = false; + textArea.ClipboardHandler.Copy(sender: null, e: null); + } + } + + public class Paste : AbstractEditAction + { + public override void Execute(TextArea textArea) + { + if (textArea.Document.ReadOnly) + return; + textArea.ClipboardHandler.Paste(sender: null, e: null); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Actions/FoldActions.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Actions/FoldActions.cs new file mode 100644 index 0000000..0ba3436 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Actions/FoldActions.cs @@ -0,0 +1,68 @@ +// +// +// +// +// $Revision$ +// + +using System; +using ICSharpCode.TextEditor.Document; + +namespace ICSharpCode.TextEditor.Actions +{ + public class ToggleFolding : AbstractEditAction + { + public override void Execute(TextArea textArea) + { + var foldMarkers = textArea.Document.FoldingManager.GetFoldingsWithStart(textArea.Caret.Line); + if (foldMarkers.Count != 0) + { + foreach (var fm in foldMarkers) + fm.IsFolded = !fm.IsFolded; + } + else + { + foldMarkers = textArea.Document.FoldingManager.GetFoldingsContainsLineNumber(textArea.Caret.Line); + if (foldMarkers.Count != 0) + { + var innerMost = foldMarkers[index: 0]; + for (var i = 1; i < foldMarkers.Count; i++) + if (new TextLocation(foldMarkers[i].StartColumn, foldMarkers[i].StartLine) > + new TextLocation(innerMost.StartColumn, innerMost.StartLine)) + innerMost = foldMarkers[i]; + innerMost.IsFolded = !innerMost.IsFolded; + } + } + + textArea.Document.FoldingManager.NotifyFoldingsChanged(EventArgs.Empty); + } + } + + public class ToggleAllFoldings : AbstractEditAction + { + public override void Execute(TextArea textArea) + { + var doFold = true; + foreach (var fm in textArea.Document.FoldingManager.FoldMarker) + if (fm.IsFolded) + { + doFold = false; + break; + } + + foreach (var fm in textArea.Document.FoldingManager.FoldMarker) + fm.IsFolded = doFold; + textArea.Document.FoldingManager.NotifyFoldingsChanged(EventArgs.Empty); + } + } + + public class ShowDefinitionsOnly : AbstractEditAction + { + public override void Execute(TextArea textArea) + { + foreach (var fm in textArea.Document.FoldingManager.FoldMarker) + fm.IsFolded = fm.FoldType == FoldType.MemberBody || fm.FoldType == FoldType.Region; + textArea.Document.FoldingManager.NotifyFoldingsChanged(EventArgs.Empty); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Actions/FormatActions.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Actions/FormatActions.cs new file mode 100644 index 0000000..093f5e0 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Actions/FormatActions.cs @@ -0,0 +1,212 @@ +// +// +// +// +// $Revision$ +// + +using System.Text; +using ICSharpCode.TextEditor.Document; + +namespace ICSharpCode.TextEditor.Actions +{ + public abstract class AbstractLineFormatAction : AbstractEditAction + { + protected TextArea textArea; + protected abstract void Convert(IDocument document, int startLine, int endLine); + + public override void Execute(TextArea textArea) + { + if (textArea.SelectionManager.SelectionIsReadonly) + return; + this.textArea = textArea; + textArea.BeginUpdate(); + textArea.Document.UndoStack.StartUndoGroup(); + if (textArea.SelectionManager.HasSomethingSelected) + foreach (var selection in textArea.SelectionManager.SelectionCollection) + Convert(textArea.Document, selection.StartPosition.Y, selection.EndPosition.Y); + else + Convert(textArea.Document, startLine: 0, textArea.Document.TotalNumberOfLines - 1); + textArea.Document.UndoStack.EndUndoGroup(); + textArea.Caret.ValidateCaretPos(); + textArea.EndUpdate(); + textArea.Refresh(); + } + } + + public abstract class AbstractSelectionFormatAction : AbstractEditAction + { + protected TextArea textArea; + protected abstract void Convert(IDocument document, int offset, int length); + + public override void Execute(TextArea textArea) + { + if (textArea.SelectionManager.SelectionIsReadonly) + return; + this.textArea = textArea; + textArea.BeginUpdate(); + if (textArea.SelectionManager.HasSomethingSelected) + foreach (var selection in textArea.SelectionManager.SelectionCollection) + Convert(textArea.Document, selection.Offset, selection.Length); + else + Convert(textArea.Document, offset: 0, textArea.Document.TextLength); + textArea.Caret.ValidateCaretPos(); + textArea.EndUpdate(); + textArea.Refresh(); + } + } + + public class RemoveLeadingWS : AbstractLineFormatAction + { + protected override void Convert(IDocument document, int y1, int y2) + { + for (var i = y1; i < y2; ++i) + { + var line = document.GetLineSegment(i); + var removeNumber = 0; + for (var x = line.Offset; x < line.Offset + line.Length && char.IsWhiteSpace(document.GetCharAt(x)); ++x) + ++removeNumber; + if (removeNumber > 0) + document.Remove(line.Offset, removeNumber); + } + } + } + + public class RemoveTrailingWS : AbstractLineFormatAction + { + protected override void Convert(IDocument document, int y1, int y2) + { + for (var i = y2 - 1; i >= y1; --i) + { + var line = document.GetLineSegment(i); + var removeNumber = 0; + for (var x = line.Offset + line.Length - 1; x >= line.Offset && char.IsWhiteSpace(document.GetCharAt(x)); --x) + ++removeNumber; + if (removeNumber > 0) + document.Remove(line.Offset + line.Length - removeNumber, removeNumber); + } + } + } + + public class ToUpperCase : AbstractSelectionFormatAction + { + protected override void Convert(IDocument document, int startOffset, int length) + { + var what = document.GetText(startOffset, length).ToUpper(); + document.Replace(startOffset, length, what); + } + } + + public class ToLowerCase : AbstractSelectionFormatAction + { + protected override void Convert(IDocument document, int startOffset, int length) + { + var what = document.GetText(startOffset, length).ToLower(); + document.Replace(startOffset, length, what); + } + } + + public class InvertCaseAction : AbstractSelectionFormatAction + { + protected override void Convert(IDocument document, int startOffset, int length) + { + var what = new StringBuilder(document.GetText(startOffset, length)); + + for (var i = 0; i < what.Length; ++i) + what[i] = char.IsUpper(what[i]) ? char.ToLower(what[i]) : char.ToUpper(what[i]); + + document.Replace(startOffset, length, what.ToString()); + } + } + + public class CapitalizeAction : AbstractSelectionFormatAction + { + protected override void Convert(IDocument document, int startOffset, int length) + { + var what = new StringBuilder(document.GetText(startOffset, length)); + + for (var i = 0; i < what.Length; ++i) + if (!char.IsLetter(what[i]) && i < what.Length - 1) + what[i + 1] = char.ToUpper(what[i + 1]); + document.Replace(startOffset, length, what.ToString()); + } + } + + public class ConvertTabsToSpaces : AbstractSelectionFormatAction + { + protected override void Convert(IDocument document, int startOffset, int length) + { + var what = document.GetText(startOffset, length); + var spaces = new string(c: ' ', document.TextEditorProperties.TabIndent); + document.Replace(startOffset, length, what.Replace("\t", spaces)); + } + } + + public class ConvertSpacesToTabs : AbstractSelectionFormatAction + { + protected override void Convert(IDocument document, int startOffset, int length) + { + var what = document.GetText(startOffset, length); + var spaces = new string(c: ' ', document.TextEditorProperties.TabIndent); + document.Replace(startOffset, length, what.Replace(spaces, "\t")); + } + } + + public class ConvertLeadingTabsToSpaces : AbstractLineFormatAction + { + protected override void Convert(IDocument document, int y1, int y2) + { + for (var i = y2; i >= y1; --i) + { + var line = document.GetLineSegment(i); + + if (line.Length > 0) + { + // count how many whitespace characters there are at the start + int whiteSpace; + for (whiteSpace = 0; whiteSpace < line.Length && char.IsWhiteSpace(document.GetCharAt(line.Offset + whiteSpace)); whiteSpace++) + { + // deliberately empty + } + + if (whiteSpace > 0) + { + var newLine = document.GetText(line.Offset, whiteSpace); + var newPrefix = newLine.Replace("\t", new string(c: ' ', document.TextEditorProperties.TabIndent)); + document.Replace(line.Offset, whiteSpace, newPrefix); + } + } + } + } + } + + public class ConvertLeadingSpacesToTabs : AbstractLineFormatAction + { + protected override void Convert(IDocument document, int y1, int y2) + { + for (var i = y2; i >= y1; --i) + { + var line = document.GetLineSegment(i); + if (line.Length > 0) + { + // note: some users may prefer a more radical ConvertLeadingSpacesToTabs that + // means there can be no spaces before the first character even if the spaces + // didn't add up to a whole number of tabs + var newLine = TextUtilities.LeadingWhiteSpaceToTabs(document.GetText(line.Offset, line.Length), document.TextEditorProperties.TabIndent); + document.Replace(line.Offset, line.Length, newLine); + } + } + } + } + + /// + /// This is a sample editaction plugin, it indents the selected area. + /// + public class IndentSelection : AbstractLineFormatAction + { + protected override void Convert(IDocument document, int startLine, int endLine) + { + document.FormattingStrategy.IndentLines(textArea, startLine, endLine); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Actions/HomeEndActions.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Actions/HomeEndActions.cs new file mode 100644 index 0000000..d6d2ee7 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Actions/HomeEndActions.cs @@ -0,0 +1,113 @@ +// +// +// +// +// $Revision$ +// + +using ICSharpCode.TextEditor.Document; + +namespace ICSharpCode.TextEditor.Actions +{ + public class Home : AbstractEditAction + { + public override void Execute(TextArea textArea) + { + var newPos = textArea.Caret.Position; + bool jumpedIntoFolding; + do + { + var curLine = textArea.Document.GetLineSegment(newPos.Y); + + if (TextUtilities.IsEmptyLine(textArea.Document, newPos.Y)) + { + if (newPos.X != 0) + newPos.X = 0; + else + newPos.X = curLine.Length; + } + else + { + var firstCharOffset = TextUtilities.GetFirstNonWSChar(textArea.Document, curLine.Offset); + var firstCharColumn = firstCharOffset - curLine.Offset; + + if (newPos.X == firstCharColumn) + newPos.X = 0; + else + newPos.X = firstCharColumn; + } + + var foldings = textArea.Document.FoldingManager.GetFoldingsFromPosition(newPos.Y, newPos.X); + jumpedIntoFolding = false; + foreach (var foldMarker in foldings) + if (foldMarker.IsFolded) + { + newPos = new TextLocation(foldMarker.StartColumn, foldMarker.StartLine); + jumpedIntoFolding = true; + break; + } + } while (jumpedIntoFolding); + + if (newPos != textArea.Caret.Position) + { + textArea.Caret.Position = newPos; + textArea.SetDesiredColumn(); + } + } + } + + public class End : AbstractEditAction + { + public override void Execute(TextArea textArea) + { + var newPos = textArea.Caret.Position; + bool jumpedIntoFolding; + do + { + var curLine = textArea.Document.GetLineSegment(newPos.Y); + newPos.X = curLine.Length; + + var foldings = textArea.Document.FoldingManager.GetFoldingsFromPosition(newPos.Y, newPos.X); + jumpedIntoFolding = false; + foreach (var foldMarker in foldings) + if (foldMarker.IsFolded) + { + newPos = new TextLocation(foldMarker.EndColumn, foldMarker.EndLine); + jumpedIntoFolding = true; + break; + } + } while (jumpedIntoFolding); + + if (newPos != textArea.Caret.Position) + { + textArea.Caret.Position = newPos; + textArea.SetDesiredColumn(); + } + } + } + + public class MoveToStart : AbstractEditAction + { + public override void Execute(TextArea textArea) + { + if (textArea.Caret.Line != 0 || textArea.Caret.Column != 0) + { + textArea.Caret.Position = new TextLocation(column: 0, line: 0); + textArea.SetDesiredColumn(); + } + } + } + + public class MoveToEnd : AbstractEditAction + { + public override void Execute(TextArea textArea) + { + var endPos = textArea.Document.OffsetToPosition(textArea.Document.TextLength); + if (textArea.Caret.Position != endPos) + { + textArea.Caret.Position = endPos; + textArea.SetDesiredColumn(); + } + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Actions/IEditAction.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Actions/IEditAction.cs new file mode 100644 index 0000000..6680f69 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Actions/IEditAction.cs @@ -0,0 +1,45 @@ +// +// +// +// +// $Revision$ +// + +using System.Windows.Forms; + +namespace ICSharpCode.TextEditor.Actions +{ + /// + /// To define a new key for the textarea, you must write a class which + /// implements this interface. + /// + public interface IEditAction + { + /// + /// An array of keys on which this edit action occurs. + /// + Keys[] Keys { get; set; } + + /// + /// When the key which is defined per XML is pressed, this method will be launched. + /// + void Execute(TextArea textArea); + } + + /// + /// To define a new key for the textarea, you must write a class which + /// implements this interface. + /// + public abstract class AbstractEditAction : IEditAction + { + /// + /// An array of keys on which this edit action occurs. + /// + public Keys[] Keys { get; set; } = null; + + /// + /// When the key which is defined per XML is pressed, this method will be launched. + /// + public abstract void Execute(TextArea textArea); + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Actions/MiscActions.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Actions/MiscActions.cs new file mode 100644 index 0000000..867fcda --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Actions/MiscActions.cs @@ -0,0 +1,915 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Diagnostics; +using System.Text; +using ICSharpCode.TextEditor.Document; + +namespace ICSharpCode.TextEditor.Actions +{ + public class Tab : AbstractEditAction + { + public static string GetIndentationString(IDocument document) + { + return GetIndentationString(document, textArea: null); + } + + public static string GetIndentationString(IDocument document, TextArea textArea) + { + var indent = new StringBuilder(); + + if (document.TextEditorProperties.ConvertTabsToSpaces) + { + var tabIndent = document.TextEditorProperties.IndentationSize; + if (textArea != null) + { + var column = textArea.TextView.GetVisualColumn(textArea.Caret.Line, textArea.Caret.Column); + indent.Append(new string(c: ' ', tabIndent - column%tabIndent)); + } + else + { + indent.Append(new string(c: ' ', tabIndent)); + } + } + else + { + indent.Append(value: '\t'); + } + + return indent.ToString(); + } + + private static void InsertTabs(IDocument document, ISelection selection, int y1, int y2) + { + var indentationString = GetIndentationString(document); + for (var i = y2; i >= y1; --i) + { + var line = document.GetLineSegment(i); + if (i == y2 && i == selection.EndPosition.Y && selection.EndPosition.X == 0) + continue; + + // this bit is optional - but useful if you are using block tabbing to sort out + // a source file with a mixture of tabs and spaces +// string newLine = document.GetText(line.Offset,line.Length); +// document.Replace(line.Offset,line.Length,newLine); + + document.Insert(line.Offset, indentationString); + } + } + + private static void InsertTabAtCaretPosition(TextArea textArea) + { + switch (textArea.Caret.CaretMode) + { + case CaretMode.InsertMode: + textArea.InsertString(GetIndentationString(textArea.Document, textArea)); + break; + case CaretMode.OverwriteMode: + var indentStr = GetIndentationString(textArea.Document, textArea); + textArea.ReplaceChar(indentStr[index: 0]); + if (indentStr.Length > 1) + textArea.InsertString(indentStr.Substring(startIndex: 1)); + break; + } + + textArea.SetDesiredColumn(); + } + + /// + /// Executes this edit action + /// + /// The which is used for callback purposes + public override void Execute(TextArea textArea) + { + if (textArea.SelectionManager.SelectionIsReadonly) + return; + textArea.Document.UndoStack.StartUndoGroup(); + if (textArea.SelectionManager.HasSomethingSelected) + { + foreach (var selection in textArea.SelectionManager.SelectionCollection) + { + var startLine = selection.StartPosition.Y; + var endLine = selection.EndPosition.Y; + if (startLine != endLine) + { + textArea.BeginUpdate(); + InsertTabs(textArea.Document, selection, startLine, endLine); + textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.LinesBetween, startLine, endLine)); + textArea.EndUpdate(); + } + else + { + InsertTabAtCaretPosition(textArea); + break; + } + } + + textArea.Document.CommitUpdate(); + textArea.AutoClearSelection = false; + } + else + { + InsertTabAtCaretPosition(textArea); + } + + textArea.Document.UndoStack.EndUndoGroup(); + } + } + + public class ShiftTab : AbstractEditAction + { + private static void RemoveTabs(IDocument document, ISelection selection, int y1, int y2) + { + document.UndoStack.StartUndoGroup(); + for (var i = y2; i >= y1; --i) + { + var line = document.GetLineSegment(i); + if (i == y2 && line.Offset == selection.EndOffset) + continue; + if (line.Length > 0) + { + var charactersToRemove = 0; + if (document.GetCharAt(line.Offset) == '\t') + { + // first character is a tab - just remove it + charactersToRemove = 1; + } + else if (document.GetCharAt(line.Offset) == ' ') + { + int leadingSpaces; + var tabIndent = document.TextEditorProperties.IndentationSize; + for (leadingSpaces = 1; + leadingSpaces < line.Length && document.GetCharAt(line.Offset + leadingSpaces) == ' '; + leadingSpaces++) + { + // deliberately empty + } + + if (leadingSpaces >= tabIndent) + charactersToRemove = tabIndent; + else if (line.Length > leadingSpaces && document.GetCharAt(line.Offset + leadingSpaces) == '\t') + charactersToRemove = leadingSpaces + 1; + else + charactersToRemove = leadingSpaces; + } + + if (charactersToRemove > 0) + document.Remove(line.Offset, charactersToRemove); + } + } + + document.UndoStack.EndUndoGroup(); + } + + /// + /// Executes this edit action + /// + /// The which is used for callback purposes + public override void Execute(TextArea textArea) + { + if (textArea.SelectionManager.HasSomethingSelected) + { + foreach (var selection in textArea.SelectionManager.SelectionCollection) + { + var startLine = selection.StartPosition.Y; + var endLine = selection.EndPosition.Y; + textArea.BeginUpdate(); + RemoveTabs(textArea.Document, selection, startLine, endLine); + textArea.Document.UpdateQueue.Clear(); + textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.LinesBetween, startLine, endLine)); + textArea.EndUpdate(); + } + + textArea.AutoClearSelection = false; + } + else + { + // Pressing Shift-Tab with nothing selected the cursor will move back to the + // previous tab stop. It will stop at the beginning of the line. Also, the desired + // column is updated to that column. + var line = textArea.Document.GetLineSegmentForOffset(textArea.Caret.Offset); + //var startOfLine = textArea.Document.GetText(line.Offset, textArea.Caret.Offset - line.Offset); + var tabIndent = textArea.Document.TextEditorProperties.IndentationSize; + var currentColumn = textArea.Caret.Column; + var remainder = currentColumn%tabIndent; + if (remainder == 0) + textArea.Caret.DesiredColumn = Math.Max(val1: 0, currentColumn - tabIndent); + else + textArea.Caret.DesiredColumn = Math.Max(val1: 0, currentColumn - remainder); + textArea.SetCaretToDesiredColumn(); + } + } + } + + public class ToggleComment : AbstractEditAction + { + /// + /// Executes this edit action + /// + /// The which is used for callback purposes + public override void Execute(TextArea textArea) + { + if (textArea.Document.ReadOnly) + return; + + if (textArea.Document.HighlightingStrategy.Properties.ContainsKey("LineComment")) + new ToggleLineComment().Execute(textArea); + else if (textArea.Document.HighlightingStrategy.Properties.ContainsKey("BlockCommentBegin")) + new ToggleBlockComment().Execute(textArea); + } + } + + public class ToggleLineComment : AbstractEditAction + { + private int firstLine; + private int lastLine; + + private void RemoveCommentAt(IDocument document, string comment, ISelection selection, int y1, int y2) + { + firstLine = y1; + lastLine = y2; + + for (var i = y2; i >= y1; --i) + { + var line = document.GetLineSegment(i); + if (selection != null && i == y2 && line.Offset == selection.Offset + selection.Length) + { + --lastLine; + continue; + } + + var lineText = document.GetText(line.Offset, line.Length); + if (lineText.Trim().StartsWith(comment)) + document.Remove(line.Offset + lineText.IndexOf(comment), comment.Length); + } + } + + private void SetCommentAt(IDocument document, string comment, ISelection selection, int y1, int y2) + { + firstLine = y1; + lastLine = y2; + + for (var i = y2; i >= y1; --i) + { + var line = document.GetLineSegment(i); + if (selection != null && i == y2 && line.Offset == selection.Offset + selection.Length) + { + --lastLine; + continue; + } + +// var lineText = document.GetText(line.Offset, line.Length); + document.Insert(line.Offset, comment); + } + } + + private bool ShouldComment(IDocument document, string comment, ISelection selection, int startLine, int endLine) + { + for (var i = endLine; i >= startLine; --i) + { + var line = document.GetLineSegment(i); + if (selection != null && i == endLine && line.Offset == selection.Offset + selection.Length) + { + --lastLine; + continue; + } + + var lineText = document.GetText(line.Offset, line.Length); + if (!lineText.Trim().StartsWith(comment)) + return true; + } + + return false; + } + + /// + /// Executes this edit action + /// + /// The which is used for callback purposes + public override void Execute(TextArea textArea) + { + if (textArea.Document.ReadOnly) + return; + + string comment = null; + if (textArea.Document.HighlightingStrategy.Properties.ContainsKey("LineComment")) + comment = textArea.Document.HighlightingStrategy.Properties["LineComment"]; + + if (comment == null || comment.Length == 0) + return; + + textArea.Document.UndoStack.StartUndoGroup(); + if (textArea.SelectionManager.HasSomethingSelected) + { + var shouldComment = true; + foreach (var selection in textArea.SelectionManager.SelectionCollection) + if (!ShouldComment(textArea.Document, comment, selection, selection.StartPosition.Y, selection.EndPosition.Y)) + { + shouldComment = false; + break; + } + + foreach (var selection in textArea.SelectionManager.SelectionCollection) + { + textArea.BeginUpdate(); + if (shouldComment) + SetCommentAt(textArea.Document, comment, selection, selection.StartPosition.Y, selection.EndPosition.Y); + else + RemoveCommentAt(textArea.Document, comment, selection, selection.StartPosition.Y, selection.EndPosition.Y); + textArea.Document.UpdateQueue.Clear(); + textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.LinesBetween, firstLine, lastLine)); + textArea.EndUpdate(); + } + + textArea.Document.CommitUpdate(); + textArea.AutoClearSelection = false; + } + else + { + textArea.BeginUpdate(); + var caretLine = textArea.Caret.Line; + if (ShouldComment(textArea.Document, comment, selection: null, caretLine, caretLine)) + SetCommentAt(textArea.Document, comment, selection: null, caretLine, caretLine); + else + RemoveCommentAt(textArea.Document, comment, selection: null, caretLine, caretLine); + textArea.Document.UpdateQueue.Clear(); + textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.SingleLine, caretLine)); + textArea.EndUpdate(); + } + + textArea.Document.UndoStack.EndUndoGroup(); + } + } + + public class ToggleBlockComment : AbstractEditAction + { + /// + /// Executes this edit action + /// + /// The which is used for callback purposes + public override void Execute(TextArea textArea) + { + if (textArea.Document.ReadOnly) + return; + + string commentStart = null; + if (textArea.Document.HighlightingStrategy.Properties.ContainsKey("BlockCommentBegin")) + commentStart = textArea.Document.HighlightingStrategy.Properties["BlockCommentBegin"]; + + string commentEnd = null; + if (textArea.Document.HighlightingStrategy.Properties.ContainsKey("BlockCommentEnd")) + commentEnd = textArea.Document.HighlightingStrategy.Properties["BlockCommentEnd"]; + + if (commentStart == null || commentStart.Length == 0 || commentEnd == null || commentEnd.Length == 0) + return; + + int selectionStartOffset; + int selectionEndOffset; + + if (textArea.SelectionManager.HasSomethingSelected) + { + selectionStartOffset = textArea.SelectionManager.SelectionCollection[index: 0].Offset; + selectionEndOffset = textArea.SelectionManager.SelectionCollection[textArea.SelectionManager.SelectionCollection.Count - 1].EndOffset; + } + else + { + selectionStartOffset = textArea.Caret.Offset; + selectionEndOffset = selectionStartOffset; + } + + var commentRegion = FindSelectedCommentRegion(textArea.Document, commentStart, commentEnd, selectionStartOffset, selectionEndOffset); + + textArea.Document.UndoStack.StartUndoGroup(); + if (commentRegion != null) + RemoveComment(textArea.Document, commentRegion); + else if (textArea.SelectionManager.HasSomethingSelected) + SetCommentAt(textArea.Document, selectionStartOffset, selectionEndOffset, commentStart, commentEnd); + textArea.Document.UndoStack.EndUndoGroup(); + + textArea.Document.CommitUpdate(); + textArea.AutoClearSelection = false; + } + + public static BlockCommentRegion FindSelectedCommentRegion(IDocument document, string commentStart, string commentEnd, int selectionStartOffset, int selectionEndOffset) + { + if (document.TextLength == 0) + return null; + + // Find start of comment in selected text. + + int commentEndOffset; + var selectedText = document.GetText(selectionStartOffset, selectionEndOffset - selectionStartOffset); + + var commentStartOffset = selectedText.IndexOf(commentStart); + if (commentStartOffset >= 0) + commentStartOffset += selectionStartOffset; + + // Find end of comment in selected text. + + if (commentStartOffset >= 0) + commentEndOffset = selectedText.IndexOf(commentEnd, commentStartOffset + commentStart.Length - selectionStartOffset); + else + commentEndOffset = selectedText.IndexOf(commentEnd); + + if (commentEndOffset >= 0) + commentEndOffset += selectionStartOffset; + + // Find start of comment before or partially inside the + // selected text. + + if (commentStartOffset == -1) + { + var offset = selectionEndOffset + commentStart.Length - 1; + if (offset > document.TextLength) + offset = document.TextLength; + var text = document.GetText(offset: 0, offset); + commentStartOffset = text.LastIndexOf(commentStart); + if (commentStartOffset >= 0) + { + // Find end of comment before comment start. + var commentEndBeforeStartOffset = text.IndexOf(commentEnd, commentStartOffset, selectionStartOffset - commentStartOffset); + if (commentEndBeforeStartOffset > commentStartOffset) + commentStartOffset = -1; + } + } + + // Find end of comment after or partially after the + // selected text. + + if (commentEndOffset == -1) + { + var offset = selectionStartOffset + 1 - commentEnd.Length; + if (offset < 0) + offset = selectionStartOffset; + var text = document.GetText(offset, document.TextLength - offset); + commentEndOffset = text.IndexOf(commentEnd); + if (commentEndOffset >= 0) + commentEndOffset += offset; + } + + if (commentStartOffset != -1 && commentEndOffset != -1) + return new BlockCommentRegion(commentStart, commentEnd, commentStartOffset, commentEndOffset); + + return null; + } + + private static void SetCommentAt(IDocument document, int offsetStart, int offsetEnd, string commentStart, string commentEnd) + { + document.Insert(offsetEnd, commentEnd); + document.Insert(offsetStart, commentStart); + } + + private static void RemoveComment(IDocument document, BlockCommentRegion commentRegion) + { + document.Remove(commentRegion.EndOffset, commentRegion.CommentEnd.Length); + document.Remove(commentRegion.StartOffset, commentRegion.CommentStart.Length); + } + } + + public class BlockCommentRegion + { + /// + /// The end offset is the offset where the comment end string starts from. + /// + public BlockCommentRegion(string commentStart, string commentEnd, int startOffset, int endOffset) + { + CommentStart = commentStart; + CommentEnd = commentEnd; + StartOffset = startOffset; + EndOffset = endOffset; + } + + public string CommentStart { get; } = string.Empty; + + public string CommentEnd { get; } = string.Empty; + + public int StartOffset { get; } = -1; + + public int EndOffset { get; } = -1; + + public override int GetHashCode() + { + var hashCode = 0; + unchecked + { + if (CommentStart != null) hashCode += 1000000007*CommentStart.GetHashCode(); + if (CommentEnd != null) hashCode += 1000000009*CommentEnd.GetHashCode(); + hashCode += 1000000021*StartOffset.GetHashCode(); + hashCode += 1000000033*EndOffset.GetHashCode(); + } + + return hashCode; + } + + public override bool Equals(object obj) + { + var other = obj as BlockCommentRegion; + if (other == null) return false; + return CommentStart == other.CommentStart && CommentEnd == other.CommentEnd && StartOffset == other.StartOffset && EndOffset == other.EndOffset; + } + } + + public class Backspace : AbstractEditAction + { + /// + /// Executes this edit action + /// + /// The which is used for callback purposes + public override void Execute(TextArea textArea) + { + if (textArea.SelectionManager.HasSomethingSelected) + { + Delete.DeleteSelection(textArea); + } + else + { + if (textArea.Caret.Offset > 0 && !textArea.IsReadOnly(textArea.Caret.Offset - 1)) + { + textArea.BeginUpdate(); + var curLineNr = textArea.Document.GetLineNumberForOffset(textArea.Caret.Offset); + var curLineOffset = textArea.Document.GetLineSegment(curLineNr).Offset; + + if (curLineOffset == textArea.Caret.Offset) + { + var line = textArea.Document.GetLineSegment(curLineNr - 1); +// var lastLine = curLineNr == textArea.Document.TotalNumberOfLines; + var lineEndOffset = line.Offset + line.Length; + var lineLength = line.Length; + textArea.Document.Remove(lineEndOffset, curLineOffset - lineEndOffset); + textArea.Caret.Position = new TextLocation(lineLength, curLineNr - 1); + textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.PositionToEnd, new TextLocation(column: 0, curLineNr - 1))); + } + else + { + var caretOffset = textArea.Caret.Offset - 1; + textArea.Caret.Position = textArea.Document.OffsetToPosition(caretOffset); + textArea.Document.Remove(caretOffset, length: 1); + + textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.PositionToLineEnd, new TextLocation(textArea.Caret.Offset - textArea.Document.GetLineSegment(curLineNr).Offset, curLineNr))); + } + + textArea.EndUpdate(); + } + } + } + } + + public class Delete : AbstractEditAction + { + internal static void DeleteSelection(TextArea textArea) + { + Debug.Assert(textArea.SelectionManager.HasSomethingSelected); + if (textArea.SelectionManager.SelectionIsReadonly) + return; + textArea.BeginUpdate(); + textArea.Caret.Position = textArea.SelectionManager.SelectionCollection[index: 0].StartPosition; + textArea.SelectionManager.RemoveSelectedText(); + textArea.ScrollToCaret(); + textArea.EndUpdate(); + } + + /// + /// Executes this edit action + /// + /// The which is used for callback purposes + public override void Execute(TextArea textArea) + { + if (textArea.SelectionManager.HasSomethingSelected) + { + DeleteSelection(textArea); + } + else + { + if (textArea.IsReadOnly(textArea.Caret.Offset)) + return; + + if (textArea.Caret.Offset < textArea.Document.TextLength) + { + textArea.BeginUpdate(); + var curLineNr = textArea.Document.GetLineNumberForOffset(textArea.Caret.Offset); + var curLine = textArea.Document.GetLineSegment(curLineNr); + + if (curLine.Offset + curLine.Length == textArea.Caret.Offset) + { + if (curLineNr + 1 < textArea.Document.TotalNumberOfLines) + { + var nextLine = textArea.Document.GetLineSegment(curLineNr + 1); + + textArea.Document.Remove(textArea.Caret.Offset, nextLine.Offset - textArea.Caret.Offset); + textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.PositionToEnd, new TextLocation(column: 0, curLineNr))); + } + } + else + { + textArea.Document.Remove(textArea.Caret.Offset, length: 1); +// textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.PositionToLineEnd, new TextLocation(textArea.Caret.Offset - textArea.Document.GetLineSegment(curLineNr).Offset, curLineNr))); + } + + textArea.UpdateMatchingBracket(); + textArea.EndUpdate(); + } + } + } + } + + public class MovePageDown : AbstractEditAction + { + /// + /// Executes this edit action + /// + /// The which is used for callback purposes + public override void Execute(TextArea textArea) + { + var curLineNr = textArea.Caret.Line; + var requestedLineNumber = Math.Min(textArea.Document.GetNextVisibleLineAbove(curLineNr, textArea.TextView.VisibleLineCount), textArea.Document.TotalNumberOfLines - 1); + + if (curLineNr != requestedLineNumber) + { + textArea.Caret.Position = new TextLocation(column: 0, requestedLineNumber); + textArea.SetCaretToDesiredColumn(); + } + } + } + + public class MovePageUp : AbstractEditAction + { + /// + /// Executes this edit action + /// + /// The which is used for callback purposes + public override void Execute(TextArea textArea) + { + var curLineNr = textArea.Caret.Line; + var requestedLineNumber = Math.Max(textArea.Document.GetNextVisibleLineBelow(curLineNr, textArea.TextView.VisibleLineCount), val2: 0); + + if (curLineNr != requestedLineNumber) + { + textArea.Caret.Position = new TextLocation(column: 0, requestedLineNumber); + textArea.SetCaretToDesiredColumn(); + } + } + } + + public class Return : AbstractEditAction + { + /// + /// Executes this edit action + /// + /// The which is used for callback purposes + public override void Execute(TextArea textArea) + { + if (textArea.Document.ReadOnly) + return; + textArea.BeginUpdate(); + textArea.Document.UndoStack.StartUndoGroup(); + try + { + if (textArea.HandleKeyPress(ch: '\n')) + return; + + textArea.InsertString(Environment.NewLine); + + var curLineNr = textArea.Caret.Line; + textArea.Document.FormattingStrategy.FormatLine(textArea, curLineNr, textArea.Caret.Offset, charTyped: '\n'); + textArea.SetDesiredColumn(); + + textArea.Document.UpdateQueue.Clear(); + textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.PositionToEnd, new TextLocation(column: 0, curLineNr - 1))); + } + finally + { + textArea.Document.UndoStack.EndUndoGroup(); + textArea.EndUpdate(); + } + } + } + + public class ToggleEditMode : AbstractEditAction + { + /// + /// Executes this edit action + /// + /// The which is used for callback purposes + public override void Execute(TextArea textArea) + { + if (textArea.Document.ReadOnly) + return; + switch (textArea.Caret.CaretMode) + { + case CaretMode.InsertMode: + textArea.Caret.CaretMode = CaretMode.OverwriteMode; + break; + case CaretMode.OverwriteMode: + textArea.Caret.CaretMode = CaretMode.InsertMode; + break; + } + } + } + + public class Undo : AbstractEditAction + { + /// + /// Executes this edit action + /// + /// The which is used for callback purposes + public override void Execute(TextArea textArea) + { + textArea.MotherTextEditorControl.Undo(); + } + } + + public class Redo : AbstractEditAction + { + /// + /// Executes this edit action + /// + /// The which is used for callback purposes + public override void Execute(TextArea textArea) + { + textArea.MotherTextEditorControl.Redo(); + } + } + + /// + /// handles the ctrl-backspace key + /// functionality attempts to roughly mimic MS Developer studio + /// I will implement this as deleting back to the point that ctrl-leftarrow would + /// take you to + /// + public class WordBackspace : AbstractEditAction + { + /// + /// Executes this edit action + /// + /// The which is used for callback purposes + public override void Execute(TextArea textArea) + { + // if anything is selected we will just delete it first + if (textArea.SelectionManager.HasSomethingSelected) + { + Delete.DeleteSelection(textArea); + return; + } + + textArea.BeginUpdate(); + // now delete from the caret to the beginning of the word + var line = + textArea.Document.GetLineSegmentForOffset(textArea.Caret.Offset); + // if we are not at the beginning of a line + if (textArea.Caret.Offset > line.Offset) + { + var prevWordStart = TextUtilities.FindPrevWordStart( + textArea.Document, + textArea.Caret.Offset); + if (prevWordStart < textArea.Caret.Offset) + if (!textArea.IsReadOnly(prevWordStart, textArea.Caret.Offset - prevWordStart)) + { + textArea.Document.Remove( + prevWordStart, + textArea.Caret.Offset - prevWordStart); + textArea.Caret.Position = textArea.Document.OffsetToPosition(prevWordStart); + } + } + + // if we are now at the beginning of a line + if (textArea.Caret.Offset == line.Offset) + { + // if we are not on the first line + var curLineNr = textArea.Document.GetLineNumberForOffset(textArea.Caret.Offset); + if (curLineNr > 0) + { + // move to the end of the line above + var lineAbove = textArea.Document.GetLineSegment(curLineNr - 1); + var endOfLineAbove = lineAbove.Offset + lineAbove.Length; + var charsToDelete = textArea.Caret.Offset - endOfLineAbove; + if (!textArea.IsReadOnly(endOfLineAbove, charsToDelete)) + { + textArea.Document.Remove(endOfLineAbove, charsToDelete); + textArea.Caret.Position = textArea.Document.OffsetToPosition(endOfLineAbove); + } + } + } + + textArea.SetDesiredColumn(); + textArea.EndUpdate(); + // if there are now less lines, we need this or there are redraw problems + textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.PositionToEnd, new TextLocation(column: 0, textArea.Document.GetLineNumberForOffset(textArea.Caret.Offset)))); + textArea.Document.CommitUpdate(); + } + } + + /// + /// handles the ctrl-delete key + /// functionality attempts to mimic MS Developer studio + /// I will implement this as deleting forwardto the point that + /// ctrl-leftarrow would take you to + /// + public class DeleteWord : Delete + { + /// + /// Executes this edit action + /// + /// The which is used for callback purposes + public override void Execute(TextArea textArea) + { + if (textArea.SelectionManager.HasSomethingSelected) + { + DeleteSelection(textArea); + return; + } + + // if anything is selected we will just delete it first + textArea.BeginUpdate(); + // now delete from the caret to the beginning of the word + var line = textArea.Document.GetLineSegmentForOffset(textArea.Caret.Offset); + if (textArea.Caret.Offset == line.Offset + line.Length) + { + // if we are at the end of a line + base.Execute(textArea); + } + else + { + var nextWordStart = TextUtilities.FindNextWordStart( + textArea.Document, + textArea.Caret.Offset); + if (nextWordStart > textArea.Caret.Offset) + if (!textArea.IsReadOnly(textArea.Caret.Offset, nextWordStart - textArea.Caret.Offset)) + textArea.Document.Remove(textArea.Caret.Offset, nextWordStart - textArea.Caret.Offset); + } + + textArea.UpdateMatchingBracket(); + textArea.EndUpdate(); + // if there are now less lines, we need this or there are redraw problems + textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.PositionToEnd, new TextLocation(column: 0, textArea.Document.GetLineNumberForOffset(textArea.Caret.Offset)))); + textArea.Document.CommitUpdate(); + } + } + + public class DeleteLine : AbstractEditAction + { + public override void Execute(TextArea textArea) + { + var lineNr = textArea.Caret.Line; + var line = textArea.Document.GetLineSegment(lineNr); + if (textArea.IsReadOnly(line.Offset, line.Length)) + return; + textArea.Document.Remove(line.Offset, line.TotalLength); + textArea.Caret.Position = textArea.Document.OffsetToPosition(line.Offset); + + textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.PositionToEnd, new TextLocation(column: 0, lineNr))); + textArea.UpdateMatchingBracket(); + textArea.Document.CommitUpdate(); + } + } + + public class DeleteToLineEnd : AbstractEditAction + { + public override void Execute(TextArea textArea) + { + var lineNr = textArea.Caret.Line; + var line = textArea.Document.GetLineSegment(lineNr); + + var numRemove = line.Offset + line.Length - textArea.Caret.Offset; + if (numRemove > 0 && !textArea.IsReadOnly(textArea.Caret.Offset, numRemove)) + { + textArea.Document.Remove(textArea.Caret.Offset, numRemove); + textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.SingleLine, new TextLocation(column: 0, lineNr))); + textArea.Document.CommitUpdate(); + } + } + } + + public class GotoMatchingBrace : AbstractEditAction + { + public override void Execute(TextArea textArea) + { + var highlight = textArea.FindMatchingBracketHighlight(); + if (highlight != null) + { + var p1 = new TextLocation(highlight.CloseBrace.X + 1, highlight.CloseBrace.Y); + var p2 = new TextLocation(highlight.OpenBrace.X + 1, highlight.OpenBrace.Y); + if (p1 == textArea.Caret.Position) + { + if (textArea.Document.TextEditorProperties.BracketMatchingStyle == BracketMatchingStyle.After) + textArea.Caret.Position = p2; + else + textArea.Caret.Position = new TextLocation(p2.X - 1, p2.Y); + } + else + { + if (textArea.Document.TextEditorProperties.BracketMatchingStyle == BracketMatchingStyle.After) + textArea.Caret.Position = p1; + else + textArea.Caret.Position = new TextLocation(p1.X - 1, p1.Y); + } + + textArea.SetDesiredColumn(); + } + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Actions/SelectionActions.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Actions/SelectionActions.cs new file mode 100644 index 0000000..4274e8e --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Actions/SelectionActions.cs @@ -0,0 +1,170 @@ +// +// +// +// +// $Revision$ +// + +namespace ICSharpCode.TextEditor.Actions +{ + public class ShiftCaretRight : CaretRight + { + public override void Execute(TextArea textArea) + { + var oldCaretPos = textArea.Caret.Position; + base.Execute(textArea); + textArea.AutoClearSelection = false; + textArea.SelectionManager.ExtendSelection(oldCaretPos, textArea.Caret.Position); + } + } + + public class ShiftCaretLeft : CaretLeft + { + public override void Execute(TextArea textArea) + { + var oldCaretPos = textArea.Caret.Position; + base.Execute(textArea); + textArea.AutoClearSelection = false; + textArea.SelectionManager.ExtendSelection(oldCaretPos, textArea.Caret.Position); + } + } + + public class ShiftCaretUp : CaretUp + { + public override void Execute(TextArea textArea) + { + var oldCaretPos = textArea.Caret.Position; + base.Execute(textArea); + textArea.AutoClearSelection = false; + textArea.SelectionManager.ExtendSelection(oldCaretPos, textArea.Caret.Position); + } + } + + public class ShiftCaretDown : CaretDown + { + public override void Execute(TextArea textArea) + { + var oldCaretPos = textArea.Caret.Position; + base.Execute(textArea); + textArea.AutoClearSelection = false; + textArea.SelectionManager.ExtendSelection(oldCaretPos, textArea.Caret.Position); + } + } + + public class ShiftWordRight : WordRight + { + public override void Execute(TextArea textArea) + { + var oldCaretPos = textArea.Caret.Position; + base.Execute(textArea); + textArea.AutoClearSelection = false; + textArea.SelectionManager.ExtendSelection(oldCaretPos, textArea.Caret.Position); + } + } + + public class ShiftWordLeft : WordLeft + { + public override void Execute(TextArea textArea) + { + var oldCaretPos = textArea.Caret.Position; + base.Execute(textArea); + textArea.AutoClearSelection = false; + textArea.SelectionManager.ExtendSelection(oldCaretPos, textArea.Caret.Position); + } + } + + public class ShiftHome : Home + { + public override void Execute(TextArea textArea) + { + var oldCaretPos = textArea.Caret.Position; + base.Execute(textArea); + textArea.AutoClearSelection = false; + textArea.SelectionManager.ExtendSelection(oldCaretPos, textArea.Caret.Position); + } + } + + public class ShiftEnd : End + { + public override void Execute(TextArea textArea) + { + var oldCaretPos = textArea.Caret.Position; + base.Execute(textArea); + textArea.AutoClearSelection = false; + textArea.SelectionManager.ExtendSelection(oldCaretPos, textArea.Caret.Position); + } + } + + public class ShiftMoveToStart : MoveToStart + { + public override void Execute(TextArea textArea) + { + var oldCaretPos = textArea.Caret.Position; + base.Execute(textArea); + textArea.AutoClearSelection = false; + textArea.SelectionManager.ExtendSelection(oldCaretPos, textArea.Caret.Position); + } + } + + public class ShiftMoveToEnd : MoveToEnd + { + public override void Execute(TextArea textArea) + { + var oldCaretPos = textArea.Caret.Position; + base.Execute(textArea); + textArea.AutoClearSelection = false; + textArea.SelectionManager.ExtendSelection(oldCaretPos, textArea.Caret.Position); + } + } + + public class ShiftMovePageUp : MovePageUp + { + public override void Execute(TextArea textArea) + { + var oldCaretPos = textArea.Caret.Position; + base.Execute(textArea); + textArea.AutoClearSelection = false; + textArea.SelectionManager.ExtendSelection(oldCaretPos, textArea.Caret.Position); + } + } + + public class ShiftMovePageDown : MovePageDown + { + public override void Execute(TextArea textArea) + { + var oldCaretPos = textArea.Caret.Position; + base.Execute(textArea); + textArea.AutoClearSelection = false; + textArea.SelectionManager.ExtendSelection(oldCaretPos, textArea.Caret.Position); + } + } + + public class SelectWholeDocument : AbstractEditAction + { + public override void Execute(TextArea textArea) + { + textArea.AutoClearSelection = false; + var startPoint = new TextLocation(column: 0, line: 0); + var endPoint = textArea.Document.OffsetToPosition(textArea.Document.TextLength); + if (textArea.SelectionManager.HasSomethingSelected) + if (textArea.SelectionManager.SelectionCollection[index: 0].StartPosition == startPoint && + textArea.SelectionManager.SelectionCollection[index: 0].EndPosition == endPoint) + return; + textArea.Caret.Position = textArea.SelectionManager.NextValidPosition(endPoint.Y); + textArea.SelectionManager.ExtendSelection(startPoint, endPoint); + // after a SelectWholeDocument selection, the caret is placed correctly, + // but it is not positioned internally. The effect is when the cursor + // is moved up or down a line, the caret will take on the column that + // it was in before the SelectWholeDocument + textArea.SetDesiredColumn(); + } + } + + public class ClearAllSelections : AbstractEditAction + { + public override void Execute(TextArea textArea) + { + textArea.SelectionManager.ClearSelection(); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/AbstractSegment.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/AbstractSegment.cs new file mode 100644 index 0000000..ec8a047 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/AbstractSegment.cs @@ -0,0 +1,45 @@ +// +// +// +// +// $Revision$ +// + +using System; + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// This interface is used to describe a span inside a text sequence + /// + public class AbstractSegment : ISegment + { + [CLSCompliant(isCompliant: false)] protected int length = -1; + + [CLSCompliant(isCompliant: false)] protected int offset = -1; + + public override string ToString() + { + return string.Format( + "[AbstractSegment: Offset = {0}, Length = {1}]", + Offset, + Length); + } + + #region ICSharpCode.TextEditor.Document.ISegment interface implementation + + public virtual int Offset + { + get => offset; + set => offset = value; + } + + public virtual int Length + { + get => length; + set => length = value; + } + + #endregion + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/BookmarkManager/Bookmark.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/BookmarkManager/Bookmark.cs new file mode 100644 index 0000000..aadedc4 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/BookmarkManager/Bookmark.cs @@ -0,0 +1,166 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Drawing; +using SWF = System.Windows.Forms; + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// Description of Bookmark. + /// + public class Bookmark + { + private IDocument document; + private bool isEnabled = true; + private TextLocation location; + + public Bookmark(IDocument document, TextLocation location) : this(document, location, isEnabled: true) + { + } + + public Bookmark(IDocument document, TextLocation location, bool isEnabled) + { + this.document = document; + this.isEnabled = isEnabled; + Location = location; + } + + public IDocument Document + { + get => document; + set + { + if (document != value) + { + if (Anchor != null) + { + location = Anchor.Location; + Anchor = null; + } + + document = value; + CreateAnchor(); + OnDocumentChanged(EventArgs.Empty); + } + } + } + + /// + /// Gets the TextAnchor used for this bookmark. + /// Is null if the bookmark is not connected to a document. + /// + public TextAnchor Anchor { get; private set; } + + public TextLocation Location + { + get + { + if (Anchor != null) + return Anchor.Location; + return location; + } + set + { + location = value; + CreateAnchor(); + } + } + + public bool IsEnabled + { + get => isEnabled; + set + { + if (isEnabled != value) + { + isEnabled = value; + if (document != null) + { + document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.SingleLine, LineNumber)); + document.CommitUpdate(); + } + + OnIsEnabledChanged(EventArgs.Empty); + } + } + } + + public int LineNumber + { + get + { + if (Anchor != null) + return Anchor.LineNumber; + return location.Line; + } + } + + public int ColumnNumber + { + get + { + if (Anchor != null) + return Anchor.ColumnNumber; + return location.Column; + } + } + + /// + /// Gets if the bookmark can be toggled off using the 'set/unset bookmark' command. + /// + public virtual bool CanToggle => true; + + private void CreateAnchor() + { + if (document != null) + { + var line = document.GetLineSegment(Math.Max(val1: 0, Math.Min(location.Line, document.TotalNumberOfLines - 1))); + Anchor = line.CreateAnchor(Math.Max(val1: 0, Math.Min(location.Column, line.Length))); + // after insertion: keep bookmarks after the initial whitespace (see DefaultFormattingStrategy.SmartReplaceLine) + Anchor.MovementType = AnchorMovementType.AfterInsertion; + Anchor.Deleted += AnchorDeleted; + } + } + + private void AnchorDeleted(object sender, EventArgs e) + { + document.BookmarkManager.RemoveMark(this); + } + + public event EventHandler DocumentChanged; + + protected virtual void OnDocumentChanged(EventArgs e) + { + DocumentChanged?.Invoke(this, e); + } + + public event EventHandler IsEnabledChanged; + + protected virtual void OnIsEnabledChanged(EventArgs e) + { + IsEnabledChanged?.Invoke(this, e); + } + + public virtual bool Click(SWF.Control parent, SWF.MouseEventArgs e) + { + if (e.Button == SWF.MouseButtons.Left && CanToggle) + { + document.BookmarkManager.RemoveMark(this); + return true; + } + + return false; + } + + public virtual void Draw(IconBarMargin margin, Graphics g, Point p) + { + margin.DrawBookmark(g, p.Y, isEnabled); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/BookmarkManager/BookmarkEventHandler.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/BookmarkManager/BookmarkEventHandler.cs new file mode 100644 index 0000000..bd27595 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/BookmarkManager/BookmarkEventHandler.cs @@ -0,0 +1,26 @@ +// +// +// +// +// $Revision$ +// + +using System; + +namespace ICSharpCode.TextEditor.Document +{ + public delegate void BookmarkEventHandler(object sender, BookmarkEventArgs e); + + /// + /// Description of BookmarkEventHandler. + /// + public class BookmarkEventArgs : EventArgs + { + public BookmarkEventArgs(Bookmark bookmark) + { + Bookmark = bookmark; + } + + public Bookmark Bookmark { get; } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/BookmarkManager/BookmarkManager.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/BookmarkManager/BookmarkManager.cs new file mode 100644 index 0000000..927bcb4 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/BookmarkManager/BookmarkManager.cs @@ -0,0 +1,222 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using ICSharpCode.TextEditor.Util; + +namespace ICSharpCode.TextEditor.Document +{ + public interface IBookmarkFactory + { + Bookmark CreateBookmark(IDocument document, TextLocation location); + } + + /// + /// This class handles the bookmarks for a buffer + /// + public class BookmarkManager + { +#if DEBUG + private readonly IList bookmark = new CheckedList(); +#else + List bookmark = new List(); + #endif + + /// + /// Contains all bookmarks + /// + public ReadOnlyCollection Marks => new ReadOnlyCollection(bookmark); + + public IDocument Document { get; } + + /// + /// Creates a new instance of + /// + internal BookmarkManager(IDocument document) + { + Document = document; + } + + /// + /// Gets/Sets the bookmark factory used to create bookmarks for "ToggleMarkAt". + /// + public IBookmarkFactory Factory { get; set; } + + /// + /// Sets the mark at the line location.Line if it is not set, if the + /// line is already marked the mark is cleared. + /// + public void ToggleMarkAt(TextLocation location) + { + Bookmark newMark; + if (Factory != null) + newMark = Factory.CreateBookmark(Document, location); + else + newMark = new Bookmark(Document, location); + + var newMarkType = newMark.GetType(); + + for (var i = 0; i < bookmark.Count; ++i) + { + var mark = bookmark[i]; + + if (mark.LineNumber == location.Line && mark.CanToggle && mark.GetType() == newMarkType) + { + bookmark.RemoveAt(i); + OnRemoved(new BookmarkEventArgs(mark)); + return; + } + } + + bookmark.Add(newMark); + OnAdded(new BookmarkEventArgs(newMark)); + } + + public void AddMark(Bookmark mark) + { + bookmark.Add(mark); + OnAdded(new BookmarkEventArgs(mark)); + } + + public void RemoveMark(Bookmark mark) + { + bookmark.Remove(mark); + OnRemoved(new BookmarkEventArgs(mark)); + } + + public void RemoveMarks(Predicate predicate) + { + for (var i = 0; i < bookmark.Count; ++i) + { + var bm = bookmark[i]; + if (predicate(bm)) + { + bookmark.RemoveAt(i--); + OnRemoved(new BookmarkEventArgs(bm)); + } + } + } + + /// + /// true, if a mark at mark exists, otherwise false + /// + public bool IsMarked(int lineNr) + { + for (var i = 0; i < bookmark.Count; ++i) + if (bookmark[i].LineNumber == lineNr) + return true; + return false; + } + + /// + /// Clears all bookmark + /// + public void Clear() + { + foreach (var mark in bookmark) + OnRemoved(new BookmarkEventArgs(mark)); + bookmark.Clear(); + } + + /// + /// The lowest mark, if no marks exists it returns -1 + /// + public Bookmark GetFirstMark(Predicate predicate) + { + if (bookmark.Count < 1) + return null; + Bookmark first = null; + for (var i = 0; i < bookmark.Count; ++i) + if (predicate(bookmark[i]) && bookmark[i].IsEnabled && (first == null || bookmark[i].LineNumber < first.LineNumber)) + first = bookmark[i]; + return first; + } + + /// + /// The highest mark, if no marks exists it returns -1 + /// + public Bookmark GetLastMark(Predicate predicate) + { + if (bookmark.Count < 1) + return null; + Bookmark last = null; + for (var i = 0; i < bookmark.Count; ++i) + if (predicate(bookmark[i]) && bookmark[i].IsEnabled && (last == null || bookmark[i].LineNumber > last.LineNumber)) + last = bookmark[i]; + return last; + } + + private static bool AcceptAnyMarkPredicate(Bookmark mark) + { + return true; + } + + public Bookmark GetNextMark(int curLineNr) + { + return GetNextMark(curLineNr, AcceptAnyMarkPredicate); + } + + /// + /// returns first mark higher than lineNr + /// + /// + /// returns the next mark > cur, if it not exists it returns FirstMark() + /// + public Bookmark GetNextMark(int curLineNr, Predicate predicate) + { + if (bookmark.Count == 0) + return null; + + var next = GetFirstMark(predicate); + foreach (var mark in bookmark) + if (predicate(mark) && mark.IsEnabled && mark.LineNumber > curLineNr) + if (mark.LineNumber < next.LineNumber || next.LineNumber <= curLineNr) + next = mark; + return next; + } + + public Bookmark GetPrevMark(int curLineNr) + { + return GetPrevMark(curLineNr, AcceptAnyMarkPredicate); + } + + /// + /// returns first mark lower than lineNr + /// + /// + /// returns the next mark lower than cur, if it not exists it returns LastMark() + /// + public Bookmark GetPrevMark(int curLineNr, Predicate predicate) + { + if (bookmark.Count == 0) + return null; + + var prev = GetLastMark(predicate); + + foreach (var mark in bookmark) + if (predicate(mark) && mark.IsEnabled && mark.LineNumber < curLineNr) + if (mark.LineNumber > prev.LineNumber || prev.LineNumber >= curLineNr) + prev = mark; + return prev; + } + + protected virtual void OnRemoved(BookmarkEventArgs e) + { + Removed?.Invoke(this, e); + } + + protected virtual void OnAdded(BookmarkEventArgs e) + { + Added?.Invoke(this, e); + } + + public event BookmarkEventHandler Removed; + public event BookmarkEventHandler Added; + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/BookmarkManager/BookmarkManagerMemento.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/BookmarkManager/BookmarkManagerMemento.cs new file mode 100644 index 0000000..3b631aa --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/BookmarkManager/BookmarkManagerMemento.cs @@ -0,0 +1,93 @@ +// +// +// +// +// $Revision$ +// + +using System.Collections.Generic; +using System.Xml; + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// This class is used for storing the state of a bookmark manager + /// + public class BookmarkManagerMemento + { + /// + /// Creates a new instance of + /// + public BookmarkManagerMemento() + { + } + + /// + /// Creates a new instance of + /// + public BookmarkManagerMemento(XmlElement element) + { + foreach (XmlElement el in element.ChildNodes) + Bookmarks.Add(int.Parse(el.Attributes["line"].InnerText)); + } + + /// + /// Creates a new instance of + /// + public BookmarkManagerMemento(List bookmarks) + { + Bookmarks = bookmarks; + } + + /// + /// Contains all bookmarks as int values + /// + public List Bookmarks { get; set; } = new List(); + + /// + /// Validates all bookmarks if they're in range of the document. + /// (removing all bookmarks < 0 and bookmarks > max. line number + /// + public void CheckMemento(IDocument document) + { + for (var i = 0; i < Bookmarks.Count; ++i) + { + var mark = Bookmarks[i]; + if (mark < 0 || mark >= document.TotalNumberOfLines) + { + Bookmarks.RemoveAt(i); + --i; + } + } + } + + /// + /// Converts a xml element to a object + /// + public object FromXmlElement(XmlElement element) + { + return new BookmarkManagerMemento(element); + } + + /// + /// Converts this to a xml element + /// + public XmlElement ToXmlElement(XmlDocument doc) + { + var bookmarknode = doc.CreateElement("Bookmarks"); + + foreach (var line in Bookmarks) + { + var markNode = doc.CreateElement("Mark"); + + var lineAttr = doc.CreateAttribute("line"); + lineAttr.InnerText = line.ToString(); + markNode.Attributes.Append(lineAttr); + + bookmarknode.AppendChild(markNode); + } + + return bookmarknode; + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/DefaultDocument.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/DefaultDocument.cs new file mode 100644 index 0000000..34da7e8 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/DefaultDocument.cs @@ -0,0 +1,369 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using ICSharpCode.TextEditor.Undo; + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// Describes the caret marker + /// + public enum LineViewerStyle + { + /// + /// No line viewer will be displayed + /// + None, + + /// + /// The row in which the caret is will be marked + /// + FullRow + } + + /// + /// Describes the indent style + /// + public enum IndentStyle + { + /// + /// No indentation occurs + /// + None, + + /// + /// The indentation from the line above will be + /// taken to indent the curent line + /// + Auto, + + /// + /// Inteligent, context sensitive indentation will occur + /// + Smart + } + + /// + /// Describes the bracket highlighting style + /// + public enum BracketHighlightingStyle + { + /// + /// Brackets won't be highlighted + /// + None, + + /// + /// Brackets will be highlighted if the caret is on the bracket + /// + OnBracket, + + /// + /// Brackets will be highlighted if the caret is after the bracket + /// + AfterBracket + } + + /// + /// Describes the selection mode of the text area + /// + public enum DocumentSelectionMode + { + /// + /// The 'normal' selection mode. + /// + Normal, + + /// + /// Selections will be added to the current selection or new + /// ones will be created (multi-select mode) + /// + Additive + } + + /// + /// The default implementation. + /// + internal sealed class DefaultDocument : IDocument + { + public LineManager LineManager { get; set; } + + public event EventHandler LineLengthChanged + { + add => LineManager.LineLengthChanged += value; + remove => LineManager.LineLengthChanged -= value; + } + + public event EventHandler LineCountChanged + { + add => LineManager.LineCountChanged += value; + remove => LineManager.LineCountChanged -= value; + } + + public event EventHandler LineDeleted + { + add => LineManager.LineDeleted += value; + remove => LineManager.LineDeleted -= value; + } + + public MarkerStrategy MarkerStrategy { get; set; } + + public ITextEditorProperties TextEditorProperties { get; set; } = new DefaultTextEditorProperties(); + + public UndoStack UndoStack { get; } = new UndoStack(); + + public IList LineSegmentCollection => LineManager.LineSegmentCollection; + + public bool ReadOnly { get; set; } = false; + + public ITextBufferStrategy TextBufferStrategy { get; set; } + + public IFormattingStrategy FormattingStrategy { get; set; } + + public FoldingManager FoldingManager { get; set; } + + public IHighlightingStrategy HighlightingStrategy + { + get => LineManager.HighlightingStrategy; + set => LineManager.HighlightingStrategy = value; + } + + public int TextLength => TextBufferStrategy.Length; + + public BookmarkManager BookmarkManager { get; set; } + + public string TextContent + { + get => GetText(offset: 0, TextBufferStrategy.Length); + set + { + Debug.Assert(TextBufferStrategy != null); + Debug.Assert(LineManager != null); + OnDocumentAboutToBeChanged(new DocumentEventArgs(this, offset: 0, length: 0, value)); + TextBufferStrategy.SetContent(value); + LineManager.SetContent(value); + UndoStack.ClearAll(); + + OnDocumentChanged(new DocumentEventArgs(this, offset: 0, length: 0, value)); + OnTextContentChanged(EventArgs.Empty); + } + } + + public void Insert(int offset, string text) + { + if (ReadOnly) + return; + OnDocumentAboutToBeChanged(new DocumentEventArgs(this, offset, length: -1, text)); + + TextBufferStrategy.Insert(offset, text); + LineManager.Insert(offset, text); + + UndoStack.Push(new UndoableInsert(this, offset, text)); + + OnDocumentChanged(new DocumentEventArgs(this, offset, length: -1, text)); + } + + public void Remove(int offset, int length) + { + if (ReadOnly) + return; + OnDocumentAboutToBeChanged(new DocumentEventArgs(this, offset, length)); + UndoStack.Push(new UndoableDelete(this, offset, GetText(offset, length))); + + TextBufferStrategy.Remove(offset, length); + LineManager.Remove(offset, length); + + OnDocumentChanged(new DocumentEventArgs(this, offset, length)); + } + + public void Replace(int offset, int length, string text) + { + if (ReadOnly) + return; + OnDocumentAboutToBeChanged(new DocumentEventArgs(this, offset, length, text)); + UndoStack.Push(new UndoableReplace(this, offset, GetText(offset, length), text)); + + TextBufferStrategy.Replace(offset, length, text); + LineManager.Replace(offset, length, text); + + OnDocumentChanged(new DocumentEventArgs(this, offset, length, text)); + } + + public char GetCharAt(int offset) + { + return TextBufferStrategy.GetCharAt(offset); + } + + public string GetText(int offset, int length) + { +#if DEBUG + if (length < 0) throw new ArgumentOutOfRangeException(nameof(length), length, "length < 0"); +#endif + return TextBufferStrategy.GetText(offset, length); + } + + public string GetText(ISegment segment) + { + return GetText(segment.Offset, segment.Length); + } + + public int TotalNumberOfLines => LineManager.TotalNumberOfLines; + + public int GetLineNumberForOffset(int offset) + { + return LineManager.GetLineNumberForOffset(offset); + } + + public LineSegment GetLineSegmentForOffset(int offset) + { + return LineManager.GetLineSegmentForOffset(offset); + } + + public LineSegment GetLineSegment(int line) + { + return LineManager.GetLineSegment(line); + } + + public int GetFirstLogicalLine(int lineNumber) + { + return LineManager.GetFirstLogicalLine(lineNumber); + } + + public int GetLastLogicalLine(int lineNumber) + { + return LineManager.GetLastLogicalLine(lineNumber); + } + + public int GetVisibleLine(int lineNumber) + { + return LineManager.GetVisibleLine(lineNumber); + } + +// public int GetVisibleColumn(int logicalLine, int logicalColumn) +// { +// return lineTrackingStrategy.GetVisibleColumn(logicalLine, logicalColumn); +// } +// + public int GetNextVisibleLineAbove(int lineNumber, int lineCount) + { + return LineManager.GetNextVisibleLineAbove(lineNumber, lineCount); + } + + public int GetNextVisibleLineBelow(int lineNumber, int lineCount) + { + return LineManager.GetNextVisibleLineBelow(lineNumber, lineCount); + } + + public TextLocation OffsetToPosition(int offset) + { + var lineNr = GetLineNumberForOffset(offset); + var line = GetLineSegment(lineNr); + return new TextLocation(offset - line.Offset, lineNr); + } + + public int PositionToOffset(TextLocation p) + { + if (p.Y >= TotalNumberOfLines) + return 0; + var line = GetLineSegment(p.Y); + return Math.Min(TextLength, line.Offset + Math.Min(line.Length, p.X)); + } + + public void UpdateSegmentListOnDocumentChange(List list, DocumentEventArgs e) where T : ISegment + { + var removedCharacters = e.Length > 0 ? e.Length : 0; + var insertedCharacters = e.Text?.Length ?? 0; + for (var i = 0; i < list.Count; ++i) + { + ISegment s = list[i]; + var segmentStart = s.Offset; + var segmentEnd = s.Offset + s.Length; + + if (e.Offset <= segmentStart) + { + segmentStart -= removedCharacters; + if (segmentStart < e.Offset) + segmentStart = e.Offset; + } + + if (e.Offset < segmentEnd) + { + segmentEnd -= removedCharacters; + if (segmentEnd < e.Offset) + segmentEnd = e.Offset; + } + + Debug.Assert(segmentStart <= segmentEnd); + + if (segmentStart == segmentEnd) + { + list.RemoveAt(i); + --i; + continue; + } + + if (e.Offset <= segmentStart) + segmentStart += insertedCharacters; + if (e.Offset < segmentEnd) + segmentEnd += insertedCharacters; + + Debug.Assert(segmentStart < segmentEnd); + + s.Offset = segmentStart; + s.Length = segmentEnd - segmentStart; + } + } + + public event DocumentEventHandler DocumentAboutToBeChanged; + public event DocumentEventHandler DocumentChanged; + + // UPDATE STUFF + + public List UpdateQueue { get; } = new List(); + + public void RequestUpdate(TextAreaUpdate update) + { + if (UpdateQueue.Count == 1 && UpdateQueue[index: 0].TextAreaUpdateType == TextAreaUpdateType.WholeTextArea) + return; + if (update.TextAreaUpdateType == TextAreaUpdateType.WholeTextArea) + UpdateQueue.Clear(); + UpdateQueue.Add(update); + } + + public void CommitUpdate() + { + UpdateCommited?.Invoke(this, EventArgs.Empty); + } + + public event EventHandler UpdateCommited; + public event EventHandler TextContentChanged; + + private void OnDocumentAboutToBeChanged(DocumentEventArgs e) + { + DocumentAboutToBeChanged?.Invoke(this, e); + } + + private void OnDocumentChanged(DocumentEventArgs e) + { + DocumentChanged?.Invoke(this, e); + } + + private void OnTextContentChanged(EventArgs e) + { + TextContentChanged?.Invoke(this, e); + } + + [Conditional("DEBUG")] + internal static void ValidatePosition(IDocument document, TextLocation position) + { + document.GetLineSegment(position.Line); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/DefaultTextEditorProperties.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/DefaultTextEditorProperties.cs new file mode 100644 index 0000000..8bef45b --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/DefaultTextEditorProperties.cs @@ -0,0 +1,97 @@ +// +// +// +// +// $Revision$ +// + +using System.Drawing; +using System.Drawing.Text; +using System.Text; + +namespace ICSharpCode.TextEditor.Document +{ + public enum BracketMatchingStyle + { + Before, + After + } + + public class DefaultTextEditorProperties : ITextEditorProperties + { + private static Font DefaultFont; + + public DefaultTextEditorProperties() + { + if (DefaultFont == null) + DefaultFont = new Font("Courier New", emSize: 10); + FontContainer = new FontContainer(DefaultFont); + } + + public int TabIndent { get; set; } = 4; + + public int IndentationSize { get; set; } = 4; + + public IndentStyle IndentStyle { get; set; } = IndentStyle.Smart; + + public bool CaretLine { get; set; } = false; + + public DocumentSelectionMode DocumentSelectionMode { get; set; } = DocumentSelectionMode.Normal; + + public bool AllowCaretBeyondEOL { get; set; } = false; + + public bool ShowMatchingBracket { get; set; } = true; + + public bool ShowLineNumbers { get; set; } = true; + + public bool ShowSpaces { get; set; } = false; + + public bool ShowTabs { get; set; } = false; + + public bool ShowEOLMarker { get; set; } = false; + + public bool ShowInvalidLines { get; set; } = false; + + public bool IsIconBarVisible { get; set; } = false; + + public bool EnableFolding { get; set; } = true; + + public bool ShowHorizontalRuler { get; set; } = false; + + public bool ShowVerticalRuler { get; set; } = true; + + public bool ConvertTabsToSpaces { get; set; } = false; + + public TextRenderingHint TextRenderingHint { get; set; } = TextRenderingHint.SystemDefault; + + public bool MouseWheelScrollDown { get; set; } = true; + + public bool MouseWheelTextZoom { get; set; } = true; + + public bool HideMouseCursor { get; set; } = false; + + public bool CutCopyWholeLine { get; set; } = true; + + public Encoding Encoding { get; set; } = Encoding.UTF8; + + public int VerticalRulerRow { get; set; } = 80; + + public LineViewerStyle LineViewerStyle { get; set; } = LineViewerStyle.None; + + public string LineTerminator { get; set; } = "\r\n"; + + public bool AutoInsertCurlyBracket { get; set; } = true; + + public Font Font + { + get => FontContainer.DefaultFont; + set => FontContainer.DefaultFont = value; + } + + public FontContainer FontContainer { get; } + + public BracketMatchingStyle BracketMatchingStyle { get; set; } = BracketMatchingStyle.After; + + public bool SupportReadOnlySegments { get; set; } = false; + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/DocumentEventArgs.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/DocumentEventArgs.cs new file mode 100644 index 0000000..4218cd3 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/DocumentEventArgs.cs @@ -0,0 +1,84 @@ +// +// +// +// +// $Revision$ +// + +using System; + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// This delegate is used for document events. + /// + public delegate void DocumentEventHandler(object sender, DocumentEventArgs e); + + /// + /// This class contains more information on a document event + /// + public class DocumentEventArgs : EventArgs + { + /// + /// Creates a new instance off + /// + public DocumentEventArgs(IDocument document) : this(document, offset: -1, length: -1, text: null) + { + } + + /// + /// Creates a new instance off + /// + public DocumentEventArgs(IDocument document, int offset) : this(document, offset, length: -1, text: null) + { + } + + /// + /// Creates a new instance off + /// + public DocumentEventArgs(IDocument document, int offset, int length) : this(document, offset, length, text: null) + { + } + + /// + /// Creates a new instance off + /// + public DocumentEventArgs(IDocument document, int offset, int length, string text) + { + Document = document; + Offset = offset; + Length = length; + Text = text; + } + + /// + /// always a valid Document which is related to the Event. + /// + public IDocument Document { get; } + + /// + /// -1 if no offset was specified for this event + /// + public int Offset { get; } + + /// + /// null if no text was specified for this event + /// + public string Text { get; } + + /// + /// -1 if no length was specified for this event + /// + public int Length { get; } + + public override string ToString() + { + return string.Format( + "[DocumentEventArgs: Document = {0}, Offset = {1}, Text = {2}, Length = {3}]", + Document, + Offset, + Text, + Length); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/DocumentFactory.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/DocumentFactory.cs new file mode 100644 index 0000000..555aa54 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/DocumentFactory.cs @@ -0,0 +1,57 @@ +// +// +// +// +// $Revision$ +// + +using System.Text; +using ICSharpCode.TextEditor.Util; + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// This interface represents a container which holds a text sequence and + /// all necessary information about it. It is used as the base for a text editor. + /// + public class DocumentFactory + { + /// + /// Creates a new object. Only create + /// with this method. + /// + public IDocument CreateDocument() + { + var doc = new DefaultDocument(); + doc.TextBufferStrategy = new GapTextBufferStrategy(); + doc.FormattingStrategy = new DefaultFormattingStrategy(); + doc.LineManager = new LineManager(doc, highlightingStrategy: null); + doc.FoldingManager = new FoldingManager(doc); + doc.FoldingManager.FoldingStrategy = null; //new ParserFoldingStrategy(); + doc.MarkerStrategy = new MarkerStrategy(doc); + doc.BookmarkManager = new BookmarkManager(doc); + return doc; + } + + /// + /// Creates a new document and loads the given file + /// + public IDocument CreateFromTextBuffer(ITextBufferStrategy textBuffer) + { + var doc = (DefaultDocument)CreateDocument(); + doc.TextContent = textBuffer.GetText(offset: 0, textBuffer.Length); + doc.TextBufferStrategy = textBuffer; + return doc; + } + + /// + /// Creates a new document and loads the given file + /// + public IDocument CreateFromFile(string fileName) + { + var document = CreateDocument(); + document.TextContent = FileReader.ReadFileContent(fileName, Encoding.Default); + return document; + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/FoldingStrategy/FoldMarker.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/FoldingStrategy/FoldMarker.cs new file mode 100644 index 0000000..d92b96b --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/FoldingStrategy/FoldMarker.cs @@ -0,0 +1,183 @@ +// +// +// +// +// $Revision$ +// + +using System; + +namespace ICSharpCode.TextEditor.Document +{ + public enum FoldType + { + Unspecified, + MemberBody, + Region, + TypeBody + } + + public class FoldMarker : ISegment, IComparable + { + private readonly IDocument document; + + [CLSCompliant(isCompliant: false)] protected int length = -1; + + [CLSCompliant(isCompliant: false)] protected int offset = -1; + + private int startLine = -1, startColumn, endLine = -1, endColumn; + + public FoldMarker(IDocument document, int offset, int length, string foldText, bool isFolded) + { + this.document = document; + this.offset = offset; + this.length = length; + FoldText = foldText; + IsFolded = isFolded; + } + + public FoldMarker(IDocument document, int startLine, int startColumn, int endLine, int endColumn) : this(document, startLine, startColumn, endLine, endColumn, FoldType.Unspecified) + { + } + + public FoldMarker(IDocument document, int startLine, int startColumn, int endLine, int endColumn, FoldType foldType) : this(document, startLine, startColumn, endLine, endColumn, foldType, "...") + { + } + + public FoldMarker(IDocument document, int startLine, int startColumn, int endLine, int endColumn, FoldType foldType, string foldText) : this(document, startLine, startColumn, endLine, endColumn, foldType, foldText, isFolded: false) + { + } + + public FoldMarker(IDocument document, int startLine, int startColumn, int endLine, int endColumn, FoldType foldType, string foldText, bool isFolded) + { + this.document = document; + + startLine = Math.Min(document.TotalNumberOfLines - 1, Math.Max(startLine, val2: 0)); + ISegment startLineSegment = document.GetLineSegment(startLine); + + endLine = Math.Min(document.TotalNumberOfLines - 1, Math.Max(endLine, val2: 0)); + ISegment endLineSegment = document.GetLineSegment(endLine); + + // Prevent the region from completely disappearing + if (string.IsNullOrEmpty(foldText)) + foldText = "..."; + + FoldType = foldType; + FoldText = foldText; + offset = startLineSegment.Offset + Math.Min(startColumn, startLineSegment.Length); + length = endLineSegment.Offset + Math.Min(endColumn, endLineSegment.Length) - offset; + IsFolded = isFolded; + } + + public FoldType FoldType { get; set; } = FoldType.Unspecified; + + public int StartLine + { + get + { + if (startLine < 0) + GetPointForOffset(document, offset, out startLine, out startColumn); + return startLine; + } + } + + public int StartColumn + { + get + { + if (startLine < 0) + GetPointForOffset(document, offset, out startLine, out startColumn); + return startColumn; + } + } + + public int EndLine + { + get + { + if (endLine < 0) + GetPointForOffset(document, offset + length, out endLine, out endColumn); + return endLine; + } + } + + public int EndColumn + { + get + { + if (endLine < 0) + GetPointForOffset(document, offset + length, out endLine, out endColumn); + return endColumn; + } + } + + public bool IsFolded { get; set; } + + public string FoldText { get; } = "..."; + + public string InnerText => document.GetText(offset, length); + + public int CompareTo(object o) + { + if (!(o is FoldMarker)) + throw new ArgumentException(); + var f = (FoldMarker)o; + if (offset != f.offset) + return offset.CompareTo(f.offset); + + return length.CompareTo(f.length); + } + + public override string ToString() + { + return string.Format( + "[FoldMarker: Offset = {0}, Length = {1}]", + offset, + length); + } + + private static void GetPointForOffset(IDocument document, int offset, out int line, out int column) + { + if (offset > document.TextLength) + { + line = document.TotalNumberOfLines + 1; + column = 1; + } + else if (offset < 0) + { + line = -1; + column = -1; + } + else + { + line = document.GetLineNumberForOffset(offset); + column = offset - document.GetLineSegment(line).Offset; + } + } + + #region ICSharpCode.TextEditor.Document.ISegment interface implementation + + public int Offset + { + get => offset; + set + { + offset = value; + startLine = -1; + endLine = -1; + } + } + + public int Length + { + get => length; + set + { + length = value; + endLine = -1; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/FoldingStrategy/FoldingManager.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/FoldingStrategy/FoldingManager.cs new file mode 100644 index 0000000..7ce83f2 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/FoldingStrategy/FoldingManager.cs @@ -0,0 +1,342 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Text; + +namespace ICSharpCode.TextEditor.Document +{ + public class FoldingManager + { + private readonly IDocument document; + private List foldMarker = new List(); + private List foldMarkerByEnd = new List(); + + internal FoldingManager(IDocument document) + { + this.document = document; + document.DocumentChanged += DocumentChanged; + +// lineTracker.LineCountChanged += new LineManagerEventHandler(LineManagerLineCountChanged); +// lineTracker.LineLengthChanged += new LineLengthEventHandler(LineManagerLineLengthChanged); +// foldMarker.Add(new FoldMarker(0, 5, 3, 5)); +// +// foldMarker.Add(new FoldMarker(5, 5, 10, 3)); +// foldMarker.Add(new FoldMarker(6, 0, 8, 2)); +// +// FoldMarker fm1 = new FoldMarker(10, 4, 10, 7); +// FoldMarker fm2 = new FoldMarker(10, 10, 10, 14); +// +// fm1.IsFolded = true; +// fm2.IsFolded = true; +// +// foldMarker.Add(fm1); +// foldMarker.Add(fm2); +// foldMarker.Sort(); + } + + public IList FoldMarker => foldMarker.AsReadOnly(); + + public IFoldingStrategy FoldingStrategy { get; set; } = null; + + private void DocumentChanged(object sender, DocumentEventArgs e) + { + var oldCount = foldMarker.Count; + document.UpdateSegmentListOnDocumentChange(foldMarker, e); + if (oldCount != foldMarker.Count) + document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.WholeTextArea)); + } + + public List GetFoldingsFromPosition(int line, int column) + { + var foldings = new List(); + if (foldMarker != null) + for (var i = 0; i < foldMarker.Count; ++i) + { + var fm = foldMarker[i]; + if (fm.StartLine == line && column > fm.StartColumn && !(fm.EndLine == line && column >= fm.EndColumn) || + fm.EndLine == line && column < fm.EndColumn && !(fm.StartLine == line && column <= fm.StartColumn) || + line > fm.StartLine && line < fm.EndLine) + foldings.Add(fm); + } + + return foldings; + } + + private List GetFoldingsByStartAfterColumn(int lineNumber, int column, bool forceFolded) + { + var foldings = new List(); + + if (foldMarker != null) + { + var index = foldMarker.BinarySearch( + new FoldMarker(document, lineNumber, column, lineNumber, column), + StartComparer.Instance); + if (index < 0) index = ~index; + + for (; index < foldMarker.Count; index++) + { + var fm = foldMarker[index]; + if (fm.StartLine > lineNumber) + break; + if (fm.StartColumn <= column) + continue; + if (!forceFolded || fm.IsFolded) + foldings.Add(fm); + } + } + + return foldings; + } + + public List GetFoldingsWithStart(int lineNumber) + { + return GetFoldingsByStartAfterColumn(lineNumber, column: -1, forceFolded: false); + } + + public List GetFoldedFoldingsWithStart(int lineNumber) + { + return GetFoldingsByStartAfterColumn(lineNumber, column: -1, forceFolded: true); + } + + public List GetFoldedFoldingsWithStartAfterColumn(int lineNumber, int column) + { + return GetFoldingsByStartAfterColumn(lineNumber, column, forceFolded: true); + } + + private List GetFoldingsByEndAfterColumn(int lineNumber, int column, bool forceFolded) + { + var foldings = new List(); + + if (foldMarker != null) + { + var index = foldMarkerByEnd.BinarySearch( + new FoldMarker(document, lineNumber, column, lineNumber, column), + EndComparer.Instance); + if (index < 0) index = ~index; + + for (; index < foldMarkerByEnd.Count; index++) + { + var fm = foldMarkerByEnd[index]; + if (fm.EndLine > lineNumber) + break; + if (fm.EndColumn <= column) + continue; + if (!forceFolded || fm.IsFolded) + foldings.Add(fm); + } + } + + return foldings; + } + + public List GetFoldingsWithEnd(int lineNumber) + { + return GetFoldingsByEndAfterColumn(lineNumber, column: -1, forceFolded: false); + } + + public List GetFoldedFoldingsWithEnd(int lineNumber) + { + return GetFoldingsByEndAfterColumn(lineNumber, column: -1, forceFolded: true); + } + + public bool IsFoldStart(int lineNumber) + { + return GetFoldingsWithStart(lineNumber).Count > 0; + } + + public bool IsFoldEnd(int lineNumber) + { + return GetFoldingsWithEnd(lineNumber).Count > 0; + } + + public List GetFoldingsContainsLineNumber(int lineNumber) + { + var foldings = new List(); + if (foldMarker != null) + foreach (var fm in foldMarker) + if (fm.StartLine < lineNumber && lineNumber < fm.EndLine) + foldings.Add(fm); + return foldings; + } + + public bool IsBetweenFolding(int lineNumber) + { + return GetFoldingsContainsLineNumber(lineNumber).Count > 0; + } + + public bool IsLineVisible(int lineNumber) + { + foreach (var fm in GetFoldingsContainsLineNumber(lineNumber)) + if (fm.IsFolded) + return false; + return true; + } + + public List GetTopLevelFoldedFoldings() + { + var foldings = new List(); + if (foldMarker != null) + { + var end = new Point(x: 0, y: 0); + foreach (var fm in foldMarker) + if (fm.IsFolded && (fm.StartLine > end.Y || fm.StartLine == end.Y && fm.StartColumn >= end.X)) + { + foldings.Add(fm); + end = new Point(fm.EndColumn, fm.EndLine); + } + } + + return foldings; + } + + public void UpdateFoldings(string fileName, object parseInfo) + { + UpdateFoldings(FoldingStrategy.GenerateFoldMarkers(document, fileName, parseInfo)); + } + + public void UpdateFoldings(List newFoldings) + { + var oldFoldingsCount = foldMarker.Count; + lock (this) + { + if (newFoldings != null && newFoldings.Count != 0) + { + newFoldings.Sort(); + if (foldMarker.Count == newFoldings.Count) + { + for (var i = 0; i < foldMarker.Count; ++i) + newFoldings[i].IsFolded = foldMarker[i].IsFolded; + foldMarker = newFoldings; + } + else + { + for (int i = 0, j = 0; i < foldMarker.Count && j < newFoldings.Count;) + { + var n = newFoldings[j].CompareTo(foldMarker[i]); + if (n > 0) + { + ++i; + } + else + { + if (n == 0) + newFoldings[j].IsFolded = foldMarker[i].IsFolded; + ++j; + } + } + } + } + + if (newFoldings != null) + { + foldMarker = newFoldings; + foldMarkerByEnd = new List(newFoldings); + foldMarkerByEnd.Sort(EndComparer.Instance); + } + else + { + foldMarker.Clear(); + foldMarkerByEnd.Clear(); + } + } + + if (oldFoldingsCount != foldMarker.Count) + { + document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.WholeTextArea)); + document.CommitUpdate(); + } + } + + public string SerializeToString() + { + var sb = new StringBuilder(); + foreach (var marker in foldMarker) + { + sb.Append(marker.Offset); + sb.Append("\n"); + sb.Append(marker.Length); + sb.Append("\n"); + sb.Append(marker.FoldText); + sb.Append("\n"); + sb.Append(marker.IsFolded); + sb.Append("\n"); + } + + return sb.ToString(); + } + + public void DeserializeFromString(string str) + { + try + { + var lines = str.Split('\n'); + for (var i = 0; i < lines.Length && lines[i].Length > 0; i += 4) + { + var offset = int.Parse(lines[i]); + var length = int.Parse(lines[i + 1]); + var text = lines[i + 2]; + var isFolded = bool.Parse(lines[i + 3]); + var found = false; + foreach (var marker in foldMarker) + if (marker.Offset == offset && marker.Length == length) + { + marker.IsFolded = isFolded; + found = true; + break; + } + + if (!found) + foldMarker.Add(new FoldMarker(document, offset, length, text, isFolded)); + } + + if (lines.Length > 0) + NotifyFoldingsChanged(EventArgs.Empty); + } + catch (Exception) + { + } + } + + public void NotifyFoldingsChanged(EventArgs e) + { + FoldingsChanged?.Invoke(this, e); + } + + public event EventHandler FoldingsChanged; + + private class StartComparer : IComparer + { + public static readonly StartComparer Instance = new StartComparer(); + + public int Compare(FoldMarker x, FoldMarker y) + { + if (x.StartLine < y.StartLine) + return -1; + if (x.StartLine == y.StartLine) + return x.StartColumn.CompareTo(y.StartColumn); + return 1; + } + } + + private class EndComparer : IComparer + { + public static readonly EndComparer Instance = new EndComparer(); + + public int Compare(FoldMarker x, FoldMarker y) + { + if (x.EndLine < y.EndLine) + return -1; + if (x.EndLine == y.EndLine) + return x.EndColumn.CompareTo(y.EndColumn); + return 1; + } + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/FoldingStrategy/IFoldingStrategy.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/FoldingStrategy/IFoldingStrategy.cs new file mode 100644 index 0000000..b81453f --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/FoldingStrategy/IFoldingStrategy.cs @@ -0,0 +1,23 @@ +// +// +// +// +// $Revision$ +// + +using System.Collections.Generic; + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// This interface is used for the folding capabilities + /// of the textarea. + /// + public interface IFoldingStrategy + { + /// + /// Calculates the fold level of a specific line. + /// + List GenerateFoldMarkers(IDocument document, string fileName, object parseInformation); + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/FoldingStrategy/IndentFoldingStrategy.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/FoldingStrategy/IndentFoldingStrategy.cs new file mode 100644 index 0000000..cbfa90f --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/FoldingStrategy/IndentFoldingStrategy.cs @@ -0,0 +1,51 @@ +// +// +// +// +// $Revision$ +// + +using System.Collections.Generic; + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// A simple folding strategy which calculates the folding level + /// using the indent level of the line. + /// + public class IndentFoldingStrategy : IFoldingStrategy + { + public List GenerateFoldMarkers(IDocument document, string fileName, object parseInformation) + { + var l = new List(); +// var offsetStack = new Stack(); +// var textStack = new Stack(); + //int level = 0; + //foreach (LineSegment segment in document.LineSegmentCollection) { + // + //} + return l; + } + + private int GetLevel(IDocument document, int offset) + { + var level = 0; + var spaces = 0; + for (var i = offset; i < document.TextLength; ++i) + { + var c = document.GetCharAt(i); + if (c == '\t' || c == ' ' && ++spaces == 4) + { + spaces = 0; + ++level; + } + else + { + break; + } + } + + return level; + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/FormattingStrategy/DefaultFormattingStrategy.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/FormattingStrategy/DefaultFormattingStrategy.cs new file mode 100644 index 0000000..f318bfe --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/FormattingStrategy/DefaultFormattingStrategy.cs @@ -0,0 +1,242 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Text; + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// This class handles the auto and smart indenting in the textbuffer while + /// you type. + /// + public class DefaultFormattingStrategy : IFormattingStrategy + { + private static readonly char[] whitespaceChars = {' ', '\t'}; + + /// + /// This function formats a specific line after ch is pressed. + /// + /// + /// the caret delta position the caret will be moved this number + /// of bytes (e.g. the number of bytes inserted before the caret, or + /// removed, if this number is negative) + /// + public virtual void FormatLine(TextArea textArea, int line, int cursorOffset, char ch) + { + if (ch == '\n') + textArea.Caret.Column = IndentLine(textArea, line); + } + + /// + /// This function sets the indentation level in a specific line + /// + /// + /// the number of inserted characters. + /// + public int IndentLine(TextArea textArea, int line) + { + textArea.Document.UndoStack.StartUndoGroup(); + int result; + switch (textArea.Document.TextEditorProperties.IndentStyle) + { + case IndentStyle.None: + result = 0; + break; + case IndentStyle.Auto: + result = AutoIndentLine(textArea, line); + break; + case IndentStyle.Smart: + result = SmartIndentLine(textArea, line); + break; + default: + throw new NotSupportedException("Unsupported value for IndentStyle: " + textArea.Document.TextEditorProperties.IndentStyle); + } + + textArea.Document.UndoStack.EndUndoGroup(); + return result; + } + + /// + /// This function sets the indentlevel in a range of lines. + /// + public virtual void IndentLines(TextArea textArea, int begin, int end) + { + textArea.Document.UndoStack.StartUndoGroup(); + for (var i = begin; i <= end; ++i) + IndentLine(textArea, i); + textArea.Document.UndoStack.EndUndoGroup(); + } + + public virtual int SearchBracketBackward(IDocument document, int offset, char openBracket, char closingBracket) + { + var brackets = -1; + // first try "quick find" - find the matching bracket if there is no string/comment in the way + for (var i = offset; i >= 0; --i) + { + var ch = document.GetCharAt(i); + if (ch == openBracket) + { + ++brackets; + if (brackets == 0) return i; + } + else if (ch == closingBracket) + { + --brackets; + } + else if (ch == '"') + { + break; + } + else if (ch == '\'') + { + break; + } + else if (ch == '/' && i > 0) + { + if (document.GetCharAt(i - 1) == '/') break; + if (document.GetCharAt(i - 1) == '*') break; + } + } + + return -1; + } + + public virtual int SearchBracketForward(IDocument document, int offset, char openBracket, char closingBracket) + { + var brackets = 1; + // try "quick find" - find the matching bracket if there is no string/comment in the way + for (var i = offset; i < document.TextLength; ++i) + { + var ch = document.GetCharAt(i); + if (ch == openBracket) + { + ++brackets; + } + else if (ch == closingBracket) + { + --brackets; + if (brackets == 0) return i; + } + else if (ch == '"') + { + break; + } + else if (ch == '\'') + { + break; + } + else if (ch == '/' && i > 0) + { + if (document.GetCharAt(i - 1) == '/') break; + } + else if (ch == '*' && i > 0) + { + if (document.GetCharAt(i - 1) == '/') break; + } + } + + return -1; + } + + /// + /// returns the whitespaces which are before a non white space character in the line line + /// as a string. + /// + protected string GetIndentation(TextArea textArea, int lineNumber) + { + if (lineNumber < 0 || lineNumber > textArea.Document.TotalNumberOfLines) + throw new ArgumentOutOfRangeException(nameof(lineNumber)); + + var lineText = TextUtilities.GetLineAsString(textArea.Document, lineNumber); + var whitespaces = new StringBuilder(); + + foreach (var ch in lineText) + if (char.IsWhiteSpace(ch)) + whitespaces.Append(ch); + else + break; + return whitespaces.ToString(); + } + + /// + /// Could be overwritten to define more complex indenting. + /// + protected virtual int AutoIndentLine(TextArea textArea, int lineNumber) + { + var indentation = lineNumber != 0 ? GetIndentation(textArea, lineNumber - 1) : ""; + if (indentation.Length > 0) + { + var newLineText = indentation + TextUtilities.GetLineAsString(textArea.Document, lineNumber).Trim(); + var oldLine = textArea.Document.GetLineSegment(lineNumber); + SmartReplaceLine(textArea.Document, oldLine, newLineText); + } + + return indentation.Length; + } + + /// + /// Replaces the text in a line. + /// If only whitespace at the beginning and end of the line was changed, this method + /// only adjusts the whitespace and doesn't replace the other text. + /// + public static void SmartReplaceLine(IDocument document, LineSegment line, string newLineText) + { + if (document == null) + throw new ArgumentNullException(nameof(document)); + if (line == null) + throw new ArgumentNullException(nameof(line)); + if (newLineText == null) + throw new ArgumentNullException(nameof(newLineText)); + var newLineTextTrim = newLineText.Trim(whitespaceChars); + var oldLineText = document.GetText(line); + if (oldLineText == newLineText) + return; + var pos = oldLineText.IndexOf(newLineTextTrim); + if (newLineTextTrim.Length > 0 && pos >= 0) + { + document.UndoStack.StartUndoGroup(); + try + { + // find whitespace at beginning + var startWhitespaceLength = 0; + while (startWhitespaceLength < newLineText.Length) + { + var c = newLineText[startWhitespaceLength]; + if (c != ' ' && c != '\t') + break; + startWhitespaceLength++; + } + + // find whitespace at end + var endWhitespaceLength = newLineText.Length - newLineTextTrim.Length - startWhitespaceLength; + + // replace whitespace sections + var lineOffset = line.Offset; + document.Replace(lineOffset + pos + newLineTextTrim.Length, line.Length - pos - newLineTextTrim.Length, newLineText.Substring(newLineText.Length - endWhitespaceLength)); + document.Replace(lineOffset, pos, newLineText.Substring(startIndex: 0, startWhitespaceLength)); + } + finally + { + document.UndoStack.EndUndoGroup(); + } + } + else + { + document.Replace(line.Offset, line.Length, newLineText); + } + } + + /// + /// Could be overwritten to define more complex indenting. + /// + protected virtual int SmartIndentLine(TextArea textArea, int line) + { + return AutoIndentLine(textArea, line); // smart = autoindent in normal texts + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/FormattingStrategy/IFormattingStrategy.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/FormattingStrategy/IFormattingStrategy.cs new file mode 100644 index 0000000..086fc8f --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/FormattingStrategy/IFormattingStrategy.cs @@ -0,0 +1,57 @@ +// +// +// +// +// $Revision$ +// + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// This interface handles the auto and smart indenting and formating + /// in the document while you type. Language bindings could overwrite this + /// interface and define their own indentation/formating. + /// + public interface IFormattingStrategy + { + /// + /// This function formats a specific line after ch is pressed. + /// + void FormatLine(TextArea textArea, int line, int caretOffset, char charTyped); + + /// + /// This function sets the indentation level in a specific line + /// + /// + /// The target caret position (length of new indentation). + /// + int IndentLine(TextArea textArea, int line); + + /// + /// This function sets the indentlevel in a range of lines. + /// + void IndentLines(TextArea textArea, int begin, int end); + + /// + /// Finds the offset of the opening bracket in the block defined by offset skipping + /// brackets in strings and comments. + /// + /// The document to search in. + /// The offset of an position in the block or the offset of the closing bracket. + /// The character for the opening bracket. + /// The character for the closing bracket. + /// Returns the offset of the opening bracket or -1 if no matching bracket was found. + int SearchBracketBackward(IDocument document, int offset, char openBracket, char closingBracket); + + /// + /// Finds the offset of the closing bracket in the block defined by offset skipping + /// brackets in strings and comments. + /// + /// The document to search in. + /// The offset of an position in the block or the offset of the opening bracket. + /// The character for the opening bracket. + /// The character for the closing bracket. + /// Returns the offset of the closing bracket or -1 if no matching bracket was found. + int SearchBracketForward(IDocument document, int offset, char openBracket, char closingBracket); + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/DefaultHighlightingStrategy.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/DefaultHighlightingStrategy.cs new file mode 100644 index 0000000..a4d7d36 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/DefaultHighlightingStrategy.cs @@ -0,0 +1,926 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Text; + +namespace ICSharpCode.TextEditor.Document +{ + public class DefaultHighlightingStrategy : IHighlightingStrategyUsingRuleSets + { + protected HighlightRuleSet activeRuleSet; + protected Span activeSpan; + protected int currentLength; + + // Line state variable + protected LineSegment currentLine; + protected int currentLineNumber; + + // Line scanning state variables + protected int currentOffset; + + // Span stack state variable + protected SpanStack currentSpanStack; + + private HighlightRuleSet defaultRuleSet; + private Dictionary environmentColors; + + // Span state variables + protected bool inSpan; + + public DefaultHighlightingStrategy() : this("Default") + { + } + + public DefaultHighlightingStrategy(string name) + { + Name = name; + + DigitColor = new HighlightColor(SystemColors.WindowText, bold: false, italic: false); + DefaultTextColor = new HighlightColor(SystemColors.WindowText, bold: false, italic: false); + + // set small 'default color environment' + environmentColors = new Dictionary + { + ["Default"] = new HighlightBackground(nameof(SystemColors.WindowText), nameof(SystemColors.Window), bold: false, italic: false), + ["Selection"] = new HighlightColor(nameof(SystemColors.HighlightText), nameof(SystemColors.Highlight), bold: false, italic: false), + ["VRuler"] = new HighlightColor(nameof(SystemColors.ControlLight), nameof(SystemColors.Window), bold: false, italic: false), + ["InvalidLines"] = new HighlightColor(Color.Red, bold: false, italic: false), + ["CaretMarker"] = new HighlightColor(Color.Yellow, bold: false, italic: false), + ["CaretLine"] = new HighlightBackground(nameof(SystemColors.ControlLight), nameof(SystemColors.Window), bold: false, italic: false), + ["LineNumbers"] = new HighlightBackground(nameof(SystemColors.GrayText), nameof(SystemColors.Window), bold: false, italic: false), + ["FoldLine"] = new HighlightColor(nameof(SystemColors.ControlDark), bold: false, italic: false), + ["FoldMarker"] = new HighlightColor(nameof(SystemColors.WindowText), nameof(SystemColors.Window), bold: false, italic: false), + ["SelectedFoldLine"] = new HighlightColor(nameof(SystemColors.WindowText), bold: false, italic: false), + ["EOLMarkers"] = new HighlightColor(nameof(SystemColors.ControlLight), nameof(SystemColors.Window), bold: false, italic: false), + ["SpaceMarkers"] = new HighlightColor(nameof(SystemColors.ControlLight), nameof(SystemColors.Window), bold: false, italic: false), + ["TabMarkers"] = new HighlightColor(nameof(SystemColors.ControlLight), nameof(SystemColors.Window), bold: false, italic: false) + }; + + } + + public HighlightColor DigitColor { get; set; } + + public IEnumerable> EnvironmentColors => environmentColors; + + public List Rules { get; private set; } = new List(); + +// internal void SetDefaultColor(HighlightBackground color) +// { +// return (HighlightColor)environmentColors[name]; +// defaultColor = color; +// } + + public HighlightColor DefaultTextColor { get; private set; } + + public Dictionary Properties { get; private set; } = new Dictionary(); + + public string Name { get; private set; } + + public string[] Extensions { set; get; } + + public HighlightColor GetColorFor(string name) + { + if (environmentColors.TryGetValue(name, out var color)) + return color; + return DefaultTextColor; + } + + public HighlightColor GetColor(IDocument document, LineSegment currentSegment, int currentOffset, int currentLength) + { + return GetColor(defaultRuleSet, document, currentSegment, currentOffset, currentLength); + } + + public HighlightRuleSet GetRuleSet(Span aSpan) + { + if (aSpan == null) + return defaultRuleSet; + + if (aSpan.RuleSet != null) + { + if (aSpan.RuleSet.Reference != null) + return aSpan.RuleSet.Highlighter.GetRuleSet(span: null); + + return aSpan.RuleSet; + } + + return null; + } + + public virtual void MarkTokens(IDocument document) + { + if (Rules.Count == 0) + return; + + var lineNumber = 0; + + while (lineNumber < document.TotalNumberOfLines) + { + var previousLine = lineNumber > 0 ? document.GetLineSegment(lineNumber - 1) : null; + if (lineNumber >= document.LineSegmentCollection.Count) + break; // then the last line is not in the collection :) + + currentSpanStack = previousLine?.HighlightSpanStack?.Clone(); + + if (currentSpanStack != null) + { + while (!currentSpanStack.IsEmpty && currentSpanStack.Peek().StopEOL) + currentSpanStack.Pop(); + if (currentSpanStack.IsEmpty) currentSpanStack = null; + } + + currentLine = document.LineSegmentCollection[lineNumber]; + + if (currentLine.Length == -1) + return; + + currentLineNumber = lineNumber; + var words = ParseLine(document); + // Alex: clear old words + currentLine.Words?.Clear(); + currentLine.Words = words; + currentLine.HighlightSpanStack = currentSpanStack == null || currentSpanStack.IsEmpty ? null : currentSpanStack; + + ++lineNumber; + } + + document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.WholeTextArea)); + document.CommitUpdate(); + currentLine = null; + } + + public virtual void MarkTokens(IDocument document, List inputLines) + { + if (Rules.Count == 0) + return; + + var processedLines = new Dictionary(); + + var spanChanged = false; + var documentLineSegmentCount = document.LineSegmentCollection.Count; + + foreach (var lineToProcess in inputLines) + if (!processedLines.ContainsKey(lineToProcess)) + { + var lineNumber = lineToProcess.LineNumber; + var processNextLine = true; + + if (lineNumber != -1) + while (processNextLine && lineNumber < documentLineSegmentCount) + { + processNextLine = MarkTokensInLine(document, lineNumber, ref spanChanged); + processedLines[currentLine] = true; + ++lineNumber; + } + } + + if (spanChanged || inputLines.Count > 20) + document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.WholeTextArea)); + else + foreach (var lineToProcess in inputLines) + document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.SingleLine, lineToProcess.LineNumber)); + document.CommitUpdate(); + currentLine = null; + } + + protected void ImportSettingsFrom(DefaultHighlightingStrategy source) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + Properties = source.Properties; + Extensions = source.Extensions; + DigitColor = source.DigitColor; + defaultRuleSet = source.defaultRuleSet; + Name = source.Name; + Rules = source.Rules; + environmentColors = source.environmentColors; + DefaultTextColor = source.DefaultTextColor; + } + + public HighlightRuleSet FindHighlightRuleSet(string name) + { + foreach (var ruleSet in Rules) + if (ruleSet.Name == name) + return ruleSet; + return null; + } + + public void AddRuleSet(HighlightRuleSet aRuleSet) + { + var existing = FindHighlightRuleSet(aRuleSet.Name); + if (existing != null) + existing.MergeFrom(aRuleSet); + else + Rules.Add(aRuleSet); + } + + public void ResolveReferences() + { + // Resolve references from Span definitions to RuleSets + ResolveRuleSetReferences(); + // Resolve references from RuleSet defintitions to Highlighters defined in an external mode file + ResolveExternalReferences(); + } + + private void ResolveRuleSetReferences() + { + foreach (var ruleSet in Rules) + { + if (ruleSet.Name == null) + defaultRuleSet = ruleSet; + + foreach (Span aSpan in ruleSet.Spans) + if (aSpan.Rule != null) + { + var found = false; + foreach (var refSet in Rules) + if (refSet.Name == aSpan.Rule) + { + found = true; + aSpan.RuleSet = refSet; + break; + } + + if (!found) + { + aSpan.RuleSet = null; + throw new HighlightingDefinitionInvalidException("The RuleSet " + aSpan.Rule + " could not be found in mode definition " + Name); + } + } + else + { + aSpan.RuleSet = null; + } + } + + if (defaultRuleSet == null) + throw new HighlightingDefinitionInvalidException("No default RuleSet is defined for mode definition " + Name); + } + + private void ResolveExternalReferences() + { + foreach (var ruleSet in Rules) + { + ruleSet.Highlighter = this; + if (ruleSet.Reference != null) + { + var highlighter = HighlightingManager.Manager.FindHighlighter(ruleSet.Reference); + + if (highlighter == null) + throw new HighlightingDefinitionInvalidException("The mode defintion " + ruleSet.Reference + " which is refered from the " + Name + " mode definition could not be found"); + if (highlighter is IHighlightingStrategyUsingRuleSets sets) + ruleSet.Highlighter = sets; + else + throw new HighlightingDefinitionInvalidException("The mode defintion " + ruleSet.Reference + " which is refered from the " + Name + " mode definition does not implement IHighlightingStrategyUsingRuleSets"); + } + } + } + + public void SetColorFor(string name, HighlightColor color) + { + if (name == "Default") + DefaultTextColor = new HighlightColor(color.Color, color.Bold, color.Italic); + environmentColors[name] = color; + } + + protected virtual HighlightColor GetColor(HighlightRuleSet ruleSet, IDocument document, LineSegment currentSegment, int currentOffset, int currentLength) + { + if (ruleSet != null) + { + if (ruleSet.Reference != null) + return ruleSet.Highlighter.GetColor(document, currentSegment, currentOffset, currentLength); + + return (HighlightColor)ruleSet.KeyWords[document, currentSegment, currentOffset, currentLength]; + } + + return null; + } + + private bool MarkTokensInLine(IDocument document, int lineNumber, ref bool spanChanged) + { + currentLineNumber = lineNumber; + var processNextLine = false; + var previousLine = lineNumber > 0 ? document.GetLineSegment(lineNumber - 1) : null; + + currentSpanStack = previousLine != null && previousLine.HighlightSpanStack != null ? previousLine.HighlightSpanStack.Clone() : null; + if (currentSpanStack != null) + { + while (!currentSpanStack.IsEmpty && currentSpanStack.Peek().StopEOL) + currentSpanStack.Pop(); + if (currentSpanStack.IsEmpty) + currentSpanStack = null; + } + + currentLine = document.LineSegmentCollection[lineNumber]; + + if (currentLine.Length == -1) + return false; + + var words = ParseLine(document); + + if (currentSpanStack != null && currentSpanStack.IsEmpty) + currentSpanStack = null; + + // Check if the span state has changed, if so we must re-render the next line + // This check may seem utterly complicated but I didn't want to introduce any function calls + // or allocations here for perf reasons. + if (currentLine.HighlightSpanStack != currentSpanStack) + { + if (currentLine.HighlightSpanStack == null) + { + foreach (var sp in currentSpanStack) + if (!sp.StopEOL) + { + spanChanged = true; + processNextLine = true; + break; + } + } + else if (currentSpanStack == null) + { + foreach (var sp in currentLine.HighlightSpanStack) + if (!sp.StopEOL) + { + spanChanged = true; + processNextLine = true; + break; + } + } + else + { + var e1 = currentSpanStack.GetEnumerator(); + var e2 = currentLine.HighlightSpanStack.GetEnumerator(); + var done = false; + while (!done) + { + var blockSpanIn1 = false; + while (e1.MoveNext()) + if (!e1.Current.StopEOL) + { + blockSpanIn1 = true; + break; + } + + var blockSpanIn2 = false; + while (e2.MoveNext()) + if (!e2.Current.StopEOL) + { + blockSpanIn2 = true; + break; + } + + if (blockSpanIn1 || blockSpanIn2) + { + if (blockSpanIn1 && blockSpanIn2) + { + if (e1.Current != e2.Current) + { + done = true; + processNextLine = true; + spanChanged = true; + } + } + else + { + spanChanged = true; + done = true; + processNextLine = true; + } + } + else + { + done = true; + } + } + } + } + + //// Alex: remove old words + currentLine.Words?.Clear(); + currentLine.Words = words; + currentLine.HighlightSpanStack = currentSpanStack != null && !currentSpanStack.IsEmpty ? currentSpanStack : null; + + return processNextLine; + } + + private void UpdateSpanStateVariables() + { + inSpan = currentSpanStack != null && !currentSpanStack.IsEmpty; + activeSpan = inSpan ? currentSpanStack.Peek() : null; + activeRuleSet = GetRuleSet(activeSpan); + } + + private List ParseLine(IDocument document) + { + var words = new List(); + HighlightColor markNext = null; + + currentOffset = 0; + currentLength = 0; + UpdateSpanStateVariables(); + + var currentLineLength = currentLine.Length; + var currentLineOffset = currentLine.Offset; + + for (var i = 0; i < currentLineLength; ++i) + { + var ch = document.GetCharAt(currentLineOffset + i); + switch (ch) + { + case '\n': + case '\r': + PushCurWord(document, ref markNext, words); + ++currentOffset; + continue; + case ' ': + PushCurWord(document, ref markNext, words); + if (activeSpan != null && activeSpan.Color.HasBackground) + words.Add(new TextWord.SpaceTextWord(activeSpan.Color)); + else + words.Add(TextWord.Space); + ++currentOffset; + continue; + case '\t': + PushCurWord(document, ref markNext, words); + if (activeSpan != null && activeSpan.Color.HasBackground) + words.Add(new TextWord.TabTextWord(activeSpan.Color)); + else + words.Add(TextWord.Tab); + ++currentOffset; + continue; + } + + // handle escape characters + var escapeCharacter = '\0'; + if (activeSpan != null && activeSpan.EscapeCharacter != '\0') + escapeCharacter = activeSpan.EscapeCharacter; + else if (activeRuleSet != null) + escapeCharacter = activeRuleSet.EscapeCharacter; + if (escapeCharacter != '\0' && escapeCharacter == ch) + { + // we found the escape character + if (activeSpan != null && activeSpan.End != null && activeSpan.End.Length == 1 + && escapeCharacter == activeSpan.End[0]) + { + // the escape character is a end-doubling escape character + // it may count as escape only when the next character is the escape, too + if (i + 1 < currentLineLength) + if (document.GetCharAt(currentLineOffset + i + 1) == escapeCharacter) + { + currentLength += 2; + PushCurWord(document, ref markNext, words); + ++i; + continue; + } + } + else + { + // this is a normal \-style escape + ++currentLength; + if (i + 1 < currentLineLength) + ++currentLength; + PushCurWord(document, ref markNext, words); + ++i; + continue; + } + } + + // highlight digits + if (!inSpan && (char.IsDigit(ch) || ch == '.' && i + 1 < currentLineLength && char.IsDigit(document.GetCharAt(currentLineOffset + i + 1))) && currentLength == 0) + { + var ishex = false; + var isfloatingpoint = false; + + if (ch == '0' && i + 1 < currentLineLength && char.ToUpper(document.GetCharAt(currentLineOffset + i + 1)) == 'X') + { + // hex digits + const string hex = "0123456789ABCDEF"; + ++currentLength; + ++i; // skip 'x' + ++currentLength; + ishex = true; + while (i + 1 < currentLineLength && hex.IndexOf(char.ToUpper(document.GetCharAt(currentLineOffset + i + 1))) != -1) + { + ++i; + ++currentLength; + } + } + else + { + ++currentLength; + while (i + 1 < currentLineLength && char.IsDigit(document.GetCharAt(currentLineOffset + i + 1))) + { + ++i; + ++currentLength; + } + } + + if (!ishex && i + 1 < currentLineLength && document.GetCharAt(currentLineOffset + i + 1) == '.') + { + isfloatingpoint = true; + ++i; + ++currentLength; + while (i + 1 < currentLineLength && char.IsDigit(document.GetCharAt(currentLineOffset + i + 1))) + { + ++i; + ++currentLength; + } + } + + if (i + 1 < currentLineLength && char.ToUpper(document.GetCharAt(currentLineOffset + i + 1)) == 'E') + { + isfloatingpoint = true; + ++i; + ++currentLength; + if (i + 1 < currentLineLength && (document.GetCharAt(currentLineOffset + i + 1) == '+' || document.GetCharAt(currentLine.Offset + i + 1) == '-')) + { + ++i; + ++currentLength; + } + + while (i + 1 < currentLine.Length && char.IsDigit(document.GetCharAt(currentLineOffset + i + 1))) + { + ++i; + ++currentLength; + } + } + + if (i + 1 < currentLine.Length) + { + var nextch = char.ToUpper(document.GetCharAt(currentLineOffset + i + 1)); + if (nextch == 'F' || nextch == 'M' || nextch == 'D') + { + isfloatingpoint = true; + ++i; + ++currentLength; + } + } + + if (!isfloatingpoint) + { + var isunsigned = false; + if (i + 1 < currentLineLength && char.ToUpper(document.GetCharAt(currentLineOffset + i + 1)) == 'U') + { + ++i; + ++currentLength; + isunsigned = true; + } + + if (i + 1 < currentLineLength && char.ToUpper(document.GetCharAt(currentLineOffset + i + 1)) == 'L') + { + ++i; + ++currentLength; + if (!isunsigned && i + 1 < currentLineLength && char.ToUpper(document.GetCharAt(currentLineOffset + i + 1)) == 'U') + { + ++i; + ++currentLength; + } + } + } + + words.Add(new TextWord(document, currentLine, currentOffset, currentLength, DigitColor, hasDefaultColor: false)); + currentOffset += currentLength; + currentLength = 0; + continue; + } + + // Check for SPAN ENDs + if (inSpan) + if (activeSpan.End != null && activeSpan.End.Length > 0) + if (MatchExpr(currentLine, activeSpan.End, i, document, activeSpan.IgnoreCase)) + { + PushCurWord(document, ref markNext, words); + var regex = GetRegString(currentLine, activeSpan.End, i, document); + currentLength += regex.Length; + words.Add(new TextWord(document, currentLine, currentOffset, currentLength, activeSpan.EndColor, hasDefaultColor: false)); + currentOffset += currentLength; + currentLength = 0; + i += regex.Length - 1; + currentSpanStack.Pop(); + UpdateSpanStateVariables(); + continue; + } + + // check for SPAN BEGIN + if (activeRuleSet != null) + { + Span mySpan = null; + foreach (Span span in activeRuleSet.Spans) + { + if (span.IsBeginSingleWord && currentLength != 0) + continue; + if (span.IsBeginStartOfLine.HasValue && + span.IsBeginStartOfLine.Value != + (currentLength == 0 && words.TrueForAll( + delegate(TextWord textWord) { return textWord.Type != TextWordType.Word; }))) + continue; + if (!MatchExpr(currentLine, span.Begin, i, document, activeRuleSet.IgnoreCase)) + continue; + mySpan = span; + break; + } + + if (mySpan != null) + { + PushCurWord(document, ref markNext, words); + var regex = GetRegString(currentLine, mySpan.Begin, i, document); + + if (!OverrideSpan(regex, document, words, mySpan, ref i)) + { + currentLength += regex.Length; + words.Add(new TextWord(document, currentLine, currentOffset, currentLength, mySpan.BeginColor, hasDefaultColor: false)); + currentOffset += currentLength; + currentLength = 0; + + i += regex.Length - 1; + if (currentSpanStack == null) + currentSpanStack = new SpanStack(); + currentSpanStack.Push(mySpan); + mySpan.IgnoreCase = activeRuleSet.IgnoreCase; + + UpdateSpanStateVariables(); + } + + continue; + } + } + + // check if the char is a delimiter + if (activeRuleSet != null && ch < 256 && activeRuleSet.Delimiters[ch]) + { + PushCurWord(document, ref markNext, words); + if (currentOffset + currentLength + 1 < currentLine.Length) + { + ++currentLength; + PushCurWord(document, ref markNext, words); + continue; + } + } + + ++currentLength; + } + + PushCurWord(document, ref markNext, words); + + OnParsedLine(document, currentLine, words); + + return words; + } + + protected virtual void OnParsedLine(IDocument document, LineSegment currentLine, List words) + { + } + + protected virtual bool OverrideSpan(string spanBegin, IDocument document, List words, Span span, ref int lineOffset) + { + return false; + } + + /// + /// pushes the curWord string on the word list, with the + /// correct color. + /// + private void PushCurWord(IDocument document, ref HighlightColor markNext, List words) + { + // Svante Lidman : Need to look through the next prev logic. + if (currentLength > 0) + { + if (words.Count > 0 && activeRuleSet != null) + { + var pInd = words.Count - 1; + while (pInd >= 0) + { + if (!words[pInd].IsWhiteSpace) + { + var prevWord = words[pInd]; + if (prevWord.HasDefaultColor) + { + var marker = (PrevMarker)activeRuleSet.PrevMarkers[document, currentLine, currentOffset, currentLength]; + if (marker != null) + prevWord.SyntaxColor = marker.Color; + } + + break; + } + + pInd--; + } + } + + if (inSpan) + { + HighlightColor c; + var hasDefaultColor = true; + if (activeSpan.Rule == null) + { + c = activeSpan.Color; + } + else + { + c = GetColor(activeRuleSet, document, currentLine, currentOffset, currentLength); + hasDefaultColor = false; + } + + if (c == null) + { + c = activeSpan.Color; + if (c.Color == Color.Transparent) + c = DefaultTextColor; + hasDefaultColor = true; + } + + words.Add(new TextWord(document, currentLine, currentOffset, currentLength, markNext ?? c, hasDefaultColor)); + } + else + { + var c = markNext ?? GetColor(activeRuleSet, document, currentLine, currentOffset, currentLength); + if (c == null) + words.Add(new TextWord(document, currentLine, currentOffset, currentLength, DefaultTextColor, hasDefaultColor: true)); + else + words.Add(new TextWord(document, currentLine, currentOffset, currentLength, c, hasDefaultColor: false)); + } + + if (activeRuleSet != null) + { + var nextMarker = (NextMarker)activeRuleSet.NextMarkers[document, currentLine, currentOffset, currentLength]; + if (nextMarker != null) + { + System.Diagnostics.Debug.Assert(words.Count > 0, "Logic changed, no longer words.Count > 0"); + if (nextMarker.MarkMarker) + { + var prevword = words[words.Count - 1]; + prevword.SyntaxColor = nextMarker.Color; + } + + markNext = nextMarker.Color; + } + else + { + markNext = null; + } + } + + currentOffset += currentLength; + currentLength = 0; + } + } + + #region Matching + + /// + /// get the string, which matches the regular expression expr, + /// in string s2 at index + /// + private static string GetRegString(LineSegment lineSegment, char[] expr, int index, IDocument document) + { + var j = 0; + var regexpr = new StringBuilder(); + + for (var i = 0; i < expr.Length; ++i, ++j) + { + if (index + j >= lineSegment.Length) + break; + + switch (expr[i]) + { + case '@': // "special" meaning + ++i; + if (i == expr.Length) + throw new HighlightingDefinitionInvalidException("Unexpected end of @ sequence, use @@ to look for a single @."); + switch (expr[i]) + { + case '!': // don't match the following expression + var whatmatch = new StringBuilder(); + ++i; + while (i < expr.Length && expr[i] != '@') + whatmatch.Append(expr[i++]); + break; + case '@': // matches @ + regexpr.Append(document.GetCharAt(lineSegment.Offset + index + j)); + break; + } + + break; + default: + if (expr[i] != document.GetCharAt(lineSegment.Offset + index + j)) + return regexpr.ToString(); + regexpr.Append(document.GetCharAt(lineSegment.Offset + index + j)); + break; + } + } + + return regexpr.ToString(); + } + + /// + /// returns true, if the get the string s2 at index matches the expression expr + /// + private static bool MatchExpr(LineSegment lineSegment, char[] expr, int index, IDocument document, bool ignoreCase) + { + for (int i = 0, j = 0; i < expr.Length; ++i, ++j) + switch (expr[i]) + { + case '@': // "special" meaning + ++i; + if (i == expr.Length) + throw new HighlightingDefinitionInvalidException("Unexpected end of @ sequence, use @@ to look for a single @."); + switch (expr[i]) + { + case 'C': // match whitespace or punctuation + if (index + j == lineSegment.Offset || index + j >= lineSegment.Offset + lineSegment.Length) + { + // nothing (EOL or SOL) + } + else + { + var ch = document.GetCharAt(lineSegment.Offset + index + j); + if (!char.IsWhiteSpace(ch) && !char.IsPunctuation(ch)) + return false; + } + + break; + case '!': // don't match the following expression + { + var whatmatch = new StringBuilder(); + ++i; + while (i < expr.Length && expr[i] != '@') + whatmatch.Append(expr[i++]); + if (lineSegment.Offset + index + j + whatmatch.Length < document.TextLength) + { + var k = 0; + for (; k < whatmatch.Length; ++k) + { + var docChar = ignoreCase ? char.ToUpperInvariant(document.GetCharAt(lineSegment.Offset + index + j + k)) : document.GetCharAt(lineSegment.Offset + index + j + k); + var spanChar = ignoreCase ? char.ToUpperInvariant(whatmatch[k]) : whatmatch[k]; + if (docChar != spanChar) + break; + } + + if (k >= whatmatch.Length) + return false; + } + +// --j; + break; + } + case '-': // don't match the expression before + { + var whatmatch = new StringBuilder(); + ++i; + while (i < expr.Length && expr[i] != '@') + whatmatch.Append(expr[i++]); + if (index - whatmatch.Length >= 0) + { + var k = 0; + for (; k < whatmatch.Length; ++k) + { + var docChar = ignoreCase ? char.ToUpperInvariant(document.GetCharAt(lineSegment.Offset + index - whatmatch.Length + k)) : document.GetCharAt(lineSegment.Offset + index - whatmatch.Length + k); + var spanChar = ignoreCase ? char.ToUpperInvariant(whatmatch[k]) : whatmatch[k]; + if (docChar != spanChar) + break; + } + + if (k >= whatmatch.Length) + return false; + } + +// --j; + break; + } + case '@': // matches @ + if (index + j >= lineSegment.Length || '@' != document.GetCharAt(lineSegment.Offset + index + j)) + return false; + break; + } + + break; + default: + { + if (index + j >= lineSegment.Length) + return false; + var offset = lineSegment.Offset; + var docChar = document.GetCharAt(offset + index + j); + var spanChar = expr[i]; + if (ignoreCase) + { + docChar = char.ToUpperInvariant(docChar); + spanChar = char.ToUpperInvariant(spanChar); + } + + if (docChar != spanChar) + return false; + break; + } + } + return true; + } + + #endregion + } +} diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/FontContainer.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/FontContainer.cs new file mode 100644 index 0000000..6bf7247 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/FontContainer.cs @@ -0,0 +1,89 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Drawing; + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// This class is used to generate bold, italic and bold/italic fonts out + /// of a base font. + /// + public class FontContainer + { + private static float twipsPerPixelY; + private Font defaultFont; + + public FontContainer(Font defaultFont) + { + DefaultFont = defaultFont; + } + + /// + /// The scaled, regular version of the base font + /// + public Font RegularFont { get; private set; } + + /// + /// The scaled, bold version of the base font + /// + public Font BoldFont { get; private set; } + + /// + /// The scaled, italic version of the base font + /// + public Font ItalicFont { get; private set; } + + /// + /// The scaled, bold/italic version of the base font + /// + public Font BoldItalicFont { get; private set; } + + public static float TwipsPerPixelY + { + get + { + if (twipsPerPixelY == 0) + using (var bmp = new Bitmap(width: 1, height: 1)) + { + using (var g = Graphics.FromImage(bmp)) + { + twipsPerPixelY = 1440/g.DpiY; + } + } + + return twipsPerPixelY; + } + } + + /// + /// The base font + /// + public Font DefaultFont + { + get => defaultFont; + set + { + // 1440 twips is one inch + var pixelSize = (float)Math.Round(value.SizeInPoints*20/TwipsPerPixelY); + + defaultFont = value; + RegularFont = new Font(value.FontFamily, pixelSize*TwipsPerPixelY/20f, FontStyle.Regular); + BoldFont = new Font(RegularFont, FontStyle.Bold); + ItalicFont = new Font(RegularFont, FontStyle.Italic); + BoldItalicFont = new Font(RegularFont, FontStyle.Bold | FontStyle.Italic); + } + } + + public static Font ParseFont(string font) + { + var descr = font.Split(',', '='); + return new Font(descr[1], float.Parse(descr[3])); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/HighlightBackground.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/HighlightBackground.cs new file mode 100644 index 0000000..3d4b624 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/HighlightBackground.cs @@ -0,0 +1,43 @@ +// +// +// +// +// $Revision$ +// + +using System.Drawing; +using System.Xml; + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// Extens the highlighting color with a background image. + /// + public class HighlightBackground : HighlightColor + { + /// + /// Creates a new instance of + /// + public HighlightBackground(XmlElement el) : base(el) + { + if (el.Attributes["image"] != null) + BackgroundImage = new Bitmap(el.Attributes["image"].InnerText); + } + + /// + /// Creates a new instance of + /// + public HighlightBackground(Color color, Color backgroundcolor, bool bold, bool italic) : base(color, backgroundcolor, bold, italic) + { + } + + public HighlightBackground(string systemColor, string systemBackgroundColor, bool bold, bool italic) : base(systemColor, systemBackgroundColor, bold, italic) + { + } + + /// + /// The image used as background + /// + public Image BackgroundImage { get; } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/HighlightColor.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/HighlightColor.cs new file mode 100644 index 0000000..22e5fa0 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/HighlightColor.cs @@ -0,0 +1,256 @@ +// +// +// +// +// $Revision$ +// + +using System.Diagnostics; +using System.Drawing; +using System.Globalization; +using System.Reflection; +using System.Xml; + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// A color used for highlighting + /// + public class HighlightColor + { + /// + /// Creates a new instance of + /// + public HighlightColor(XmlElement el) + { + Debug.Assert(el != null, "ICSharpCode.TextEditor.Document.SyntaxColor(XmlElement el) : el == null"); + if (el.Attributes["bold"] != null) + Bold = bool.Parse(el.Attributes["bold"].InnerText); + + if (el.Attributes["italic"] != null) + Italic = bool.Parse(el.Attributes["italic"].InnerText); + + if (el.Attributes["color"] != null) + { + var c = el.Attributes["color"].InnerText; + if (c[index: 0] == '#') + Color = ParseColor(c); + else if (c.StartsWith("SystemColors.")) + Color = ParseColorString(c.Substring("SystemColors.".Length)); + else + Color = (Color)Color.GetType().InvokeMember(c, BindingFlags.GetProperty, binder: null, Color, new object[0]); + HasForeground = true; + } + else + { + Color = Color.Transparent; // to set it to the default value. + } + + if (el.Attributes["bgcolor"] != null) + { + var c = el.Attributes["bgcolor"].InnerText; + if (c[index: 0] == '#') + BackgroundColor = ParseColor(c); + else if (c.StartsWith("SystemColors.")) + BackgroundColor = ParseColorString(c.Substring("SystemColors.".Length)); + else + BackgroundColor = (Color)Color.GetType().InvokeMember(c, BindingFlags.GetProperty, binder: null, Color, new object[0]); + HasBackground = true; + } + } + + /// + /// Creates a new instance of + /// + public HighlightColor(XmlElement el, HighlightColor defaultColor) + { + Debug.Assert(el != null, "ICSharpCode.TextEditor.Document.SyntaxColor(XmlElement el) : el == null"); + if (el.Attributes["bold"] != null) + Bold = bool.Parse(el.Attributes["bold"].InnerText); + else + Bold = defaultColor.Bold; + + if (el.Attributes["italic"] != null) + Italic = bool.Parse(el.Attributes["italic"].InnerText); + else + Italic = defaultColor.Italic; + + if (el.Attributes["color"] != null) + { + var c = el.Attributes["color"].InnerText; + if (c[index: 0] == '#') + Color = ParseColor(c); + else if (c.StartsWith("SystemColors.")) + Color = ParseColorString(c.Substring("SystemColors.".Length)); + else + Color = (Color)Color.GetType().InvokeMember(c, BindingFlags.GetProperty, binder: null, Color, new object[0]); + HasForeground = true; + } + else + { + Color = defaultColor.Color; + } + + if (el.Attributes["bgcolor"] != null) + { + var c = el.Attributes["bgcolor"].InnerText; + if (c[index: 0] == '#') + BackgroundColor = ParseColor(c); + else if (c.StartsWith("SystemColors.")) + BackgroundColor = ParseColorString(c.Substring("SystemColors.".Length)); + else + BackgroundColor = (Color)Color.GetType().InvokeMember(c, BindingFlags.GetProperty, binder: null, Color, new object[0]); + HasBackground = true; + } + else + { + BackgroundColor = defaultColor.BackgroundColor; + } + } + + /// + /// Creates a new instance of + /// + public HighlightColor(HighlightColor original, Color color, Color backColor) + { + Bold = original.Bold; + Italic = original.Italic; + HasForeground = original.HasForeground; + HasBackground = original.HasBackground; + Color = color; + BackgroundColor = backColor; + } + + /// + /// Creates a new instance of + /// + public HighlightColor(Color color, bool bold, bool italic) + { + HasForeground = true; + Color = color; + Bold = bold; + Italic = italic; + } + + /// + /// Creates a new instance of + /// + public HighlightColor(Color color, Color backgroundcolor, bool bold, bool italic) + { + HasForeground = true; + HasBackground = true; + Color = color; + BackgroundColor = backgroundcolor; + Bold = bold; + Italic = italic; + } + + /// + /// Creates a new instance of + /// + public HighlightColor(string systemColor, string systemBackgroundColor, bool bold, bool italic) + { + HasForeground = true; + HasBackground = true; + + Color = ParseColorString(systemColor); + BackgroundColor = ParseColorString(systemBackgroundColor); + + Bold = bold; + Italic = italic; + } + + /// + /// Creates a new instance of + /// + public HighlightColor(string systemColor, bool bold, bool italic) + { + HasForeground = true; + + Color = ParseColorString(systemColor); + + Bold = bold; + Italic = italic; + } + + public bool HasForeground { get; } + + public bool HasBackground { get; } + + /// + /// If true the font will be displayed bold style + /// + public bool Bold { get; } + + /// + /// If true the font will be displayed italic style + /// + public bool Italic { get; } + + /// + /// The background color used + /// + public Color BackgroundColor { get; } = Color.WhiteSmoke; + + /// + /// The foreground color used + /// + public Color Color { get; } + + /// + /// The font used + /// + public Font GetFont(FontContainer fontContainer) + { + if (Bold) + return Italic ? fontContainer.BoldItalicFont : fontContainer.BoldFont; + return Italic ? fontContainer.ItalicFont : fontContainer.RegularFont; + } + + private static Color ParseColorString(string colorName) + { + var cNames = colorName.Split('*'); + var myPropInfo = typeof(SystemColors).GetProperty( + cNames[0], BindingFlags.Public | + BindingFlags.Instance | + BindingFlags.Static); + var c = (Color)myPropInfo.GetValue(obj: null, index: null); + + if (cNames.Length == 2) + { + // hack : can't figure out how to parse doubles with '.' (culture info might set the '.' to ',') + var factor = double.Parse(cNames[1])/100; + c = Color.FromArgb((int)(c.R*factor), (int)(c.G*factor), (int)(c.B*factor)); + } + + return c; + } + + private static Color ParseColor(string c) + { + var a = 255; + var offset = 0; + if (c.Length > 7) + { + offset = 2; + a = int.Parse(c.Substring(startIndex: 1, length: 2), NumberStyles.HexNumber); + } + + var r = int.Parse(c.Substring(1 + offset, length: 2), NumberStyles.HexNumber); + var g = int.Parse(c.Substring(3 + offset, length: 2), NumberStyles.HexNumber); + var b = int.Parse(c.Substring(5 + offset, length: 2), NumberStyles.HexNumber); + return Color.FromArgb(a, r, g, b); + } + + /// + /// Converts a instance to string (for debug purposes) + /// + public override string ToString() + { + return "[HighlightColor: Bold = " + Bold + + ", Italic = " + Italic + + ", Color = " + Color + + ", BackgroundColor = " + BackgroundColor + "]"; + } + } +} diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/HighlightInfo.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/HighlightInfo.cs new file mode 100644 index 0000000..4114096 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/HighlightInfo.cs @@ -0,0 +1,23 @@ +// +// +// +// +// $Revision$ +// + +namespace ICSharpCode.TextEditor.Document +{ + public class HighlightInfo + { + public bool BlockSpanOn; + public Span CurSpan; + public bool Span; + + public HighlightInfo(Span curSpan, bool span, bool blockSpanOn) + { + CurSpan = curSpan; + Span = span; + BlockSpanOn = blockSpanOn; + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/HighlightRuleSet.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/HighlightRuleSet.cs new file mode 100644 index 0000000..27936c8 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/HighlightRuleSet.cs @@ -0,0 +1,121 @@ +// +// +// +// +// $Revision$ +// + +using System.Collections.Generic; +using System.Linq; +using System.Xml; +using ICSharpCode.TextEditor.Util; + +namespace ICSharpCode.TextEditor.Document +{ + public class HighlightRuleSet + { + internal IHighlightingStrategyUsingRuleSets Highlighter; + + public HighlightRuleSet() + { + KeyWords = new LookupTable(casesensitive: false); + PrevMarkers = new LookupTable(casesensitive: false); + NextMarkers = new LookupTable(casesensitive: false); + } + + public HighlightRuleSet(XmlElement el) + { + if (el.Attributes["name"] != null) + Name = el.Attributes["name"].InnerText; + + if (el.HasAttribute("escapecharacter")) + EscapeCharacter = el.GetAttribute("escapecharacter")[index: 0]; + + if (el.Attributes["reference"] != null) + Reference = el.Attributes["reference"].InnerText; + + if (el.Attributes["ignorecase"] != null) + IgnoreCase = bool.Parse(el.Attributes["ignorecase"].InnerText); + + for (var i = 0; i < Delimiters.Length; ++i) + Delimiters[i] = false; + + if (el["Delimiters"] != null) + { + var delimiterString = el["Delimiters"].InnerText; + foreach (var ch in delimiterString) + Delimiters[ch] = true; + } + +// Spans = new LookupTable(!IgnoreCase); + + KeyWords = new LookupTable(!IgnoreCase); + PrevMarkers = new LookupTable(!IgnoreCase); + NextMarkers = new LookupTable(!IgnoreCase); + + var nodes = el.GetElementsByTagName("KeyWords"); + foreach (XmlElement el2 in nodes) + { + var color = new HighlightColor(el2); + + var keys = el2.GetElementsByTagName("Key"); + foreach (XmlElement node in keys) + KeyWords[node.Attributes["word"].InnerText] = color; + } + + nodes = el.GetElementsByTagName("Span"); + foreach (XmlElement el2 in nodes) Spans.Add(new Span(el2)); + /* + Span span = new Span(el2); + Spans[span.Begin] = span;*/ + + nodes = el.GetElementsByTagName("MarkPrevious"); + foreach (XmlElement el2 in nodes) + { + var prev = new PrevMarker(el2); + PrevMarkers[prev.What] = prev; + } + + nodes = el.GetElementsByTagName("MarkFollowing"); + foreach (XmlElement el2 in nodes) + { + var next = new NextMarker(el2); + NextMarkers[next.What] = next; + } + } + + public List Spans { get; private set; } = new List(); + + public LookupTable KeyWords { get; } + + public LookupTable PrevMarkers { get; } + + public LookupTable NextMarkers { get; } + + public bool[] Delimiters { get; } = new bool[256]; + + public char EscapeCharacter { get; } + + public bool IgnoreCase { get; } + + public string Name { get; set; } + + public string Reference { get; } + + /// + /// Merges spans etc. from the other rule set into this rule set. + /// + public void MergeFrom(HighlightRuleSet ruleSet) + { + for (var i = 0; i < Delimiters.Length; i++) + Delimiters[i] |= ruleSet.Delimiters[i]; + // insert merged spans in front of old spans + var oldSpans = Spans; + Spans = ruleSet.Spans.ToList(); + Spans.AddRange(oldSpans); + //keyWords.MergeFrom(ruleSet.keyWords); + //prevMarkers.MergeFrom(ruleSet.prevMarkers); + //nextMarkers.MergeFrom(ruleSet.nextMarkers); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/HighlightingColorNotFoundException.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/HighlightingColorNotFoundException.cs new file mode 100644 index 0000000..f7174c8 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/HighlightingColorNotFoundException.cs @@ -0,0 +1,32 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Runtime.Serialization; + +namespace ICSharpCode.TextEditor.Document +{ + [Serializable] + public class HighlightingColorNotFoundException : Exception + { + public HighlightingColorNotFoundException() + { + } + + public HighlightingColorNotFoundException(string message) : base(message) + { + } + + public HighlightingColorNotFoundException(string message, Exception innerException) : base(message, innerException) + { + } + + protected HighlightingColorNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/HighlightingDefinitionInvalidException.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/HighlightingDefinitionInvalidException.cs new file mode 100644 index 0000000..88cb5bc --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/HighlightingDefinitionInvalidException.cs @@ -0,0 +1,37 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Runtime.Serialization; + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// Indicates that the highlighting definition that was tried to load was invalid. + /// You get this exception only once per highlighting definition, after that the definition + /// is replaced with the default highlighter. + /// + [Serializable] + public class HighlightingDefinitionInvalidException : Exception + { + public HighlightingDefinitionInvalidException() + { + } + + public HighlightingDefinitionInvalidException(string message) : base(message) + { + } + + public HighlightingDefinitionInvalidException(string message, Exception innerException) : base(message, innerException) + { + } + + protected HighlightingDefinitionInvalidException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/HighlightingDefinitionParser.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/HighlightingDefinitionParser.cs new file mode 100644 index 0000000..f1ad5ba --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/HighlightingDefinitionParser.cs @@ -0,0 +1,104 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; +using System.Xml.Schema; + +namespace ICSharpCode.TextEditor.Document +{ + public static class HighlightingDefinitionParser + { + public static DefaultHighlightingStrategy Parse(SyntaxMode syntaxMode, XmlReader xmlReader) + { + return Parse(highlighter: null, syntaxMode, xmlReader); + } + + public static DefaultHighlightingStrategy Parse(DefaultHighlightingStrategy highlighter, SyntaxMode syntaxMode, XmlReader xmlReader) + { + if (syntaxMode == null) + throw new ArgumentNullException(nameof(syntaxMode)); + if (xmlReader == null) + throw new ArgumentNullException("xmlTextReader"); + try + { + List errors = null; + var settings = new XmlReaderSettings(); + var shemaStream = typeof(HighlightingDefinitionParser).Assembly.GetManifestResourceStream("ICSharpCode.TextEditor.Resources.Mode.xsd"); + settings.Schemas.Add(targetNamespace: null, new XmlTextReader(shemaStream)); + settings.Schemas.ValidationEventHandler += delegate(object sender, ValidationEventArgs args) + { + if (errors == null) + errors = new List(); + errors.Add(args); + }; + settings.ValidationType = ValidationType.Schema; + var validatingReader = XmlReader.Create(xmlReader, settings); + + var doc = new XmlDocument(); + doc.Load(validatingReader); + + if (highlighter == null) + highlighter = new DefaultHighlightingStrategy(doc.DocumentElement.Attributes["name"].InnerText); + + if (doc.DocumentElement.HasAttribute("extends")) + { + var entry = HighlightingManager.Manager.FindHighlighterEntry(doc.DocumentElement.GetAttribute("extends")); + if (entry.Key == null) + throw new HighlightingDefinitionInvalidException("Cannot find referenced highlighting source " + doc.DocumentElement.GetAttribute("extends")); + + highlighter = Parse(highlighter, entry.Key, entry.Value.GetSyntaxModeFile(entry.Key)); + if (highlighter == null) return null; + } + + if (doc.DocumentElement.HasAttribute("extensions")) + highlighter.Extensions = doc.DocumentElement.GetAttribute("extensions").Split(';', '|'); + + var environment = doc.DocumentElement["Environment"]; + if (environment != null) + foreach (XmlNode node in environment.ChildNodes) + if (node is XmlElement el) + { + if (el.Name == "Custom") + highlighter.SetColorFor(el.GetAttribute("name"), el.HasAttribute("bgcolor") ? new HighlightBackground(el) : new HighlightColor(el)); + else + highlighter.SetColorFor(el.Name, el.HasAttribute("bgcolor") ? new HighlightBackground(el) : new HighlightColor(el)); + } + + // parse properties + if (doc.DocumentElement["Properties"] != null) + foreach (XmlElement propertyElement in doc.DocumentElement["Properties"].ChildNodes) + highlighter.Properties[propertyElement.Attributes["name"].InnerText] = propertyElement.Attributes["value"].InnerText; + + if (doc.DocumentElement["Digits"] != null) + highlighter.DigitColor = new HighlightColor(doc.DocumentElement["Digits"]); + + var nodes = doc.DocumentElement.GetElementsByTagName("RuleSet"); + foreach (XmlElement element in nodes) + highlighter.AddRuleSet(new HighlightRuleSet(element)); + + xmlReader.Close(); + + if (errors != null) + { + var msg = new StringBuilder(); + foreach (var args in errors) + msg.AppendLine(args.Message); + throw new HighlightingDefinitionInvalidException(msg.ToString()); + } + + return highlighter; + } + catch (Exception e) + { + throw new HighlightingDefinitionInvalidException("Could not load mode definition file '" + syntaxMode.FileName + "'.\n", e); + } + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/HighlightingManager.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/HighlightingManager.cs new file mode 100644 index 0000000..370c8dd --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/HighlightingManager.cs @@ -0,0 +1,147 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace ICSharpCode.TextEditor.Document +{ + public class HighlightingManager + { + // hash table from extension name to highlighting definition, + // OR from extension name to Pair SyntaxMode,ISyntaxModeFileProvider + + private readonly Dictionary extensionsToName = new Dictionary(); + private readonly List syntaxModeFileProviders = new List(); + + static HighlightingManager() + { + Manager = new HighlightingManager(); + Manager.AddSyntaxModeFileProvider(new ResourceSyntaxModeProvider()); + } + + public HighlightingManager() + { + CreateDefaultHighlightingStrategy(); + } + + public Dictionary HighlightingDefinitions { get; } = new Dictionary(); + + public static HighlightingManager Manager { get; } + + public DefaultHighlightingStrategy DefaultHighlighting => (DefaultHighlightingStrategy)HighlightingDefinitions["Default"]; + + public void AddSyntaxModeFileProvider(ISyntaxModeFileProvider syntaxModeFileProvider) + { + foreach (var syntaxMode in syntaxModeFileProvider.SyntaxModes) + { + HighlightingDefinitions[syntaxMode.Name] = new DictionaryEntry(syntaxMode, syntaxModeFileProvider); + foreach (var extension in syntaxMode.Extensions) + extensionsToName[extension.ToUpperInvariant()] = syntaxMode.Name; + } + + if (!syntaxModeFileProviders.Contains(syntaxModeFileProvider)) + syntaxModeFileProviders.Add(syntaxModeFileProvider); + } + + public void AddHighlightingStrategy(IHighlightingStrategy highlightingStrategy) + { + HighlightingDefinitions[highlightingStrategy.Name] = highlightingStrategy; + foreach (var extension in highlightingStrategy.Extensions) + extensionsToName[extension.ToUpperInvariant()] = highlightingStrategy.Name; + } + + public void ReloadSyntaxModes() + { + HighlightingDefinitions.Clear(); + extensionsToName.Clear(); + CreateDefaultHighlightingStrategy(); + foreach (ISyntaxModeFileProvider provider in syntaxModeFileProviders) + { + provider.UpdateSyntaxModeList(); + AddSyntaxModeFileProvider(provider); + } + + OnReloadSyntaxHighlighting(EventArgs.Empty); + } + + private void CreateDefaultHighlightingStrategy() + { + var defaultHighlightingStrategy = new DefaultHighlightingStrategy(); + defaultHighlightingStrategy.Extensions = new string[] { }; + defaultHighlightingStrategy.Rules.Add(new HighlightRuleSet()); + HighlightingDefinitions["Default"] = defaultHighlightingStrategy; + } + + private IHighlightingStrategy LoadDefinition(DictionaryEntry entry) + { + var syntaxMode = (SyntaxMode)entry.Key; + var syntaxModeFileProvider = (ISyntaxModeFileProvider)entry.Value; + + DefaultHighlightingStrategy highlightingStrategy = null; + try + { + var reader = syntaxModeFileProvider.GetSyntaxModeFile(syntaxMode); + if (reader == null) + throw new HighlightingDefinitionInvalidException("Could not get syntax mode file for " + syntaxMode.Name); + highlightingStrategy = HighlightingDefinitionParser.Parse(syntaxMode, reader); + if (highlightingStrategy.Name != syntaxMode.Name) + throw new HighlightingDefinitionInvalidException("The name specified in the .xshd '" + highlightingStrategy.Name + "' must be equal the syntax mode name '" + syntaxMode.Name + "'"); + } + finally + { + if (highlightingStrategy == null) + highlightingStrategy = DefaultHighlighting; + HighlightingDefinitions[syntaxMode.Name] = highlightingStrategy; + highlightingStrategy.ResolveReferences(); + } + + return highlightingStrategy; + } + + internal KeyValuePair FindHighlighterEntry(string name) + { + foreach (ISyntaxModeFileProvider provider in syntaxModeFileProviders) + foreach (var mode in provider.SyntaxModes) + if (mode.Name == name) + return new KeyValuePair(mode, provider); + return default; + } + + public IHighlightingStrategy FindHighlighter(string name) + { + if (HighlightingDefinitions.TryGetValue(name, out var def)) + { + switch (def) + { + case DictionaryEntry entry: + return LoadDefinition(entry); + case IHighlightingStrategy strategy: + return strategy; + } + } + + return DefaultHighlighting; + } + + public IHighlightingStrategy FindHighlighterForFile(string fileName) + { + var highlighterName = extensionsToName.FirstOrDefault(e => fileName.EndsWith(e.Key, StringComparison.OrdinalIgnoreCase)); + return highlighterName.Key == null ? DefaultHighlighting : FindHighlighter(highlighterName.Value); + } + + protected virtual void OnReloadSyntaxHighlighting(EventArgs e) + { + ReloadSyntaxHighlighting?.Invoke(this, e); + } + + public event EventHandler ReloadSyntaxHighlighting; + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/HighlightingStrategyFactory.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/HighlightingStrategyFactory.cs new file mode 100644 index 0000000..75953c9 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/HighlightingStrategyFactory.cs @@ -0,0 +1,34 @@ +// +// +// +// +// $Revision$ +// + +namespace ICSharpCode.TextEditor.Document +{ + public class HighlightingStrategyFactory + { + public static IHighlightingStrategy CreateHighlightingStrategy() + { + return (IHighlightingStrategy)HighlightingManager.Manager.HighlightingDefinitions["Default"]; + } + + public static IHighlightingStrategy CreateHighlightingStrategy(string name) + { + var highlightingStrategy = HighlightingManager.Manager.FindHighlighter(name); + + if (highlightingStrategy == null) + return CreateHighlightingStrategy(); + return highlightingStrategy; + } + + public static IHighlightingStrategy CreateHighlightingStrategyForFile(string fileName) + { + var highlightingStrategy = HighlightingManager.Manager.FindHighlighterForFile(fileName); + if (highlightingStrategy == null) + return CreateHighlightingStrategy(); + return highlightingStrategy; + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/IHighlightingStrategy.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/IHighlightingStrategy.cs new file mode 100644 index 0000000..3510559 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/IHighlightingStrategy.cs @@ -0,0 +1,60 @@ +// +// +// +// +// $Revision$ +// + +using System.Collections.Generic; + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// A highlighting strategy for a buffer. + /// + public interface IHighlightingStrategy + { + /// + /// The name of the highlighting strategy, must be unique + /// + string Name { get; } + + /// + /// The file extenstions on which this highlighting strategy gets + /// used + /// + string[] Extensions { get; } + + Dictionary Properties { get; } + + // returns special color. (BackGround Color, Cursor Color and so on) + + /// + /// Gets the color of an Environment element. + /// + HighlightColor GetColorFor(string name); + + /// + /// Used internally, do not call + /// + void MarkTokens(IDocument document, List lines); + + /// + /// Used internally, do not call + /// + void MarkTokens(IDocument document); + } + + public interface IHighlightingStrategyUsingRuleSets : IHighlightingStrategy + { + /// + /// Used internally, do not call + /// + HighlightRuleSet GetRuleSet(Span span); + + /// + /// Used internally, do not call + /// + HighlightColor GetColor(IDocument document, LineSegment keyWord, int index, int length); + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/NextMarker.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/NextMarker.cs new file mode 100644 index 0000000..2409b25 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/NextMarker.cs @@ -0,0 +1,44 @@ +// +// +// +// +// $Revision$ +// + +using System.Xml; + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// Used for mark next token + /// + public class NextMarker + { + /// + /// Creates a new instance of + /// + public NextMarker(XmlElement mark) + { + Color = new HighlightColor(mark); + What = mark.InnerText; + if (mark.Attributes["markmarker"] != null) + MarkMarker = bool.Parse(mark.Attributes["markmarker"].InnerText); + } + + /// + /// String value to indicate to mark next token + /// + public string What { get; } + + /// + /// Color for marking next token + /// + public HighlightColor Color { get; } + + /// + /// If true the indication text will be marked with the same color + /// too + /// + public bool MarkMarker { get; } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/PrevMarker.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/PrevMarker.cs new file mode 100644 index 0000000..6aa0e22 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/PrevMarker.cs @@ -0,0 +1,44 @@ +// +// +// +// +// $Revision$ +// + +using System.Xml; + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// Used for mark previous token + /// + public class PrevMarker + { + /// + /// Creates a new instance of + /// + public PrevMarker(XmlElement mark) + { + Color = new HighlightColor(mark); + What = mark.InnerText; + if (mark.Attributes["markmarker"] != null) + MarkMarker = bool.Parse(mark.Attributes["markmarker"].InnerText); + } + + /// + /// String value to indicate to mark previous token + /// + public string What { get; } + + /// + /// Color for marking previous token + /// + public HighlightColor Color { get; } + + /// + /// If true the indication text will be marked with the same color + /// too + /// + public bool MarkMarker { get; } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/Span.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/Span.cs new file mode 100644 index 0000000..034364c --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/Span.cs @@ -0,0 +1,91 @@ +// +// +// +// +// $Revision$ +// + +using System.Xml; + +namespace ICSharpCode.TextEditor.Document +{ + public sealed class Span + { + private readonly HighlightColor beginColor; + private readonly HighlightColor endColor; + + public Span(XmlElement span) + { + Color = new HighlightColor(span); + + if (span.HasAttribute("rule")) + Rule = span.GetAttribute("rule"); + + if (span.HasAttribute("escapecharacter")) + EscapeCharacter = span.GetAttribute("escapecharacter")[index: 0]; + + Name = span.GetAttribute("name"); + if (span.HasAttribute("stopateol")) + StopEOL = bool.Parse(span.GetAttribute("stopateol")); + + Begin = span["Begin"].InnerText.ToCharArray(); + beginColor = new HighlightColor(span["Begin"], Color); + + if (span["Begin"].HasAttribute("singleword")) + IsBeginSingleWord = bool.Parse(span["Begin"].GetAttribute("singleword")); + if (span["Begin"].HasAttribute("startofline")) + IsBeginStartOfLine = bool.Parse(span["Begin"].GetAttribute("startofline")); + + if (span["End"] != null) + { + End = span["End"].InnerText.ToCharArray(); + endColor = new HighlightColor(span["End"], Color); + if (span["End"].HasAttribute("singleword")) + IsEndSingleWord = bool.Parse(span["End"].GetAttribute("singleword")); + } + } + + internal HighlightRuleSet RuleSet { get; set; } + + public bool IgnoreCase { get; set; } + + public bool StopEOL { get; } + + public bool? IsBeginStartOfLine { get; } + + public bool IsBeginSingleWord { get; } + + public bool IsEndSingleWord { get; } + + public HighlightColor Color { get; } + + public HighlightColor BeginColor + { + get + { + if (beginColor != null) + return beginColor; + + return Color; + } + } + + public HighlightColor EndColor => endColor ?? Color; + + public char[] Begin { get; } + + public char[] End { get; } + + public string Name { get; } + + public string Rule { get; } + + /// + /// Gets the escape character of the span. The escape character is a character that can be used in front + /// of the span end to make it not end the span. The escape character followed by another escape character + /// means the escape character was escaped like in @"a "" b" literals in C#. + /// The default value '\0' means no escape character is allowed. + /// + public char EscapeCharacter { get; } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/SpanStack.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/SpanStack.cs new file mode 100644 index 0000000..d857b91 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/SpanStack.cs @@ -0,0 +1,110 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// A stack of Span instances. Works like Stack<Span>, but can be cloned quickly + /// because it is implemented as linked list. + /// + public sealed class SpanStack : ICloneable, IEnumerable + { + private StackNode top; + + public bool IsEmpty => top == null; + + object ICloneable.Clone() + { + return Clone(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public Span Pop() + { + var s = top.Data; + top = top.Previous; + return s; + } + + public Span Peek() + { + return top.Data; + } + + public void Push(Span s) + { + top = new StackNode(top, s); + } + + public SpanStack Clone() + { + var n = new SpanStack(); + n.top = top; + return n; + } + + public Enumerator GetEnumerator() + { + return new Enumerator(new StackNode(top, data: null)); + } + + internal sealed class StackNode + { + public readonly Span Data; + public readonly StackNode Previous; + + public StackNode(StackNode previous, Span data) + { + Previous = previous; + Data = data; + } + } + + public struct Enumerator : IEnumerator + { + private StackNode c; + + internal Enumerator(StackNode node) + { + c = node; + } + + public Span Current => c.Data; + + object IEnumerator.Current => c.Data; + + public void Dispose() + { + c = null; + } + + public bool MoveNext() + { + c = c.Previous; + return c != null; + } + + public void Reset() + { + throw new NotSupportedException(); + } + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/SyntaxModes/FileSyntaxModeProvider.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/SyntaxModes/FileSyntaxModeProvider.cs new file mode 100644 index 0000000..b5ff04d --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/SyntaxModes/FileSyntaxModeProvider.cs @@ -0,0 +1,84 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Xml; + +namespace ICSharpCode.TextEditor.Document +{ + public class FileSyntaxModeProvider : ISyntaxModeFileProvider + { + private readonly string directory; + private List syntaxModes; + + public FileSyntaxModeProvider(string directory) + { + this.directory = directory; + UpdateSyntaxModeList(); + } + + public ICollection SyntaxModes => syntaxModes; + + public void UpdateSyntaxModeList() + { + var syntaxModeFile = Path.Combine(directory, "SyntaxModes.xml"); + + if (File.Exists(syntaxModeFile)) + { + using (var s = File.OpenRead(syntaxModeFile)) + { + syntaxModes = SyntaxMode.GetSyntaxModes(s); + } + } + else + { + syntaxModes = ScanDirectory(directory); + } + } + + public XmlTextReader GetSyntaxModeFile(SyntaxMode syntaxMode) + { + var syntaxModeFile = Path.Combine(directory, syntaxMode.FileName); + if (!File.Exists(syntaxModeFile)) + throw new HighlightingDefinitionInvalidException("Can't load highlighting definition " + syntaxModeFile + " (file not found)!"); + return new XmlTextReader(File.OpenRead(syntaxModeFile)); + } + + private static List ScanDirectory(string directory) + { + var files = Directory.GetFiles(directory); + var modes = new List(); + foreach (var file in files) + if (Path.GetExtension(file).Equals(".XSHD", StringComparison.OrdinalIgnoreCase)) + { + var reader = new XmlTextReader(file); + while (reader.Read()) + if (reader.NodeType == XmlNodeType.Element) + switch (reader.Name) + { + case "SyntaxDefinition": + var name = reader.GetAttribute("name"); + var extensions = reader.GetAttribute("extensions"); + modes.Add( + new SyntaxMode( + Path.GetFileName(file), + name, + extensions)); + goto bailout; + default: + throw new HighlightingDefinitionInvalidException("Unknown root node in syntax highlighting file :" + reader.Name); + } + bailout: + reader.Close(); + } + + return modes; + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/SyntaxModes/ISyntaxModeFileProvider.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/SyntaxModes/ISyntaxModeFileProvider.cs new file mode 100644 index 0000000..0deff29 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/SyntaxModes/ISyntaxModeFileProvider.cs @@ -0,0 +1,20 @@ +// +// +// +// +// $Revision$ +// + +using System.Collections.Generic; +using System.Xml; + +namespace ICSharpCode.TextEditor.Document +{ + public interface ISyntaxModeFileProvider + { + ICollection SyntaxModes { get; } + + XmlTextReader GetSyntaxModeFile(SyntaxMode syntaxMode); + void UpdateSyntaxModeList(); + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/SyntaxModes/ResourceSyntaxModeProvider.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/SyntaxModes/ResourceSyntaxModeProvider.cs new file mode 100644 index 0000000..c3a4675 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/SyntaxModes/ResourceSyntaxModeProvider.cs @@ -0,0 +1,41 @@ +// +// +// +// +// $Revision$ +// + +using System.Collections.Generic; +using System.Xml; + +namespace ICSharpCode.TextEditor.Document +{ + public class ResourceSyntaxModeProvider : ISyntaxModeFileProvider + { + private readonly List syntaxModes; + + public ResourceSyntaxModeProvider() + { + var assembly = typeof(SyntaxMode).Assembly; + var syntaxModeStream = assembly.GetManifestResourceStream("ICSharpCode.TextEditor.Resources.SyntaxModes.xml"); + if (syntaxModeStream != null) + syntaxModes = SyntaxMode.GetSyntaxModes(syntaxModeStream); + else + syntaxModes = new List(); + } + + public ICollection SyntaxModes => syntaxModes; + + public XmlTextReader GetSyntaxModeFile(SyntaxMode syntaxMode) + { + var assembly = typeof(SyntaxMode).Assembly; + var stream = assembly.GetManifestResourceStream($"ICSharpCode.TextEditor.Resources.{syntaxMode.FileName}"); + return new XmlTextReader(stream); + } + + public void UpdateSyntaxModeList() + { + // resources don't change during runtime + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/SyntaxModes/SyntaxMode.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/SyntaxModes/SyntaxMode.cs new file mode 100644 index 0000000..2274847 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/SyntaxModes/SyntaxMode.cs @@ -0,0 +1,76 @@ +// +// +// +// +// $Revision$ +// + +using System.Collections.Generic; +using System.IO; +using System.Xml; + +namespace ICSharpCode.TextEditor.Document +{ + public class SyntaxMode + { + public SyntaxMode(string fileName, string name, string extensions) + { + FileName = fileName; + Name = name; + Extensions = extensions.Split(';', '|', ','); + } + + public SyntaxMode(string fileName, string name, string[] extensions) + { + FileName = fileName; + Name = name; + Extensions = extensions; + } + + public string FileName { get; } + public string Name { get; } + public string[] Extensions { get; } + + public static List GetSyntaxModes(Stream xmlSyntaxModeStream) + { + using (var reader = new XmlTextReader(xmlSyntaxModeStream)) + { + var syntaxModes = new List(); + + while (reader.Read()) + { + switch (reader.NodeType) + { + case XmlNodeType.Element: + switch (reader.Name) + { + case "SyntaxModes": + var version = reader.GetAttribute("version"); + if (version != "1.0") + throw new HighlightingDefinitionInvalidException("Unknown syntax mode file defininition with version " + version); + break; + case "Mode": + syntaxModes.Add( + new SyntaxMode( + reader.GetAttribute("file"), + reader.GetAttribute("name"), + reader.GetAttribute("extensions"))); + break; + default: + throw new HighlightingDefinitionInvalidException("Unknown node in syntax mode file :" + reader.Name); + } + + break; + } + } + + return syntaxModes; + } + } + + public override string ToString() + { + return string.Format("[SyntaxMode: FileName={0}, Name={1}, Extensions=({2})]", FileName, Name, string.Join(",", Extensions)); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/TextWord.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/TextWord.cs new file mode 100644 index 0000000..231be0e --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/HighlightingStrategy/TextWord.cs @@ -0,0 +1,192 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Diagnostics; +using System.Drawing; + +namespace ICSharpCode.TextEditor.Document +{ + public enum TextWordType + { + Word, + Space, + Tab + } + + /// + /// This class represents single words with color information, two special versions of a word are + /// spaces and tabs. + /// + public class TextWord + { + private readonly IDocument document; + private readonly LineSegment line; + private HighlightColor color; + + protected TextWord() + { + } + + // TAB + public TextWord(IDocument document, LineSegment line, int offset, int length, HighlightColor color, bool hasDefaultColor) + { + Debug.Assert(document != null); + Debug.Assert(line != null); + Debug.Assert(color != null); + + this.document = document; + this.line = line; + Offset = offset; + Length = length; + this.color = color; + HasDefaultColor = hasDefaultColor; + } + + public static TextWord Space { get; } = new SpaceTextWord(); + + public static TextWord Tab { get; } = new TabTextWord(); + + public int Offset { get; } + + public int Length { get; private set; } + + public bool HasDefaultColor { get; } + + public virtual TextWordType Type => TextWordType.Word; + + public string Word + { + get + { + if (document == null) + return string.Empty; + return document.GetText(line.Offset + Offset, Length); + } + } + + public Color Color + { + get + { + if (color == null) + return SystemColors.WindowText; + return color.Color; + } + } + + public bool Bold + { + get + { + if (color == null) + return false; + return color.Bold; + } + } + + public bool Italic + { + get + { + if (color == null) + return false; + return color.Italic; + } + } + + public HighlightColor SyntaxColor + { + get => color; + set + { + Debug.Assert(value != null); + color = value; + } + } + + public virtual bool IsWhiteSpace => false; + + /// + /// Splits the into two parts: the part before is assigned to + /// the reference parameter , the part after is returned. + /// + public static TextWord Split(ref TextWord word, int pos) + { +#if DEBUG + if (word.Type != TextWordType.Word) + throw new ArgumentException("word.Type must be Word"); + if (pos <= 0) + throw new ArgumentOutOfRangeException(nameof(pos), pos, "pos must be > 0"); + if (pos >= word.Length) + throw new ArgumentOutOfRangeException(nameof(pos), pos, "pos must be < word.Length"); +#endif + var after = new TextWord(word.document, word.line, word.Offset + pos, word.Length - pos, word.color, word.HasDefaultColor); + word = new TextWord(word.document, word.line, word.Offset, pos, word.color, word.HasDefaultColor); + return after; + } + + public virtual Font GetFont(FontContainer fontContainer) + { + return color.GetFont(fontContainer); + } + + /// + /// Converts a instance to string (for debug purposes) + /// + public override string ToString() + { + return "[TextWord: Word = " + Word + ", Color = " + Color + "]"; + } + + public sealed class SpaceTextWord : TextWord + { + public SpaceTextWord() + { + Length = 1; + } + + public SpaceTextWord(HighlightColor color) + { + Length = 1; + SyntaxColor = color; + } + + public override TextWordType Type => TextWordType.Space; + + public override bool IsWhiteSpace => true; + + public override Font GetFont(FontContainer fontContainer) + { + return null; + } + } + + public sealed class TabTextWord : TextWord + { + public TabTextWord() + { + Length = 1; + } + + public TabTextWord(HighlightColor color) + { + Length = 1; + SyntaxColor = color; + } + + public override TextWordType Type => TextWordType.Tab; + + public override bool IsWhiteSpace => true; + + public override Font GetFont(FontContainer fontContainer) + { + return null; + } + } + } +} diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/IDocument.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/IDocument.cs new file mode 100644 index 0000000..8fd09fa --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/IDocument.cs @@ -0,0 +1,289 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using ICSharpCode.TextEditor.Undo; + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// This interface represents a container which holds a text sequence and + /// all necessary information about it. It is used as the base for a text editor. + /// + public interface IDocument + { + ITextEditorProperties TextEditorProperties { get; set; } + + UndoStack UndoStack { get; } + + /// + /// If true the document can't be altered + /// + bool ReadOnly { get; set; } + + /// + /// The attached to the instance + /// + IFormattingStrategy FormattingStrategy { get; set; } + + /// + /// The attached to the instance + /// + ITextBufferStrategy TextBufferStrategy { get; } + + /// + /// The attached to the instance + /// + FoldingManager FoldingManager { get; } + + /// + /// The attached to the instance + /// + IHighlightingStrategy HighlightingStrategy { get; set; } + + /// + /// The attached to the instance + /// + BookmarkManager BookmarkManager { get; } + + MarkerStrategy MarkerStrategy { get; } + + /// + /// A container where all TextAreaUpdate objects get stored + /// + List UpdateQueue { get; } + + string GetText(ISegment segment); + + /// + /// Requests an update of the textarea + /// + void RequestUpdate(TextAreaUpdate update); + + /// + /// Commits all updates in the queue to the textarea (the + /// textarea will be painted) + /// + void CommitUpdate(); + + /// + /// Moves, Resizes, Removes a list of segments on insert/remove/replace events. + /// + void UpdateSegmentListOnDocumentChange(List list, DocumentEventArgs e) where T : ISegment; + + /// + /// Is fired when CommitUpdate is called + /// + event EventHandler UpdateCommited; + + /// + /// + event DocumentEventHandler DocumentAboutToBeChanged; + + /// + /// + event DocumentEventHandler DocumentChanged; + + event EventHandler TextContentChanged; + +// /// +// /// The attached to the instance +// /// +// SelectionManager SelectionManager { +// get; +// } + + #region ILineManager interface + + /// + /// A collection of all line segments + /// + /// + /// The collection should only be used if you're aware + /// of the 'last line ends with a delimiter problem'. Otherwise + /// the method should be used. + /// + IList LineSegmentCollection { get; } + + /// + /// The total number of lines in the document. + /// + int TotalNumberOfLines { get; } + + /// + /// Returns a valid line number for the given offset. + /// + /// + /// A offset which points to a character in the line which + /// line number is returned. + /// + /// + /// An int which value is the line number. + /// + /// If offset points not to a valid position + int GetLineNumberForOffset(int offset); + + /// + /// Returns a for the given offset. + /// + /// + /// A offset which points to a character in the line which + /// is returned. + /// + /// + /// A object. + /// + /// If offset points not to a valid position + LineSegment GetLineSegmentForOffset(int offset); + + /// + /// Returns a for the given line number. + /// This function should be used to get a line instead of getting the + /// line using the list. + /// + /// + /// The line number which is requested. + /// + /// + /// A object. + /// + /// If offset points not to a valid position + LineSegment GetLineSegment(int lineNumber); + + /// + /// Get the first logical line for a given visible line. + /// example : lineNumber == 100 foldings are in the linetracker + /// between 0..1 (2 folded, invisible lines) this method returns 102 + /// the 'logical' line number + /// + int GetFirstLogicalLine(int lineNumber); + + /// + /// Get the last logical line for a given visible line. + /// example : lineNumber == 100 foldings are in the linetracker + /// between 0..1 (2 folded, invisible lines) this method returns 102 + /// the 'logical' line number + /// + int GetLastLogicalLine(int lineNumber); + + /// + /// Get the visible line for a given logical line. + /// example : lineNumber == 100 foldings are in the linetracker + /// between 0..1 (2 folded, invisible lines) this method returns 98 + /// the 'visible' line number + /// + int GetVisibleLine(int lineNumber); + +// /// +// /// Get the visible column for a given logical line and logical column. +// /// +// int GetVisibleColumn(int logicalLine, int logicalColumn); + + /// + /// Get the next visible line after lineNumber + /// + int GetNextVisibleLineAbove(int lineNumber, int lineCount); + + /// + /// Get the next visible line below lineNumber + /// + int GetNextVisibleLineBelow(int lineNumber, int lineCount); + + event EventHandler LineLengthChanged; + event EventHandler LineCountChanged; + event EventHandler LineDeleted; + + #endregion + + #region ITextBufferStrategy interface + + /// + /// Get the whole text as string. + /// When setting the text using the TextContent property, the undo stack is cleared. + /// Set TextContent only for actions such as loading a file; if you want to change the current document + /// use the Replace method instead. + /// + string TextContent { get; set; } + + /// + /// The current length of the sequence of characters that can be edited. + /// + int TextLength { get; } + + /// + /// Inserts a string of characters into the sequence. + /// + /// + /// offset where to insert the string. + /// + /// + /// text to be inserted. + /// + void Insert(int offset, string text); + + /// + /// Removes some portion of the sequence. + /// + /// + /// offset of the remove. + /// + /// + /// number of characters to remove. + /// + void Remove(int offset, int length); + + /// + /// Replace some portion of the sequence. + /// + /// + /// offset. + /// + /// + /// number of characters to replace. + /// + /// + /// text to be replaced with. + /// + void Replace(int offset, int length, string text); + + /// + /// Returns a specific char of the sequence. + /// + /// + /// Offset of the char to get. + /// + char GetCharAt(int offset); + + /// + /// Fetches a string of characters contained in the sequence. + /// + /// + /// Offset into the sequence to fetch + /// + /// + /// number of characters to copy. + /// + string GetText(int offset, int length); + + #endregion + + #region ITextModel interface + + /// + /// returns the logical line/column position from an offset + /// + TextLocation OffsetToPosition(int offset); + + /// + /// returns the offset from a logical line/column position + /// + int PositionToOffset(TextLocation p); + + #endregion + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/ISegment.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/ISegment.cs new file mode 100644 index 0000000..fc24f85 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/ISegment.cs @@ -0,0 +1,53 @@ +// +// +// +// +// $Revision$ +// + +using System.Collections.Generic; + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// This interface is used to describe a span inside a text sequence + /// + public interface ISegment + { + /// + /// The offset where the span begins + /// + int Offset { get; set; } + + /// + /// The length of the span + /// + int Length { get; set; } + } + + public class SegmentComparer : IComparer + { + public int Compare(ISegment x, ISegment y) + { + if (x == null) + { + if (y == null) + return 0; + + // If x is null and y is not null, y + // is greater. + return -1; + } + + // If x is not null and y is null, x is greater. + if (y == null) + return 1; + + var retval = x.Offset.CompareTo(y.Offset); + if (retval != 0) + return retval; + + return x.Length.CompareTo(y.Length); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/ITextEditorProperties.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/ITextEditorProperties.cs new file mode 100644 index 0000000..371d3b5 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/ITextEditorProperties.cs @@ -0,0 +1,184 @@ +// +// +// +// +// $Revision$ +// + +using System.Drawing; +using System.Drawing.Text; +using System.Text; + +namespace ICSharpCode.TextEditor.Document +{ + public interface ITextEditorProperties + { + bool CaretLine { get; set; } + + bool AutoInsertCurlyBracket + { + // is wrapped in text editor control + get; + set; + } + + bool HideMouseCursor + { + // is wrapped in text editor control + get; + set; + } + + bool IsIconBarVisible + { + // is wrapped in text editor control + get; + set; + } + + bool AllowCaretBeyondEOL { get; set; } + + bool ShowMatchingBracket + { + // is wrapped in text editor control + get; + set; + } + + bool CutCopyWholeLine { get; set; } + + TextRenderingHint TextRenderingHint + { + // is wrapped in text editor control + get; + set; + } + + bool MouseWheelScrollDown { get; set; } + + bool MouseWheelTextZoom { get; set; } + + string LineTerminator { get; set; } + + LineViewerStyle LineViewerStyle + { + // is wrapped in text editor control + get; + set; + } + + bool ShowInvalidLines + { + // is wrapped in text editor control + get; + set; + } + + int VerticalRulerRow + { + // is wrapped in text editor control + get; + set; + } + + bool ShowSpaces + { + // is wrapped in text editor control + get; + set; + } + + bool ShowTabs + { + // is wrapped in text editor control + get; + set; + } + + bool ShowEOLMarker + { + // is wrapped in text editor control + get; + set; + } + + bool ConvertTabsToSpaces + { + // is wrapped in text editor control + get; + set; + } + + bool ShowHorizontalRuler + { + // is wrapped in text editor control + get; + set; + } + + bool ShowVerticalRuler + { + // is wrapped in text editor control + get; + set; + } + + Encoding Encoding { get; set; } + + bool EnableFolding + { + // is wrapped in text editor control + get; + set; + } + + bool ShowLineNumbers + { + // is wrapped in text editor control + get; + set; + } + + /// + /// The width of a tab. + /// + int TabIndent + { + // is wrapped in text editor control + get; + set; + } + + /// + /// The amount of spaces a tab is converted to if ConvertTabsToSpaces is true. + /// + int IndentationSize { get; set; } + + IndentStyle IndentStyle + { + // is wrapped in text editor control + get; + set; + } + + DocumentSelectionMode DocumentSelectionMode { get; set; } + + Font Font + { + // is wrapped in text editor control + get; + set; + } + + FontContainer FontContainer { get; } + + BracketMatchingStyle BracketMatchingStyle + { + // is wrapped in text editor control + get; + set; + } + + bool SupportReadOnlySegments { get; set; } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/LineManager/DeferredEventList.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/LineManager/DeferredEventList.cs new file mode 100644 index 0000000..32af07e --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/LineManager/DeferredEventList.cs @@ -0,0 +1,42 @@ +// +// +// +// +// $Revision$ +// + +using System.Collections.Generic; + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// A list of events that are fired after the line manager has finished working. + /// + internal struct DeferredEventList + { + internal List removedLines; + internal List textAnchor; + + public void AddRemovedLine(LineSegment line) + { + if (removedLines == null) + removedLines = new List(); + removedLines.Add(line); + } + + public void AddDeletedAnchor(TextAnchor anchor) + { + if (textAnchor == null) + textAnchor = new List(); + textAnchor.Add(anchor); + } + + public void RaiseEvents() + { + // removedLines is raised by the LineManager + if (textAnchor != null) + foreach (var a in textAnchor) + a.RaiseDeleted(); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/LineManager/LineManager.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/LineManager/LineManager.cs new file mode 100644 index 0000000..bf8f477 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/LineManager/LineManager.cs @@ -0,0 +1,359 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace ICSharpCode.TextEditor.Document +{ + internal sealed class LineManager + { + // use always the same DelimiterSegment object for the NextDelimiter + private readonly DelimiterSegment delimiterSegment = new DelimiterSegment(); + + private readonly IDocument document; + private readonly LineSegmentTree lineCollection = new LineSegmentTree(); + private IHighlightingStrategy highlightingStrategy; + + public LineManager(IDocument document, IHighlightingStrategy highlightingStrategy) + { + this.document = document; + this.highlightingStrategy = highlightingStrategy; + } + + public IList LineSegmentCollection => lineCollection; + + public int TotalNumberOfLines => lineCollection.Count; + + public IHighlightingStrategy HighlightingStrategy + { + get => highlightingStrategy; + set + { + if (highlightingStrategy != value) + { + highlightingStrategy = value; + highlightingStrategy?.MarkTokens(document); + } + } + } + + public int GetLineNumberForOffset(int offset) + { + return GetLineSegmentForOffset(offset).LineNumber; + } + + public LineSegment GetLineSegmentForOffset(int offset) + { + return lineCollection.GetByOffset(offset); + } + + public LineSegment GetLineSegment(int lineNr) + { + return lineCollection[lineNr]; + } + + public void Insert(int offset, string text) + { + Replace(offset, length: 0, text); + } + + public void Remove(int offset, int length) + { + Replace(offset, length, string.Empty); + } + + public void Replace(int offset, int length, string text) + { + //Debug.WriteLine("Replace offset=" + offset + " length=" + length + " text.Length=" + text.Length); + var lineStart = GetLineNumberForOffset(offset); + var oldNumberOfLines = TotalNumberOfLines; + var deferredEventList = new DeferredEventList(); + RemoveInternal(ref deferredEventList, offset, length); + var numberOfLinesAfterRemoving = TotalNumberOfLines; + if (!string.IsNullOrEmpty(text)) + InsertInternal(offset, text); +// #if DEBUG +// Console.WriteLine("New line collection:"); +// Console.WriteLine(lineCollection.GetTreeAsString()); +// Console.WriteLine("New text:"); +// Console.WriteLine("'" + document.TextContent + "'"); +// #endif + // Only fire events after RemoveInternal+InsertInternal finished completely: + // Otherwise we would expose inconsistent state to the event handlers. + RunHighlighter(lineStart, 1 + Math.Max(val1: 0, TotalNumberOfLines - numberOfLinesAfterRemoving)); + + if (deferredEventList.removedLines != null) + foreach (var ls in deferredEventList.removedLines) + OnLineDeleted(new LineEventArgs(document, ls)); + deferredEventList.RaiseEvents(); + if (TotalNumberOfLines != oldNumberOfLines) + OnLineCountChanged(new LineCountChangeEventArgs(document, lineStart, TotalNumberOfLines - oldNumberOfLines)); + } + + private void RemoveInternal(ref DeferredEventList deferredEventList, int offset, int length) + { + Debug.Assert(length >= 0); + if (length == 0) return; + var it = lineCollection.GetEnumeratorForOffset(offset); + var startSegment = it.Current; + var startSegmentOffset = startSegment.Offset; + if (offset + length < startSegmentOffset + startSegment.TotalLength) + { + // just removing a part of this line segment + startSegment.RemovedLinePart(ref deferredEventList, offset - startSegmentOffset, length); + SetSegmentLength(startSegment, startSegment.TotalLength - length); + return; + } + + // merge startSegment with another line segment because startSegment's delimiter was deleted + // possibly remove lines in between if multiple delimiters were deleted + var charactersRemovedInStartLine = startSegmentOffset + startSegment.TotalLength - offset; + Debug.Assert(charactersRemovedInStartLine > 0); + startSegment.RemovedLinePart(ref deferredEventList, offset - startSegmentOffset, charactersRemovedInStartLine); + + var endSegment = lineCollection.GetByOffset(offset + length); + if (endSegment == startSegment) + { + // special case: we are removing a part of the last line up to the + // end of the document + SetSegmentLength(startSegment, startSegment.TotalLength - length); + return; + } + + var endSegmentOffset = endSegment.Offset; + var charactersLeftInEndLine = endSegmentOffset + endSegment.TotalLength - (offset + length); + endSegment.RemovedLinePart(ref deferredEventList, startColumn: 0, endSegment.TotalLength - charactersLeftInEndLine); + startSegment.MergedWith(endSegment, offset - startSegmentOffset); + SetSegmentLength(startSegment, startSegment.TotalLength - charactersRemovedInStartLine + charactersLeftInEndLine); + startSegment.DelimiterLength = endSegment.DelimiterLength; + // remove all segments between startSegment (excl.) and endSegment (incl.) + it.MoveNext(); + LineSegment segmentToRemove; + do + { + segmentToRemove = it.Current; + it.MoveNext(); + lineCollection.RemoveSegment(segmentToRemove); + segmentToRemove.Deleted(ref deferredEventList); + } while (segmentToRemove != endSegment); + } + + private void InsertInternal(int offset, string text) + { + var segment = lineCollection.GetByOffset(offset); + var ds = NextDelimiter(text, offset: 0); + if (ds == null) + { + // no newline is being inserted, all text is inserted in a single line + segment.InsertedLinePart(offset - segment.Offset, text.Length); + SetSegmentLength(segment, segment.TotalLength + text.Length); + return; + } + + var firstLine = segment; + firstLine.InsertedLinePart(offset - firstLine.Offset, ds.Offset); + var lastDelimiterEnd = 0; + while (ds != null) + { + // split line segment at line delimiter + var lineBreakOffset = offset + ds.Offset + ds.Length; + var segmentOffset = segment.Offset; + var lengthAfterInsertionPos = segmentOffset + segment.TotalLength - (offset + lastDelimiterEnd); + lineCollection.SetSegmentLength(segment, lineBreakOffset - segmentOffset); + var newSegment = lineCollection.InsertSegmentAfter(segment, lengthAfterInsertionPos); + segment.DelimiterLength = ds.Length; + + segment = newSegment; + lastDelimiterEnd = ds.Offset + ds.Length; + + ds = NextDelimiter(text, lastDelimiterEnd); + } + + firstLine.SplitTo(segment); + // insert rest after last delimiter + if (lastDelimiterEnd != text.Length) + { + segment.InsertedLinePart(startColumn: 0, text.Length - lastDelimiterEnd); + SetSegmentLength(segment, segment.TotalLength + text.Length - lastDelimiterEnd); + } + } + + private void SetSegmentLength(LineSegment segment, int newTotalLength) + { + var delta = newTotalLength - segment.TotalLength; + if (delta != 0) + { + lineCollection.SetSegmentLength(segment, newTotalLength); + OnLineLengthChanged(new LineLengthChangeEventArgs(document, segment, delta)); + } + } + + private void RunHighlighter(int firstLine, int lineCount) + { + if (highlightingStrategy != null) + { + var markLines = new List(); + var it = lineCollection.GetEnumeratorForIndex(firstLine); + for (var i = 0; i < lineCount && it.IsValid; i++) + { + markLines.Add(it.Current); + it.MoveNext(); + } + + highlightingStrategy.MarkTokens(document, markLines); + } + } + + public void SetContent(string text) + { + lineCollection.Clear(); + if (text != null) + Replace(offset: 0, length: 0, text); + } + + public int GetVisibleLine(int logicalLineNumber) + { + if (!document.TextEditorProperties.EnableFolding) + return logicalLineNumber; + + var visibleLine = 0; + var foldEnd = 0; + var foldings = document.FoldingManager.GetTopLevelFoldedFoldings(); + foreach (var fm in foldings) + { + if (fm.StartLine >= logicalLineNumber) + break; + if (fm.StartLine >= foldEnd) + { + visibleLine += fm.StartLine - foldEnd; + if (fm.EndLine > logicalLineNumber) + return visibleLine; + foldEnd = fm.EndLine; + } + } + +// Debug.Assert(logicalLineNumber >= foldEnd); + visibleLine += logicalLineNumber - foldEnd; + return visibleLine; + } + + public int GetFirstLogicalLine(int visibleLineNumber) + { + if (!document.TextEditorProperties.EnableFolding) + return visibleLineNumber; + var v = 0; + var foldEnd = 0; + var foldings = document.FoldingManager.GetTopLevelFoldedFoldings(); + foreach (var fm in foldings) + if (fm.StartLine >= foldEnd) + { + if (v + fm.StartLine - foldEnd >= visibleLineNumber) + break; + v += fm.StartLine - foldEnd; + foldEnd = fm.EndLine; + } + + // help GC + foldings.Clear(); + return foldEnd + visibleLineNumber - v; + } + + public int GetLastLogicalLine(int visibleLineNumber) + { + if (!document.TextEditorProperties.EnableFolding) + return visibleLineNumber; + return GetFirstLogicalLine(visibleLineNumber + 1) - 1; + } + + // TODO : speedup the next/prev visible line search + // HOW? : save the foldings in a sorted list and lookup the + // line numbers in this list + public int GetNextVisibleLineAbove(int lineNumber, int lineCount) + { + var curLineNumber = lineNumber; + if (document.TextEditorProperties.EnableFolding) + for (var i = 0; i < lineCount && curLineNumber < TotalNumberOfLines; ++i) + { + ++curLineNumber; + while (curLineNumber < TotalNumberOfLines && (curLineNumber >= lineCollection.Count || !document.FoldingManager.IsLineVisible(curLineNumber))) + ++curLineNumber; + } + else + curLineNumber += lineCount; + + return Math.Min(TotalNumberOfLines - 1, curLineNumber); + } + + public int GetNextVisibleLineBelow(int lineNumber, int lineCount) + { + var curLineNumber = lineNumber; + if (document.TextEditorProperties.EnableFolding) + for (var i = 0; i < lineCount; ++i) + { + --curLineNumber; + while (curLineNumber >= 0 && !document.FoldingManager.IsLineVisible(curLineNumber)) + --curLineNumber; + } + else + curLineNumber -= lineCount; + + return Math.Max(val1: 0, curLineNumber); + } + + private DelimiterSegment NextDelimiter(string text, int offset) + { + for (var i = offset; i < text.Length; i++) + switch (text[i]) + { + case '\r': + if (i + 1 < text.Length) + if (text[i + 1] == '\n') + { + delimiterSegment.Offset = i; + delimiterSegment.Length = 2; + return delimiterSegment; + } +#if DATACONSISTENCYTEST + Debug.Assert(condition: false, "Found lone \\r, data consistency problems?"); +#endif + goto case '\n'; + case '\n': + delimiterSegment.Offset = i; + delimiterSegment.Length = 1; + return delimiterSegment; + } + return null; + } + + private void OnLineCountChanged(LineCountChangeEventArgs e) + { + LineCountChanged?.Invoke(this, e); + } + + private void OnLineLengthChanged(LineLengthChangeEventArgs e) + { + LineLengthChanged?.Invoke(this, e); + } + + private void OnLineDeleted(LineEventArgs e) + { + LineDeleted?.Invoke(this, e); + } + + public event EventHandler LineLengthChanged; + public event EventHandler LineCountChanged; + public event EventHandler LineDeleted; + + private sealed class DelimiterSegment + { + internal int Length; + internal int Offset; + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/LineManager/LineManagerEventArgs.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/LineManager/LineManagerEventArgs.cs new file mode 100644 index 0000000..efbc1e7 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/LineManager/LineManagerEventArgs.cs @@ -0,0 +1,70 @@ +// +// +// +// +// $Revision$ +// + +using System; + +namespace ICSharpCode.TextEditor.Document +{ + public class LineCountChangeEventArgs : EventArgs + { + public LineCountChangeEventArgs(IDocument document, int lineStart, int linesMoved) + { + Document = document; + LineStart = lineStart; + LinesMoved = linesMoved; + } + + /// + /// always a valid Document which is related to the Event. + /// + public IDocument Document { get; } + + /// + /// -1 if no offset was specified for this event + /// + public int LineStart { get; } + + /// + /// -1 if no length was specified for this event + /// + public int LinesMoved { get; } + } + + public class LineEventArgs : EventArgs + { + public LineEventArgs(IDocument document, LineSegment lineSegment) + { + Document = document; + LineSegment = lineSegment; + } + + public IDocument Document { get; } + + public LineSegment LineSegment { get; } + + public override string ToString() + { + return string.Format("[LineEventArgs Document={0} LineSegment={1}]", Document, LineSegment); + } + } + + public class LineLengthChangeEventArgs : LineEventArgs + { + public LineLengthChangeEventArgs(IDocument document, LineSegment lineSegment, int moved) + : base(document, lineSegment) + { + LengthDelta = moved; + } + + public int LengthDelta { get; } + + public override string ToString() + { + return string.Format("[LineLengthEventArgs Document={0} LineSegment={1} LengthDelta={2}]", Document, LineSegment, LengthDelta); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/LineManager/LineSegment.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/LineManager/LineSegment.cs new file mode 100644 index 0000000..059f9ea --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/LineManager/LineSegment.cs @@ -0,0 +1,234 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using ICSharpCode.TextEditor.Util; + +namespace ICSharpCode.TextEditor.Document +{ + public sealed class LineSegment : ISegment + { + internal LineSegmentTree.Enumerator treeEntry; + + public bool IsDeleted => !treeEntry.IsValid; + + public int LineNumber => treeEntry.CurrentIndex; + + public int Offset => treeEntry.CurrentOffset; + + public int Length => TotalLength - DelimiterLength; + + public int TotalLength { get; internal set; } + + public int DelimiterLength { get; internal set; } + + // highlighting information + public List Words { get; set; } + + public SpanStack HighlightSpanStack { get; set; } + + int ISegment.Offset + { + get => Offset; + set => throw new NotSupportedException(); + } + + int ISegment.Length + { + get => Length; + set => throw new NotSupportedException(); + } + + public TextWord GetWord(int column) + { + var curColumn = 0; + foreach (var word in Words) + { + if (column < curColumn + word.Length) + return word; + curColumn += word.Length; + } + + return null; + } + + public HighlightColor GetColorForPosition(int x) + { + if (Words != null) + { + var xPos = 0; + foreach (var word in Words) + { + if (x < xPos + word.Length) + return word.SyntaxColor; + xPos += word.Length; + } + } + + return new HighlightColor(SystemColors.WindowText, bold: false, italic: false); + } + + /// + /// Converts a instance to string (for debug purposes) + /// + public override string ToString() + { + if (IsDeleted) + return "[LineSegment: (deleted) Length = " + Length + ", TotalLength = " + TotalLength + ", DelimiterLength = " + DelimiterLength + "]"; + return "[LineSegment: LineNumber=" + LineNumber + ", Offset = " + Offset + ", Length = " + Length + ", TotalLength = " + TotalLength + ", DelimiterLength = " + DelimiterLength + "]"; + } + + #region Anchor management + + private WeakCollection anchors; + + public TextAnchor CreateAnchor(int column) + { + if (column < 0 || column > Length) + throw new ArgumentOutOfRangeException(nameof(column)); + var anchor = new TextAnchor(this, column); + AddAnchor(anchor); + return anchor; + } + + private void AddAnchor(TextAnchor anchor) + { + Debug.Assert(anchor.Line == this); + + if (anchors == null) + anchors = new WeakCollection(); + + anchors.Add(anchor); + } + + /// + /// Is called when the LineSegment is deleted. + /// + internal void Deleted(ref DeferredEventList deferredEventList) + { + //Console.WriteLine("Deleted"); + treeEntry = LineSegmentTree.Enumerator.Invalid; + if (anchors != null) + { + foreach (var a in anchors) + a.Delete(ref deferredEventList); + anchors = null; + } + } + + /// + /// Is called when a part of the line is removed. + /// + internal void RemovedLinePart(ref DeferredEventList deferredEventList, int startColumn, int length) + { + if (length == 0) + return; + Debug.Assert(length > 0); + + //Console.WriteLine("RemovedLinePart " + startColumn + ", " + length); + if (anchors != null) + { + List deletedAnchors = null; + foreach (var a in anchors) + if (a.ColumnNumber > startColumn) + { + if (a.ColumnNumber >= startColumn + length) + { + a.ColumnNumber -= length; + } + else + { + if (deletedAnchors == null) + deletedAnchors = new List(); + a.Delete(ref deferredEventList); + deletedAnchors.Add(a); + } + } + + if (deletedAnchors != null) + foreach (var a in deletedAnchors) + anchors.Remove(a); + } + } + + /// + /// Is called when a part of the line is inserted. + /// + internal void InsertedLinePart(int startColumn, int length) + { + if (length == 0) + return; + Debug.Assert(length > 0); + + //Console.WriteLine("InsertedLinePart " + startColumn + ", " + length); + if (anchors != null) + foreach (var a in anchors) + if (a.MovementType == AnchorMovementType.BeforeInsertion + ? a.ColumnNumber > startColumn + : a.ColumnNumber >= startColumn) + a.ColumnNumber += length; + } + + /// + /// Is called after another line's content is appended to this line because the newline in between + /// was deleted. + /// The DefaultLineManager will call Deleted() on the deletedLine after the MergedWith call. + /// firstLineLength: the length of the line before the merge. + /// + internal void MergedWith(LineSegment deletedLine, int firstLineLength) + { + //Console.WriteLine("MergedWith"); + + if (deletedLine.anchors != null) + { + foreach (var a in deletedLine.anchors) + { + a.Line = this; + AddAnchor(a); + a.ColumnNumber += firstLineLength; + } + + deletedLine.anchors = null; + } + } + + /// + /// Is called after a newline was inserted into this line, splitting it into this and followingLine. + /// + internal void SplitTo(LineSegment followingLine) + { + //Console.WriteLine("SplitTo"); + + if (anchors != null) + { + List movedAnchors = null; + foreach (var a in anchors) + if (a.MovementType == AnchorMovementType.BeforeInsertion + ? a.ColumnNumber > Length + : a.ColumnNumber >= Length) + { + a.Line = followingLine; + followingLine.AddAnchor(a); + a.ColumnNumber -= Length; + + if (movedAnchors == null) + movedAnchors = new List(); + movedAnchors.Add(a); + } + + if (movedAnchors != null) + foreach (var a in movedAnchors) + anchors.Remove(a); + } + } + + #endregion + } +} diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/LineManager/LineSegmentTree.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/LineManager/LineSegmentTree.cs new file mode 100644 index 0000000..48afb8b --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/LineManager/LineSegmentTree.cs @@ -0,0 +1,452 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using ICSharpCode.TextEditor.Util; + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// Data structure for efficient management of the line segments (most operations are O(lg n)). + /// This implements an augmented red-black tree where each node has fields for the number of + /// nodes in its subtree (like an order statistics tree) for access by index(=line number). + /// Additionally, each node knows the total length of all segments in its subtree. + /// This means we can find nodes by offset in O(lg n) time. Since the offset itself is not stored in + /// the line segment but computed from the lengths stored in the tree, we adjusting the offsets when + /// text is inserted in one line means we just have to increment the totalLength of the affected line and + /// its parent nodes - an O(lg n) operation. + /// However this means getting the line number or offset from a LineSegment is not a constant time + /// operation, but takes O(lg n). + /// NOTE: The tree is never empty, Clear() causes it to contain an empty segment. + /// + internal sealed class LineSegmentTree : IList + { + private readonly AugmentableRedBlackTree tree = new AugmentableRedBlackTree(new MyHost()); + + public LineSegmentTree() + { + Clear(); + } + + /// + /// Gets the total length of all line segments. Runs in O(1). + /// + public int TotalLength => tree.root?.val.totalLength ?? 0; + + /// + /// Gets the number of items in the collections. Runs in O(1). + /// + public int Count => tree.Count; + + /// + /// Gets or sets an item by index. Runs in O(lg n). + /// + public LineSegment this[int index] + { + get => GetNode(index).val.lineSegment; + set => throw new NotSupportedException(); + } + + bool ICollection.IsReadOnly => true; + + /// + /// Gets the index of an item. Runs in O(lg n). + /// + public int IndexOf(LineSegment item) + { + var index = item.LineNumber; + if (index < 0 || index >= Count) + return -1; + if (item != this[index]) + return -1; + return index; + } + + void IList.RemoveAt(int index) => throw new NotSupportedException(); + + /// + /// Clears the list. Runs in O(1). + /// + public void Clear() + { + tree.Clear(); + var emptySegment = new LineSegment(); + emptySegment.TotalLength = 0; + emptySegment.DelimiterLength = 0; + tree.Add(new RBNode(emptySegment)); + emptySegment.treeEntry = GetEnumeratorForIndex(index: 0); +#if DEBUG + CheckProperties(); +#endif + } + + /// + /// Tests whether an item is in the list. Runs in O(n). + /// + public bool Contains(LineSegment item) => IndexOf(item) >= 0; + + /// + /// Copies all elements from the list to the array. + /// + public void CopyTo(LineSegment[] array, int arrayIndex) + { + if (array == null) throw new ArgumentNullException(nameof(array)); + foreach (var val in this) + array[arrayIndex++] = val; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + void IList.Insert(int index, LineSegment item) => throw new NotSupportedException(); + void ICollection.Add(LineSegment item) => throw new NotSupportedException(); + bool ICollection.Remove(LineSegment item) => throw new NotSupportedException(); + + private RedBlackTreeNode GetNode(int index) + { +#if DATACONSISTENCYTEST + if (index < 0 || index >= tree.Count) + throw new ArgumentOutOfRangeException(nameof(index), index, "index should be between 0 and " + (tree.Count - 1)); +#endif + var node = tree.root; + while (true) + if (node.left != null && index < node.left.val.count) + { + node = node.left; + } + else + { + if (node.left != null) + index -= node.left.val.count; + if (index == 0) + return node; + index--; + node = node.right; + } + } + + private static int GetIndexFromNode(RedBlackTreeNode node) + { + var index = node.left?.val.count ?? 0; + while (node.parent != null) + { + if (node == node.parent.right) + { + if (node.parent.left != null) + index += node.parent.left.val.count; + index++; + } + + node = node.parent; + } + + return index; + } + + private RedBlackTreeNode GetNodeByOffset(int offset) + { + if (offset < 0 || offset > TotalLength) + throw new ArgumentOutOfRangeException(nameof(offset), offset, "offset should be between 0 and " + TotalLength); + if (offset == TotalLength) + { + if (tree.root == null) + throw new InvalidOperationException("Cannot call GetNodeByOffset while tree is empty."); + return tree.root.RightMost; + } + + var node = tree.root; + while (true) + if (node.left != null && offset < node.left.val.totalLength) + { + node = node.left; + } + else + { + if (node.left != null) + offset -= node.left.val.totalLength; + offset -= node.val.lineSegment.TotalLength; + if (offset < 0) + return node; + node = node.right; + } + } + + private static int GetOffsetFromNode(RedBlackTreeNode node) + { + var offset = node.left?.val.totalLength ?? 0; + while (node.parent != null) + { + if (node == node.parent.right) + { + if (node.parent.left != null) + offset += node.parent.left.val.totalLength; + offset += node.parent.val.lineSegment.TotalLength; + } + + node = node.parent; + } + + return offset; + } + + public LineSegment GetByOffset(int offset) => GetNodeByOffset(offset).val.lineSegment; + + /// + /// Updates the length of a line segment. Runs in O(lg n). + /// + public void SetSegmentLength(LineSegment segment, int newTotalLength) + { + if (segment == null) + throw new ArgumentNullException(nameof(segment)); + var node = segment.treeEntry.it.node; + segment.TotalLength = newTotalLength; + default(MyHost).UpdateAfterChildrenChange(node); +#if DEBUG + CheckProperties(); +#endif + } + + public void RemoveSegment(LineSegment segment) + { + tree.RemoveAt(segment.treeEntry.it); +#if DEBUG + CheckProperties(); +#endif + } + + public LineSegment InsertSegmentAfter(LineSegment segment, int length) + { + var newSegment = new LineSegment(); + newSegment.TotalLength = length; + newSegment.DelimiterLength = segment.DelimiterLength; + + newSegment.treeEntry = InsertAfter(segment.treeEntry.it.node, newSegment); + return newSegment; + } + + private Enumerator InsertAfter(RedBlackTreeNode node, LineSegment newSegment) + { + var newNode = new RedBlackTreeNode(new RBNode(newSegment)); + if (node.right == null) + tree.InsertAsRight(node, newNode); + else + tree.InsertAsLeft(node.right.LeftMost, newNode); +#if DEBUG + CheckProperties(); +#endif + return new Enumerator(new RedBlackTreeIterator(newNode)); + } + + public Enumerator GetEnumerator() + { + return new Enumerator(tree.GetEnumerator()); + } + + public Enumerator GetEnumeratorForIndex(int index) + { + return new Enumerator(new RedBlackTreeIterator(GetNode(index))); + } + + public Enumerator GetEnumeratorForOffset(int offset) + { + return new Enumerator(new RedBlackTreeIterator(GetNodeByOffset(offset))); + } + + internal struct RBNode + { + internal LineSegment lineSegment; + internal int count; + internal int totalLength; + + public RBNode(LineSegment lineSegment) + { + this.lineSegment = lineSegment; + count = 1; + totalLength = lineSegment.TotalLength; + } + + public override string ToString() + { + return "[RBNode count=" + count + " totalLength=" + totalLength + + " lineSegment.LineNumber=" + lineSegment.LineNumber + + " lineSegment.Offset=" + lineSegment.Offset + + " lineSegment.TotalLength=" + lineSegment.TotalLength + + " lineSegment.DelimiterLength=" + lineSegment.DelimiterLength + "]"; + } + } + + private struct MyHost : IRedBlackTreeHost + { + public int Compare(RBNode x, RBNode y) + { + throw new NotImplementedException(); + } + + public bool Equals(RBNode a, RBNode b) + { + throw new NotImplementedException(); + } + + public void UpdateAfterChildrenChange(RedBlackTreeNode node) + { + var count = 1; + var totalLength = node.val.lineSegment.TotalLength; + if (node.left != null) + { + count += node.left.val.count; + totalLength += node.left.val.totalLength; + } + + if (node.right != null) + { + count += node.right.val.count; + totalLength += node.right.val.totalLength; + } + + if (count != node.val.count || totalLength != node.val.totalLength) + { + node.val.count = count; + node.val.totalLength = totalLength; + if (node.parent != null) UpdateAfterChildrenChange(node.parent); + } + } + + public void UpdateAfterRotateLeft(RedBlackTreeNode node) + { + UpdateAfterChildrenChange(node); + UpdateAfterChildrenChange(node.parent); + } + + public void UpdateAfterRotateRight(RedBlackTreeNode node) + { + UpdateAfterChildrenChange(node); + UpdateAfterChildrenChange(node.parent); + } + } + + public struct Enumerator : IEnumerator + { + /// + /// An invalid enumerator value. Calling MoveNext on the invalid enumerator + /// will always return false, accessing Current will throw an exception. + /// + public static readonly Enumerator Invalid = default; + + internal RedBlackTreeIterator it; + + internal Enumerator(RedBlackTreeIterator it) + { + this.it = it; + } + + /// + /// Gets the current value. Runs in O(1). + /// + public LineSegment Current => it.Current.lineSegment; + + public bool IsValid => it.IsValid; + + /// + /// Gets the index of the current value. Runs in O(lg n). + /// + public int CurrentIndex + { + get + { + if (it.node == null) + throw new InvalidOperationException(); + return GetIndexFromNode(it.node); + } + } + + /// + /// Gets the offset of the current value. Runs in O(lg n). + /// + public int CurrentOffset + { + get + { + if (it.node == null) + throw new InvalidOperationException(); + return GetOffsetFromNode(it.node); + } + } + + object IEnumerator.Current => it.Current.lineSegment; + + public void Dispose() + { + } + + /// + /// Moves to the next index. Runs in O(lg n), but for k calls, the combined time is only O(k+lg n). + /// + public bool MoveNext() + { + return it.MoveNext(); + } + + /// + /// Moves to the previous index. Runs in O(lg n), but for k calls, the combined time is only O(k+lg n). + /// + public bool MoveBack() + { + return it.MoveBack(); + } + + void IEnumerator.Reset() + { + throw new NotSupportedException(); + } + } + +#if DEBUG + [Conditional("DATACONSISTENCYTEST")] + private void CheckProperties() + { + if (tree.root == null) + { + Debug.Assert(Count == 0); + } + else + { + Debug.Assert(tree.root.val.count == Count); + CheckProperties(tree.root); + } + } + + private static void CheckProperties(RedBlackTreeNode node) + { + var count = 1; + var totalLength = node.val.lineSegment.TotalLength; + if (node.left != null) + { + CheckProperties(node.left); + count += node.left.val.count; + totalLength += node.left.val.totalLength; + } + + if (node.right != null) + { + CheckProperties(node.right); + count += node.right.val.count; + totalLength += node.right.val.totalLength; + } + + Debug.Assert(node.val.count == count); + Debug.Assert(node.val.totalLength == totalLength); + } + + public string GetTreeAsString() + { + return tree.GetTreeAsString(); + } +#endif + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/MarkerStrategy/MarkerStrategy.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/MarkerStrategy/MarkerStrategy.cs new file mode 100644 index 0000000..fa7f1e3 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/MarkerStrategy/MarkerStrategy.cs @@ -0,0 +1,112 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// Manages the list of markers and provides ways to retrieve markers for specific positions. + /// + public sealed class MarkerStrategy + { + private readonly Dictionary> markersTable = new Dictionary>(); + private readonly List textMarker = new List(); + + public MarkerStrategy(IDocument document) + { + Document = document; + document.DocumentChanged += DocumentChanged; + } + + public IDocument Document { get; } + + public IEnumerable TextMarker => textMarker.AsReadOnly(); + + public void AddMarker(TextMarker item) + { + markersTable.Clear(); + textMarker.Add(item); + } + + public void InsertMarker(int index, TextMarker item) + { + markersTable.Clear(); + textMarker.Insert(index, item); + } + + public void RemoveMarker(TextMarker item) + { + markersTable.Clear(); + textMarker.Remove(item); + } + + public void RemoveAll(Predicate match) + { + markersTable.Clear(); + textMarker.RemoveAll(match); + } + + public List GetMarkers(int offset) + { + if (!markersTable.ContainsKey(offset)) + { + var markers = new List(); + for (var i = 0; i < textMarker.Count; ++i) + { + var marker = textMarker[i]; + if (marker.Offset <= offset && offset <= marker.EndOffset) + markers.Add(marker); + } + + markersTable[offset] = markers; + } + + return markersTable[offset]; + } + + public List GetMarkers(int offset, int length) + { + var endOffset = offset + length - 1; + var markers = new List(); + for (var i = 0; i < textMarker.Count; ++i) + { + var marker = textMarker[i]; + var markerOffset = marker.Offset; + var markerEndOffset = marker.EndOffset; + if ( // start in marker region + markerOffset <= offset && offset <= markerEndOffset || + // end in marker region + markerOffset <= endOffset && endOffset <= markerEndOffset || + // marker start in region + offset <= markerOffset && markerOffset <= endOffset || + // marker end in region + offset <= markerEndOffset && markerEndOffset <= endOffset + ) + markers.Add(marker); + } + + return markers; + } + + public List GetMarkers(TextLocation position) + { + if (position.Y >= Document.TotalNumberOfLines || position.Y < 0) + return new List(); + var segment = Document.GetLineSegment(position.Y); + return GetMarkers(segment.Offset + position.X); + } + + private void DocumentChanged(object sender, DocumentEventArgs e) + { + // reset markers table + markersTable.Clear(); + Document.UpdateSegmentListOnDocumentChange(textMarker, e); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/MarkerStrategy/TextMarker.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/MarkerStrategy/TextMarker.cs new file mode 100644 index 0000000..7e2e153 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/MarkerStrategy/TextMarker.cs @@ -0,0 +1,98 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Drawing; + +namespace ICSharpCode.TextEditor.Document +{ + public enum TextMarkerType + { + SolidBlock, + Underlined, + WaveLine + } + + /// + /// Marks a part of a document. + /// + public class TextMarker : ISegment + { + [CLSCompliant(isCompliant: false)] protected int length = -1; + + [CLSCompliant(isCompliant: false)] protected int offset = -1; + + public TextMarker(int offset, int length, TextMarkerType textMarkerType) : this(offset, length, textMarkerType, Color.Red) + { + } + + public TextMarker(int offset, int length, TextMarkerType textMarkerType, Color color) + { + if (length < 1) length = 1; + this.offset = offset; + this.length = length; + TextMarkerType = textMarkerType; + Color = color; + } + + public TextMarker(int offset, int length, TextMarkerType textMarkerType, Color color, Color foreColor) + { + if (length < 1) length = 1; + this.offset = offset; + this.length = length; + TextMarkerType = textMarkerType; + Color = color; + ForeColor = foreColor; + OverrideForeColor = true; + } + + public TextMarkerType TextMarkerType { get; } + + public Color Color { get; } + + public Color ForeColor { get; } + + public bool OverrideForeColor { get; } + + /// + /// Marks the text segment as read-only. + /// + public bool IsReadOnly { get; set; } + + public string ToolTip { get; set; } = null; + + /// + /// Gets the last offset that is inside the marker region. + /// + public int EndOffset => offset + length - 1; + + public override string ToString() + { + return string.Format( + "[TextMarker: Offset = {0}, Length = {1}, Type = {2}]", + offset, + length, + TextMarkerType); + } + + #region ICSharpCode.TextEditor.Document.ISegment interface implementation + + public int Offset + { + get => offset; + set => offset = value; + } + + public int Length + { + get => length; + set => length = value; + } + + #endregion + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/Selection/ColumnRange.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/Selection/ColumnRange.cs new file mode 100644 index 0000000..7a2b1b1 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/Selection/ColumnRange.cs @@ -0,0 +1,43 @@ +// +// +// +// +// $Revision$ +// + +namespace ICSharpCode.TextEditor.Document +{ + public class ColumnRange + { + public static readonly ColumnRange NoColumn = new ColumnRange(startColumn: -2, endColumn: -2); + public static readonly ColumnRange WholeColumn = new ColumnRange(startColumn: -1, endColumn: -1); + + public ColumnRange(int startColumn, int endColumn) + { + StartColumn = startColumn; + EndColumn = endColumn; + } + + public int StartColumn { get; set; } + + public int EndColumn { get; set; } + + public override int GetHashCode() + { + return StartColumn + (EndColumn << 16); + } + + public override bool Equals(object obj) + { + if (obj is ColumnRange range) + return range.StartColumn == StartColumn && + range.EndColumn == EndColumn; + return false; + } + + public override string ToString() + { + return string.Format("[ColumnRange: StartColumn={0}, EndColumn={1}]", StartColumn, EndColumn); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/Selection/DefaultSelection.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/Selection/DefaultSelection.cs new file mode 100644 index 0000000..bdd0959 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/Selection/DefaultSelection.cs @@ -0,0 +1,111 @@ +// +// +// +// +// $Revision$ +// + +using System.Diagnostics; + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// Default implementation of the interface. + /// + public class DefaultSelection : ISelection + { + private readonly IDocument document; + private TextLocation endPosition; + private TextLocation startPosition; + + /// + /// Creates a new instance of + /// + public DefaultSelection(IDocument document, TextLocation startPosition, TextLocation endPosition) + { + DefaultDocument.ValidatePosition(document, startPosition); + DefaultDocument.ValidatePosition(document, endPosition); + Debug.Assert(startPosition <= endPosition); + this.document = document; + this.startPosition = startPosition; + this.endPosition = endPosition; + } + + public TextLocation StartPosition + { + get => startPosition; + set + { + DefaultDocument.ValidatePosition(document, value); + startPosition = value; + } + } + + public TextLocation EndPosition + { + get => endPosition; + set + { + DefaultDocument.ValidatePosition(document, value); + endPosition = value; + } + } + + public int Offset => document.PositionToOffset(startPosition); + + public int EndOffset => document.PositionToOffset(endPosition); + + public int Length => EndOffset - Offset; + + /// + /// Returns true, if the selection is empty + /// + public bool IsEmpty => startPosition == endPosition; + + /// + /// Returns true, if the selection is rectangular + /// + // TODO : make this unused property used. + public bool IsRectangularSelection { get; set; } + + /// + /// The text which is selected by this selection. + /// + public string SelectedText + { + get + { + if (document != null) + { + if (Length < 0) + return null; + return document.GetText(Offset, Length); + } + + return null; + } + } + + public bool ContainsPosition(TextLocation position) + { + if (IsEmpty) + return false; + return startPosition.Y < position.Y && position.Y < endPosition.Y || + startPosition.Y == position.Y && startPosition.X <= position.X && (startPosition.Y != endPosition.Y || position.X <= endPosition.X) || + endPosition.Y == position.Y && startPosition.Y != endPosition.Y && position.X <= endPosition.X; + } + + public bool ContainsOffset(int offset) + { + return Offset <= offset && offset <= EndOffset; + } + + /// + /// Converts a instance to string (for debug purposes) + /// + public override string ToString() + { + return string.Format("[DefaultSelection : StartPosition={0}, EndPosition={1}]", startPosition, endPosition); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/Selection/ISelection.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/Selection/ISelection.cs new file mode 100644 index 0000000..a57a276 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/Selection/ISelection.cs @@ -0,0 +1,44 @@ +// +// +// +// +// $Revision$ +// + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// An interface representing a portion of the current selection. + /// + public interface ISelection + { + TextLocation StartPosition { get; set; } + + TextLocation EndPosition { get; set; } + + int Offset { get; } + + int EndOffset { get; } + + int Length { get; } + + /// + /// Returns true, if the selection is rectangular + /// + bool IsRectangularSelection { get; } + + /// + /// Returns true, if the selection is empty + /// + bool IsEmpty { get; } + + /// + /// The text which is selected by this selection. + /// + string SelectedText { get; } + + bool ContainsOffset(int offset); + + bool ContainsPosition(TextLocation position); + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/Selection/SelectionManager.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/Selection/SelectionManager.cs new file mode 100644 index 0000000..618142e --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/Selection/SelectionManager.cs @@ -0,0 +1,463 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using System.Text; + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// This class manages the selections in a document. + /// + public class SelectionManager : IDisposable + { + private readonly TextArea textArea; + + private IDocument document; + internal SelectFrom selectFrom = new SelectFrom(); + + internal List selectionCollection = new List(); + private TextLocation selectionStart; + + /// + /// Creates a new instance of + /// + public SelectionManager(IDocument document) + { + this.document = document; + document.DocumentChanged += DocumentChanged; + } + + /// + /// Creates a new instance of + /// + public SelectionManager(IDocument document, TextArea textArea) + { + this.document = document; + this.textArea = textArea; + document.DocumentChanged += DocumentChanged; + } + + internal TextLocation SelectionStart + { + get => selectionStart; + set + { + DefaultDocument.ValidatePosition(document, value); + selectionStart = value; + } + } + + /// + /// A collection containing all selections. + /// + public List SelectionCollection => selectionCollection; + + /// + /// true if the is not empty, false otherwise. + /// + public bool HasSomethingSelected => selectionCollection.Count > 0; + + public bool SelectionIsReadonly + { + get + { + if (document.ReadOnly) + return true; + foreach (var sel in selectionCollection) + if (SelectionIsReadOnly(document, sel)) + return true; + return false; + } + } + + /// + /// The text that is currently selected. + /// + public string SelectedText + { + get + { + var builder = new StringBuilder(); + +// PriorityQueue queue = new PriorityQueue(); + + foreach (var s in selectionCollection) builder.Append(s.SelectedText); +// queue.Insert(-s.Offset, s); + +// while (queue.Count > 0) { +// ISelection s = ((ISelection)queue.Remove()); +// builder.Append(s.SelectedText); +// } + + return builder.ToString(); + } + } + + public void Dispose() + { + if (document != null) + { + document.DocumentChanged -= DocumentChanged; + document = null; + } + } + + internal static bool SelectionIsReadOnly(IDocument document, ISelection sel) + { + if (document.TextEditorProperties.SupportReadOnlySegments) + return document.MarkerStrategy.GetMarkers(sel.Offset, sel.Length).Exists(m => m.IsReadOnly); + return false; + } + + private void DocumentChanged(object sender, DocumentEventArgs e) + { + if (e.Text == null) + { + Remove(e.Offset, e.Length); + } + else + { + if (e.Length < 0) + Insert(e.Offset, e.Text); + else + Replace(e.Offset, e.Length, e.Text); + } + } + + /// + /// Clears the selection and sets a new selection + /// using the given object. + /// + public void SetSelection(ISelection selection) + { +// autoClearSelection = false; + if (selection != null) + { + if (SelectionCollection.Count == 1 && + selection.StartPosition == SelectionCollection[index: 0].StartPosition && + selection.EndPosition == SelectionCollection[index: 0].EndPosition) + return; + ClearWithoutUpdate(); + selectionCollection.Add(selection); + document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.LinesBetween, selection.StartPosition.Y, selection.EndPosition.Y)); + document.CommitUpdate(); + OnSelectionChanged(EventArgs.Empty); + } + else + { + ClearSelection(); + } + } + + public void SetSelection(TextLocation startPosition, TextLocation endPosition) + { + SetSelection(new DefaultSelection(document, startPosition, endPosition)); + } + + public bool GreaterEqPos(TextLocation p1, TextLocation p2) + { + return p1.Y > p2.Y || p1.Y == p2.Y && p1.X >= p2.X; + } + + public void ExtendSelection(TextLocation oldPosition, TextLocation newPosition) + { + // where oldposition is where the cursor was, + // and newposition is where it has ended up from a click (both zero based) + + if (oldPosition == newPosition) + return; + + TextLocation min; + TextLocation max; + var oldnewX = newPosition.X; + var oldIsGreater = GreaterEqPos(oldPosition, newPosition); + if (oldIsGreater) + { + min = newPosition; + max = oldPosition; + } + else + { + min = oldPosition; + max = newPosition; + } + + if (min == max) + return; + + if (!HasSomethingSelected) + { + SetSelection(new DefaultSelection(document, min, max)); + // initialise selectFrom for a cursor selection + if (selectFrom.where == WhereFrom.None) + SelectionStart = oldPosition; //textArea.Caret.Position; + return; + } + + var selection = selectionCollection[index: 0]; + + // changed selection via gutter + if (selectFrom.where == WhereFrom.Gutter) + newPosition.X = 0; + + if (GreaterEqPos(newPosition, SelectionStart)) // selecting forward + { + selection.StartPosition = SelectionStart; + // this handles last line selection + if (selectFrom.where == WhereFrom.Gutter) //&& newPosition.Y != oldPosition.Y) + { + selection.EndPosition = new TextLocation(textArea.Caret.Column, textArea.Caret.Line); + } + else + { + newPosition.X = oldnewX; + selection.EndPosition = newPosition; + } + } + else + { + // selecting back + if (selectFrom.where == WhereFrom.Gutter && selectFrom.first == WhereFrom.Gutter) + selection.EndPosition = NextValidPosition(SelectionStart.Y); + else + selection.EndPosition = SelectionStart; //selection.StartPosition; + selection.StartPosition = newPosition; + } + + document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.LinesBetween, min.Y, max.Y)); + document.CommitUpdate(); + OnSelectionChanged(EventArgs.Empty); + } + + // retrieve the next available line + // - checks that there are more lines available after the current one + // - if there are then the next line is returned + // - if there are NOT then the last position on the given line is returned + public TextLocation NextValidPosition(int line) + { + if (line < document.TotalNumberOfLines - 1) + return new TextLocation(column: 0, line + 1); + return new TextLocation(document.GetLineSegment(document.TotalNumberOfLines - 1).Length + 1, line); + } + + private void ClearWithoutUpdate() + { + while (selectionCollection.Count > 0) + { + var selection = selectionCollection[selectionCollection.Count - 1]; + selectionCollection.RemoveAt(selectionCollection.Count - 1); + document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.LinesBetween, selection.StartPosition.Y, selection.EndPosition.Y)); + OnSelectionChanged(EventArgs.Empty); + } + } + + /// + /// Clears the selection. + /// + public void ClearSelection() + { + var mousepos = textArea.mousepos; + // this is the most logical place to reset selection starting + // positions because it is always called before a new selection + selectFrom.first = selectFrom.where; + var newSelectionStart = textArea.TextView.GetLogicalPosition(mousepos.X - textArea.TextView.DrawingPosition.X, mousepos.Y - textArea.TextView.DrawingPosition.Y); + if (selectFrom.where == WhereFrom.Gutter) + newSelectionStart.X = 0; + if (newSelectionStart.Line >= document.TotalNumberOfLines) + { + newSelectionStart.Line = document.TotalNumberOfLines - 1; + newSelectionStart.Column = document.GetLineSegment(document.TotalNumberOfLines - 1).Length; + } + + SelectionStart = newSelectionStart; + + ClearWithoutUpdate(); + document.CommitUpdate(); + } + + /// + /// Removes the selected text from the buffer and clears + /// the selection. + /// + public void RemoveSelectedText() + { + if (SelectionIsReadonly) + { + ClearSelection(); + return; + } + + var lines = new List(); + var offset = -1; + var oneLine = true; +// PriorityQueue queue = new PriorityQueue(); + foreach (var s in selectionCollection) + { +// ISelection s = ((ISelection)queue.Remove()); + if (oneLine) + { + var lineBegin = s.StartPosition.Y; + if (lineBegin != s.EndPosition.Y) + oneLine = false; + else + lines.Add(lineBegin); + } + + offset = s.Offset; + document.Remove(s.Offset, s.Length); + +// queue.Insert(-s.Offset, s); + } + + ClearSelection(); + if (offset >= 0) + { + // TODO: +// document.Caret.Offset = offset; + } + + if (offset != -1) + { + if (oneLine) + foreach (var i in lines) + document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.SingleLine, i)); + else + document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.WholeTextArea)); + document.CommitUpdate(); + } + } + + private bool SelectionsOverlap(ISelection s1, ISelection s2) + { + return s1.Offset <= s2.Offset && s2.Offset <= s1.Offset + s1.Length || + s1.Offset <= s2.Offset + s2.Length && s2.Offset + s2.Length <= s1.Offset + s1.Length || + s1.Offset >= s2.Offset && s1.Offset + s1.Length <= s2.Offset + s2.Length; + } + + /// + /// Returns true if the given offset points to a section which is + /// selected. + /// + public bool IsSelected(int offset) + { + return GetSelectionAt(offset) != null; + } + + /// + /// Returns a object giving the selection in which + /// the offset points to. + /// + /// + /// null if the offset doesn't point to a selection + /// + public ISelection GetSelectionAt(int offset) + { + foreach (var s in selectionCollection) + if (s.ContainsOffset(offset)) + return s; + return null; + } + + /// + /// Used internally, do not call. + /// + internal void Insert(int offset, string text) + { +// foreach (ISelection selection in SelectionCollection) { +// if (selection.Offset > offset) { +// selection.Offset += text.Length; +// } else if (selection.Offset + selection.Length > offset) { +// selection.Length += text.Length; +// } +// } + } + + /// + /// Used internally, do not call. + /// + internal void Remove(int offset, int length) + { +// foreach (ISelection selection in selectionCollection) { +// if (selection.Offset > offset) { +// selection.Offset -= length; +// } else if (selection.Offset + selection.Length > offset) { +// selection.Length -= length; +// } +// } + } + + /// + /// Used internally, do not call. + /// + internal void Replace(int offset, int length, string text) + { +// foreach (ISelection selection in selectionCollection) { +// if (selection.Offset > offset) { +// selection.Offset = selection.Offset - length + text.Length; +// } else if (selection.Offset + selection.Length > offset) { +// selection.Length = selection.Length - length + text.Length; +// } +// } + } + + public ColumnRange GetSelectionAtLine(int lineNumber) + { + foreach (var selection in selectionCollection) + { + var startLine = selection.StartPosition.Y; + var endLine = selection.EndPosition.Y; + if (startLine < lineNumber && lineNumber < endLine) + return ColumnRange.WholeColumn; + + if (startLine == lineNumber) + { + var line = document.GetLineSegment(startLine); + var startColumn = selection.StartPosition.X; + var endColumn = endLine == lineNumber ? selection.EndPosition.X : line.Length + 1; + return new ColumnRange(startColumn, endColumn); + } + + if (endLine == lineNumber) + { + var endColumn = selection.EndPosition.X; + return new ColumnRange(startColumn: 0, endColumn); + } + } + + return ColumnRange.NoColumn; + } + + public void FireSelectionChanged() + { + OnSelectionChanged(EventArgs.Empty); + } + + protected virtual void OnSelectionChanged(EventArgs e) + { + SelectionChanged?.Invoke(this, e); + } + + public event EventHandler SelectionChanged; + } + + // selection initiated from... + internal class SelectFrom + { + public int first = WhereFrom.None; // first selection initiator + public int where = WhereFrom.None; // last selection initiator + } + + // selection initiated from type... + internal class WhereFrom + { + public const int None = 0; + public const int Gutter = 1; + public const int TArea = 2; + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/TextAnchor.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/TextAnchor.cs new file mode 100644 index 0000000..701f5c7 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/TextAnchor.cs @@ -0,0 +1,102 @@ +// +// +// +// +// $Revision$ +// + +using System; + +namespace ICSharpCode.TextEditor.Document +{ + public enum AnchorMovementType + { + /// + /// Behaves like a start marker - when text is inserted at the anchor position, the anchor will stay + /// before the inserted text. + /// + BeforeInsertion, + + /// + /// Behave like an end marker - when text is insered at the anchor position, the anchor will move + /// after the inserted text. + /// + AfterInsertion + } + + /// + /// An anchor that can be put into a document and moves around when the document is changed. + /// + public sealed class TextAnchor + { + private int columnNumber; + + private LineSegment lineSegment; + + internal TextAnchor(LineSegment lineSegment, int columnNumber) + { + this.lineSegment = lineSegment; + this.columnNumber = columnNumber; + } + + public LineSegment Line + { + get + { + if (lineSegment == null) throw AnchorDeletedError(); + return lineSegment; + } + internal set => lineSegment = value; + } + + public bool IsDeleted => lineSegment == null; + + public int LineNumber => Line.LineNumber; + + public int ColumnNumber + { + get + { + if (lineSegment == null) throw AnchorDeletedError(); + return columnNumber; + } + internal set => columnNumber = value; + } + + public TextLocation Location => new TextLocation(ColumnNumber, LineNumber); + + public int Offset => Line.Offset + columnNumber; + + /// + /// Controls how the anchor moves. + /// + public AnchorMovementType MovementType { get; set; } + + private static Exception AnchorDeletedError() + { + return new InvalidOperationException("The text containing the anchor was deleted"); + } + + public event EventHandler Deleted; + + internal void Delete(ref DeferredEventList deferredEventList) + { + // we cannot fire an event here because this method is called while the LineManager adjusts the + // lineCollection, so an event handler could see inconsistent state + lineSegment = null; + deferredEventList.AddDeletedAnchor(this); + } + + internal void RaiseDeleted() + { + Deleted?.Invoke(this, EventArgs.Empty); + } + + public override string ToString() + { + if (IsDeleted) + return "[TextAnchor (deleted)]"; + return "[TextAnchor " + Location + "]"; + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/TextBufferStrategy/GapTextBufferStrategy.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/TextBufferStrategy/GapTextBufferStrategy.cs new file mode 100644 index 0000000..5acb387 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/TextBufferStrategy/GapTextBufferStrategy.cs @@ -0,0 +1,184 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Text; +using System.Threading; + +namespace ICSharpCode.TextEditor.Document +{ + public class GapTextBufferStrategy : ITextBufferStrategy + { + private const int minGapLength = 128; + private const int maxGapLength = 2048; + + private char[] buffer = new char[0]; + private string cachedContent; + + private int gapBeginOffset; + private int gapEndOffset; + private int gapLength; // gapLength == gapEndOffset - gapBeginOffset + + public int Length => buffer.Length - gapLength; + + public void SetContent(string text) + { + if (text == null) + text = string.Empty; + cachedContent = text; + buffer = text.ToCharArray(); + gapBeginOffset = gapEndOffset = gapLength = 0; + } + + public char GetCharAt(int offset) + { +#if DEBUG + CheckThread(); +#endif + + if (offset < 0 || offset >= Length) + throw new ArgumentOutOfRangeException(nameof(offset), offset, "0 <= offset < " + Length); + + return offset < gapBeginOffset ? buffer[offset] : buffer[offset + gapLength]; + } + + public string GetText(int offset, int length) + { +#if DEBUG + CheckThread(); +#endif + + if (offset < 0 || offset > Length) + throw new ArgumentOutOfRangeException(nameof(offset), offset, "0 <= offset <= " + Length); + if (length < 0 || offset + length > Length) + throw new ArgumentOutOfRangeException(nameof(length), length, "0 <= length, offset(" + offset + ")+length <= " + Length); + if (offset == 0 && length == Length) + { + if (cachedContent != null) + return cachedContent; + return cachedContent = GetTextInternal(offset, length); + } + + return GetTextInternal(offset, length); + } + + public void Insert(int offset, string text) + { + Replace(offset, length: 0, text); + } + + public void Remove(int offset, int length) + { + Replace(offset, length, string.Empty); + } + + public void Replace(int offset, int length, string text) + { + if (text == null) + text = string.Empty; + +#if DEBUG + CheckThread(); +#endif + + if (offset < 0 || offset > Length) + throw new ArgumentOutOfRangeException(nameof(offset), offset, "0 <= offset <= " + Length); + if (length < 0 || offset + length > Length) + throw new ArgumentOutOfRangeException(nameof(length), length, "0 <= length, offset+length <= " + Length); + + cachedContent = null; + + // Math.Max is used so that if we need to resize the array + // the new array has enough space for all old chars + PlaceGap(offset, text.Length - length); + gapEndOffset += length; // delete removed text + text.CopyTo(sourceIndex: 0, buffer, gapBeginOffset, text.Length); + gapBeginOffset += text.Length; + gapLength = gapEndOffset - gapBeginOffset; + if (gapLength > maxGapLength) + MakeNewBuffer(gapBeginOffset, minGapLength); + } + + private string GetTextInternal(int offset, int length) + { + var end = offset + length; + + if (end < gapBeginOffset) + return new string(buffer, offset, length); + + if (offset > gapBeginOffset) + return new string(buffer, offset + gapLength, length); + + var block1Size = gapBeginOffset - offset; + var block2Size = end - gapBeginOffset; + + var buf = new StringBuilder(block1Size + block2Size); + buf.Append(buffer, offset, block1Size); + buf.Append(buffer, gapEndOffset, block2Size); + return buf.ToString(); + } + + private void PlaceGap(int newGapOffset, int minRequiredGapLength) + { + if (gapLength < minRequiredGapLength) + { + // enlarge gap + MakeNewBuffer(newGapOffset, minRequiredGapLength); + } + else + { + while (newGapOffset < gapBeginOffset) + buffer[--gapEndOffset] = buffer[--gapBeginOffset]; + while (newGapOffset > gapBeginOffset) + buffer[gapBeginOffset++] = buffer[gapEndOffset++]; + } + } + + private void MakeNewBuffer(int newGapOffset, int newGapLength) + { + if (newGapLength < minGapLength) newGapLength = minGapLength; + + var newBuffer = new char[Length + newGapLength]; + if (newGapOffset < gapBeginOffset) + { + // gap is moving backwards + + // first part: + Array.Copy(buffer, sourceIndex: 0, newBuffer, destinationIndex: 0, newGapOffset); + // moving middle part: + Array.Copy(buffer, newGapOffset, newBuffer, newGapOffset + newGapLength, gapBeginOffset - newGapOffset); + // last part: + Array.Copy(buffer, gapEndOffset, newBuffer, newBuffer.Length - (buffer.Length - gapEndOffset), buffer.Length - gapEndOffset); + } + else + { + // gap is moving forwards + // first part: + Array.Copy(buffer, sourceIndex: 0, newBuffer, destinationIndex: 0, gapBeginOffset); + // moving middle part: + Array.Copy(buffer, gapEndOffset, newBuffer, gapBeginOffset, newGapOffset - gapBeginOffset); + // last part: + var lastPartLength = newBuffer.Length - (newGapOffset + newGapLength); + Array.Copy(buffer, buffer.Length - lastPartLength, newBuffer, newGapOffset + newGapLength, lastPartLength); + } + + gapBeginOffset = newGapOffset; + gapEndOffset = newGapOffset + newGapLength; + gapLength = newGapLength; + buffer = newBuffer; + } +#if DEBUG + private readonly int creatorThread = Thread.CurrentThread.ManagedThreadId; + + private void CheckThread() + { + if (Thread.CurrentThread.ManagedThreadId != creatorThread) + throw new InvalidOperationException("GapTextBufferStategy is not thread-safe!"); + } +#endif + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/TextBufferStrategy/ITextBufferStrategy.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/TextBufferStrategy/ITextBufferStrategy.cs new file mode 100644 index 0000000..24a409d --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/TextBufferStrategy/ITextBufferStrategy.cs @@ -0,0 +1,83 @@ +// +// +// +// +// $Revision$ +// + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// Interface to describe a sequence of characters that can be edited. + /// + public interface ITextBufferStrategy + { + /// + /// The current length of the sequence of characters that can be edited. + /// + int Length { get; } + + /// + /// Inserts a string of characters into the sequence. + /// + /// + /// offset where to insert the string. + /// + /// + /// text to be inserted. + /// + void Insert(int offset, string text); + + /// + /// Removes some portion of the sequence. + /// + /// + /// offset of the remove. + /// + /// + /// number of characters to remove. + /// + void Remove(int offset, int length); + + /// + /// Replace some portion of the sequence. + /// + /// + /// offset. + /// + /// + /// number of characters to replace. + /// + /// + /// text to be replaced with. + /// + void Replace(int offset, int length, string text); + + /// + /// Fetches a string of characters contained in the sequence. + /// + /// + /// Offset into the sequence to fetch + /// + /// + /// number of characters to copy. + /// + string GetText(int offset, int length); + + /// + /// Returns a specific char of the sequence. + /// + /// + /// Offset of the char to get. + /// + char GetCharAt(int offset); + + /// + /// This method sets the stored content. + /// + /// + /// The string that represents the character sequence. + /// + void SetContent(string text); + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/TextBufferStrategy/StringTextBufferStrategy.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/TextBufferStrategy/StringTextBufferStrategy.cs new file mode 100644 index 0000000..c485d47 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/TextBufferStrategy/StringTextBufferStrategy.cs @@ -0,0 +1,73 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.IO; +using System.Text; +using ICSharpCode.TextEditor.Util; + +namespace ICSharpCode.TextEditor.Document +{ + /// + /// Simple implementation of the ITextBuffer interface implemented using a + /// string. + /// Only for fall-back purposes. + /// + public class StringTextBufferStrategy : ITextBufferStrategy + { + private string storedText = ""; + + public int Length => storedText.Length; + + public void Insert(int offset, string text) + { + if (text != null) + storedText = storedText.Insert(offset, text); + } + + public void Remove(int offset, int length) + { + storedText = storedText.Remove(offset, length); + } + + public void Replace(int offset, int length, string text) + { + Remove(offset, length); + Insert(offset, text); + } + + public string GetText(int offset, int length) + { + if (length == 0) + return ""; + if (offset == 0 && length >= storedText.Length) + return storedText; + return storedText.Substring(offset, Math.Min(length, storedText.Length - offset)); + } + + public char GetCharAt(int offset) + { + if (offset == Length) + return '\0'; + return storedText[offset]; + } + + public void SetContent(string text) + { + storedText = text; + } + + public static ITextBufferStrategy CreateTextBufferFromFile(string fileName) + { + if (!File.Exists(fileName)) + throw new FileNotFoundException(fileName); + var s = new StringTextBufferStrategy(); + s.SetContent(FileReader.ReadFileContent(fileName, Encoding.Default)); + return s; + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/TextLocation.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/TextLocation.cs new file mode 100644 index 0000000..d905443 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/TextLocation.cs @@ -0,0 +1,115 @@ +// +// +// +// +// $Revision: 2658$ +// + +using System; + +namespace ICSharpCode.TextEditor +{ + /// + /// A line/column position. + /// Text editor lines/columns are counting from zero. + /// + public struct TextLocation : IComparable, IEquatable + { + /// + /// Represents no text location (-1, -1). + /// + public static readonly TextLocation Empty = new TextLocation(column: -1, line: -1); + + public TextLocation(int column, int line) + { + X = column; + Y = line; + } + + public int X { get; set; } + + public int Y { get; set; } + + public int Line + { + get => Y; + set => Y = value; + } + + public int Column + { + get => X; + set => X = value; + } + + public bool IsEmpty => X <= 0 && Y <= 0; + + public override string ToString() + { + return string.Format("(Line {1}, Col {0})", X, Y); + } + + public override int GetHashCode() + { + return unchecked((87*X.GetHashCode()) ^ Y.GetHashCode()); + } + + public override bool Equals(object obj) + { + if (!(obj is TextLocation)) return false; + return (TextLocation)obj == this; + } + + public bool Equals(TextLocation other) + { + return this == other; + } + + public static bool operator ==(TextLocation a, TextLocation b) + { + return a.X == b.X && a.Y == b.Y; + } + + public static bool operator !=(TextLocation a, TextLocation b) + { + return a.X != b.X || a.Y != b.Y; + } + + public static bool operator <(TextLocation a, TextLocation b) + { + if (a.Y < b.Y) + return true; + if (a.Y == b.Y) + return a.X < b.X; + return false; + } + + public static bool operator >(TextLocation a, TextLocation b) + { + if (a.Y > b.Y) + return true; + if (a.Y == b.Y) + return a.X > b.X; + return false; + } + + public static bool operator <=(TextLocation a, TextLocation b) + { + return !(a > b); + } + + public static bool operator >=(TextLocation a, TextLocation b) + { + return !(a < b); + } + + public int CompareTo(TextLocation other) + { + if (this == other) + return 0; + if (this < other) + return -1; + return 1; + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Document/TextUtilities.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/TextUtilities.cs new file mode 100644 index 0000000..dd04fee --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Document/TextUtilities.cs @@ -0,0 +1,304 @@ +// +// +// +// +// $Revision$ +// + +using System.Diagnostics; +using System.Text; + +namespace ICSharpCode.TextEditor.Document +{ + public sealed class TextUtilities + { + public enum CharacterType + { + LetterDigitOrUnderscore, + WhiteSpace, + Other + } + + /// + /// This function takes a string and converts the whitespace in front of + /// it to tabs. If the length of the whitespace at the start of the string + /// was not a whole number of tabs then there will still be some spaces just + /// before the text starts. + /// the output string will be of the form: + /// 1. zero or more tabs + /// 2. zero or more spaces (less than tabIndent) + /// 3. the rest of the line + /// + public static string LeadingWhiteSpaceToTabs(string line, int tabIndent) + { + var sb = new StringBuilder(line.Length); + var consecutiveSpaces = 0; + int i; + for (i = 0; i < line.Length; i++) + if (line[i] == ' ') + { + consecutiveSpaces++; + if (consecutiveSpaces == tabIndent) + { + sb.Append(value: '\t'); + consecutiveSpaces = 0; + } + } + else if (line[i] == '\t') + { + sb.Append(value: '\t'); + // if we had say 3 spaces then a tab and tabIndent was 4 then + // we would want to simply replace all of that with 1 tab + consecutiveSpaces = 0; + } + else + { + break; + } + + if (i < line.Length) + sb.Append(line.Substring(i - consecutiveSpaces)); + return sb.ToString(); + } + + public static bool IsLetterDigitOrUnderscore(char c) + { + if (!char.IsLetterOrDigit(c)) + return c == '_'; + return true; + } + + /// + /// This method returns the expression before a specified offset. + /// That method is used in code completion to determine the expression given + /// to the parser for type resolve. + /// + public static string GetExpressionBeforeOffset(TextArea textArea, int initialOffset) + { + var document = textArea.Document; + var offset = initialOffset; + while (offset - 1 > 0) + switch (document.GetCharAt(offset - 1)) + { + case '\n': + case '\r': + case '}': + goto done; +// offset = SearchBracketBackward(document, offset - 2, '{','}'); +// break; + case ']': + offset = SearchBracketBackward(document, offset - 2, openBracket: '[', closingBracket: ']'); + break; + case ')': + offset = SearchBracketBackward(document, offset - 2, openBracket: '(', closingBracket: ')'); + break; + case '.': + --offset; + break; + case '"': + if (offset < initialOffset - 1) + return null; + return "\"\""; + case '\'': + if (offset < initialOffset - 1) + return null; + return "'a'"; + case '>': + if (document.GetCharAt(offset - 2) == '-') + { + offset -= 2; + break; + } + + goto done; + default: + if (char.IsWhiteSpace(document.GetCharAt(offset - 1))) + { + --offset; + break; + } + + var start = offset - 1; + if (!IsLetterDigitOrUnderscore(document.GetCharAt(start))) + goto done; + + while (start > 0 && IsLetterDigitOrUnderscore(document.GetCharAt(start - 1))) + --start; + var word = document.GetText(start, offset - start).Trim(); + switch (word) + { + case "ref": + case "out": + case "in": + case "return": + case "throw": + case "case": + goto done; + } + + if (word.Length > 0 && !IsLetterDigitOrUnderscore(word[index: 0])) + goto done; + offset = start; + break; + } + done: + //// simple exit fails when : is inside comment line or any other character + //// we have to check if we got several ids in resulting line, which usually happens when + //// id. is typed on next line after comment one + //// Would be better if lexer would parse properly such expressions. However this will cause + //// modifications in this area too - to get full comment line and remove it afterwards + if (offset < 0) + return string.Empty; + + var resText = document.GetText(offset, textArea.Caret.Offset - offset).Trim(); + var pos = resText.LastIndexOf(value: '\n'); + if (pos >= 0) + offset += pos + 1; + var expression = document.GetText(offset, textArea.Caret.Offset - offset).Trim(); + return expression; + } + + public static CharacterType GetCharacterType(char c) + { + if (IsLetterDigitOrUnderscore(c)) + return CharacterType.LetterDigitOrUnderscore; + if (char.IsWhiteSpace(c)) + return CharacterType.WhiteSpace; + return CharacterType.Other; + } + + public static int GetFirstNonWSChar(IDocument document, int offset) + { + while (offset < document.TextLength && char.IsWhiteSpace(document.GetCharAt(offset))) + ++offset; + return offset; + } + + public static int FindWordEnd(IDocument document, int offset) + { + var line = document.GetLineSegmentForOffset(offset); + var endPos = line.Offset + line.Length; + while (offset < endPos && IsLetterDigitOrUnderscore(document.GetCharAt(offset))) + ++offset; + + return offset; + } + + public static int FindWordStart(IDocument document, int offset) + { + var line = document.GetLineSegmentForOffset(offset); + var lineOffset = line.Offset; + while (offset > lineOffset && IsLetterDigitOrUnderscore(document.GetCharAt(offset - 1))) + --offset; + + return offset; + } + + // go forward to the start of the next word + // if the cursor is at the start or in the middle of a word we move to the end of the word + // and then past any whitespace that follows it + // if the cursor is at the start or in the middle of some whitespace we move to the start of the + // next word + public static int FindNextWordStart(IDocument document, int offset) + { +// var originalOffset = offset; + var line = document.GetLineSegmentForOffset(offset); + var endPos = line.Offset + line.Length; + // lets go to the end of the word, whitespace or operator + var t = GetCharacterType(document.GetCharAt(offset)); + while (offset < endPos && GetCharacterType(document.GetCharAt(offset)) == t) + ++offset; + + // now we're at the end of the word, lets find the start of the next one by skipping whitespace + while (offset < endPos && GetCharacterType(document.GetCharAt(offset)) == CharacterType.WhiteSpace) + ++offset; + + return offset; + } + + // go back to the start of the word we are on + // if we are already at the start of a word or if we are in whitespace, then go back + // to the start of the previous word + public static int FindPrevWordStart(IDocument document, int offset) + { +// var originalOffset = offset; + if (offset > 0) + { + var line = document.GetLineSegmentForOffset(offset); + var t = GetCharacterType(document.GetCharAt(offset - 1)); + while (offset > line.Offset && GetCharacterType(document.GetCharAt(offset - 1)) == t) + --offset; + + // if we were in whitespace, and now we're at the end of a word or operator, go back to the beginning of it + if (t == CharacterType.WhiteSpace && offset > line.Offset) + { + t = GetCharacterType(document.GetCharAt(offset - 1)); + while (offset > line.Offset && GetCharacterType(document.GetCharAt(offset - 1)) == t) + --offset; + } + } + + return offset; + } + + public static string GetLineAsString(IDocument document, int lineNumber) + { + var line = document.GetLineSegment(lineNumber); + return document.GetText(line.Offset, line.Length); + } + + public static int SearchBracketBackward(IDocument document, int offset, char openBracket, char closingBracket) + { + return document.FormattingStrategy.SearchBracketBackward(document, offset, openBracket, closingBracket); + } + + public static int SearchBracketForward(IDocument document, int offset, char openBracket, char closingBracket) + { + return document.FormattingStrategy.SearchBracketForward(document, offset, openBracket, closingBracket); + } + + /// + /// Returns true, if the line lineNumber is empty or filled with whitespaces. + /// + public static bool IsEmptyLine(IDocument document, int lineNumber) + { + return IsEmptyLine(document, document.GetLineSegment(lineNumber)); + } + + /// + /// Returns true, if the line lineNumber is empty or filled with whitespaces. + /// + public static bool IsEmptyLine(IDocument document, LineSegment line) + { + for (var i = line.Offset; i < line.Offset + line.Length; ++i) + { + var ch = document.GetCharAt(i); + if (!char.IsWhiteSpace(ch)) + return false; + } + + return true; + } + + private static bool IsWordPart(char ch) + { + return IsLetterDigitOrUnderscore(ch) || ch == '.'; + } + + public static string GetWordAt(IDocument document, int offset) + { + if (offset < 0 || offset >= document.TextLength - 1 || !IsWordPart(document.GetCharAt(offset))) + return string.Empty; + var startOffset = offset; + var endOffset = offset; + while (startOffset > 0 && IsWordPart(document.GetCharAt(startOffset - 1))) + --startOffset; + + while (endOffset < document.TextLength - 1 && IsWordPart(document.GetCharAt(endOffset + 1))) + ++endOffset; + + Debug.Assert(endOffset >= startOffset); + return document.GetText(startOffset, endOffset - startOffset + 1); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/AbstractMargin.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/AbstractMargin.cs new file mode 100644 index 0000000..eb6ff96 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/AbstractMargin.cs @@ -0,0 +1,76 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Drawing; +using System.Windows.Forms; +using ICSharpCode.TextEditor.Document; + +namespace ICSharpCode.TextEditor +{ + public delegate void MarginMouseEventHandler(AbstractMargin sender, Point mousepos, MouseButtons mouseButtons); + + public delegate void MarginPaintEventHandler(AbstractMargin sender, Graphics g, Rectangle rect); + + /// + /// This class views the line numbers and folding markers. + /// + public abstract class AbstractMargin + { + [CLSCompliant(isCompliant: false)] protected Rectangle drawingPosition = new Rectangle(x: 0, y: 0, width: 0, height: 0); + + [CLSCompliant(isCompliant: false)] protected TextArea textArea; + + protected AbstractMargin(TextArea textArea) + { + this.textArea = textArea; + } + + public Rectangle DrawingPosition + { + get => drawingPosition; + set => drawingPosition = value; + } + + public TextArea TextArea => textArea; + + public IDocument Document => textArea.Document; + + public ITextEditorProperties TextEditorProperties => textArea.Document.TextEditorProperties; + + public virtual Cursor Cursor { get; set; } = Cursors.Default; + + public virtual int Width => -1; + + public virtual bool IsVisible => true; + + public virtual void HandleMouseDown(Point mousepos, MouseButtons mouseButtons) + { + MouseDown?.Invoke(this, mousepos, mouseButtons); + } + + public virtual void HandleMouseMove(Point mousepos, MouseButtons mouseButtons) + { + MouseMove?.Invoke(this, mousepos, mouseButtons); + } + + public virtual void HandleMouseLeave(EventArgs e) + { + MouseLeave?.Invoke(this, e); + } + + public virtual void Paint(Graphics g, Rectangle rect) + { + Painted?.Invoke(this, g, rect); + } + + public event MarginPaintEventHandler Painted; + public event MarginMouseEventHandler MouseDown; + public event MarginMouseEventHandler MouseMove; + public event EventHandler MouseLeave; + } +} diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/BracketHighlighter.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/BracketHighlighter.cs new file mode 100644 index 0000000..d3bf6bb --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/BracketHighlighter.cs @@ -0,0 +1,75 @@ +// +// +// +// +// $Revision$ +// + +using System; +using ICSharpCode.TextEditor.Document; + +namespace ICSharpCode.TextEditor +{ + public class Highlight + { + public Highlight(TextLocation openBrace, TextLocation closeBrace) + { + OpenBrace = openBrace; + CloseBrace = closeBrace; + } + + public TextLocation OpenBrace { get; set; } + public TextLocation CloseBrace { get; set; } + } + + public class BracketHighlightingSheme + { + public BracketHighlightingSheme(char opentag, char closingtag) + { + OpenTag = opentag; + ClosingTag = closingtag; + } + + public char OpenTag { get; set; } + + public char ClosingTag { get; set; } + + public Highlight GetHighlight(IDocument document, int offset) + { + int searchOffset; + if (document.TextEditorProperties.BracketMatchingStyle == BracketMatchingStyle.After) + searchOffset = offset; + else + searchOffset = offset + 1; + var word = document.GetCharAt(Math.Max(val1: 0, Math.Min(document.TextLength - 1, searchOffset))); + + var endP = document.OffsetToPosition(searchOffset); + if (word == OpenTag) + { + if (searchOffset < document.TextLength) + { + var bracketOffset = TextUtilities.SearchBracketForward(document, searchOffset + 1, OpenTag, ClosingTag); + if (bracketOffset >= 0) + { + var p = document.OffsetToPosition(bracketOffset); + return new Highlight(p, endP); + } + } + } + else if (word == ClosingTag) + { + if (searchOffset > 0) + { + var bracketOffset = TextUtilities.SearchBracketBackward(document, searchOffset - 1, OpenTag, ClosingTag); + if (bracketOffset >= 0) + { + var p = document.OffsetToPosition(bracketOffset); + return new Highlight(p, endP); + } + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/BrushRegistry.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/BrushRegistry.cs new file mode 100644 index 0000000..b6bb01c --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/BrushRegistry.cs @@ -0,0 +1,69 @@ +// +// +// +// +// $Revision$ +// + +using System.Collections.Generic; +using System.Drawing; + +namespace ICSharpCode.TextEditor +{ + /// + /// Contains brushes/pens for the text editor to speed up drawing. Re-Creation of brushes and pens + /// seems too costly. + /// + public class BrushRegistry + { + private static readonly Dictionary brushes = new Dictionary(); + private static readonly Dictionary pens = new Dictionary(); + private static readonly Dictionary dotPens = new Dictionary(); + + private static readonly float[] dotPattern = {1, 1, 1, 1}; + + public static Brush GetBrush(Color color) + { + lock (brushes) + { + if (!brushes.TryGetValue(color, out var brush)) + { + brush = new SolidBrush(color); + brushes.Add(color, brush); + } + + return brush; + } + } + + public static Pen GetPen(Color color) + { + lock (pens) + { + if (!pens.TryGetValue(color, out var pen)) + { + pen = new Pen(color); + pens.Add(color, pen); + } + + return pen; + } + } + + public static Pen GetDotPen(Color color) + { + lock (dotPens) + { + Pen pen; + if (!dotPens.TryGetValue(color, out pen)) + { + pen = new Pen(color); + pen.DashPattern = dotPattern; + dotPens.Add(color, pen); + } + + return pen; + } + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/Caret.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/Caret.cs new file mode 100644 index 0000000..5edb5bb --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/Caret.cs @@ -0,0 +1,526 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using ICSharpCode.TextEditor.Document; + +namespace ICSharpCode.TextEditor +{ + /// + /// In this enumeration are all caret modes listed. + /// + public enum CaretMode + { + /// + /// If the caret is in insert mode typed characters will be + /// inserted at the caret position + /// + InsertMode, + + /// + /// If the caret is in overwrite mode typed characters will + /// overwrite the character at the caret position + /// + OverwriteMode + } + + public class Caret : IDisposable + { + private static bool caretCreated; + private readonly CaretImplementation caretImplementation; + private CaretMode caretMode; + private int column; + private Point currentPos = new Point(x: -1, y: -1); + + private bool firePositionChangedAfterUpdateEnd; + private bool hidden = true; + private Ime ime; + private int line; + + private int oldLine = -1; + private bool outstandingUpdate; + private TextArea textArea; + + public Caret(TextArea textArea) + { + this.textArea = textArea; + textArea.GotFocus += OnTextAreaGotFocus; + textArea.LostFocus += OnTextAreaLostFocus; + if (Environment.OSVersion.Platform == PlatformID.Unix) + caretImplementation = new ManagedCaret(this); + else + caretImplementation = new Win32Caret(this); + } + + /// + /// The 'preferred' xPos in which the caret moves, when it is moved + /// up/down. Measured in pixels, not in characters! + /// + public int DesiredColumn { get; set; } + + /// + /// The current caret mode. + /// + public CaretMode CaretMode + { + get => caretMode; + set + { + caretMode = value; + OnCaretModeChanged(EventArgs.Empty); + } + } + + public int Line + { + get => line; + set + { + line = value; + ValidateCaretPos(); + UpdateCaretPosition(); + OnPositionChanged(EventArgs.Empty); + } + } + + public int Column + { + get => column; + set + { + column = value; + ValidateCaretPos(); + UpdateCaretPosition(); + OnPositionChanged(EventArgs.Empty); + } + } + + public TextLocation Position + { + get => new TextLocation(column, line); + set + { + line = value.Y; + column = value.X; + ValidateCaretPos(); + UpdateCaretPosition(); + OnPositionChanged(EventArgs.Empty); + } + } + + public int Offset => textArea.Document.PositionToOffset(Position); + + public Point ScreenPosition + { + get + { + var x = textArea.TextView.GetDrawingXPos(line, column); + return new Point( + textArea.TextView.DrawingPosition.X + x, + textArea.TextView.DrawingPosition.Y + + textArea.Document.GetVisibleLine(line)*textArea.TextView.FontHeight + - textArea.TextView.TextArea.VirtualTop.Y); + } + } + + public void Dispose() + { + textArea.GotFocus -= OnTextAreaGotFocus; + textArea.LostFocus -= OnTextAreaLostFocus; + textArea = null; + caretImplementation.Dispose(); + } + + public TextLocation ValidatePosition(TextLocation pos) + { + var line = Math.Max(val1: 0, Math.Min(textArea.Document.TotalNumberOfLines - 1, pos.Y)); + var column = Math.Max(val1: 0, pos.X); + + if (column == int.MaxValue || !textArea.TextEditorProperties.AllowCaretBeyondEOL) + { + var lineSegment = textArea.Document.GetLineSegment(line); + column = Math.Min(column, lineSegment.Length); + } + + return new TextLocation(column, line); + } + + /// + /// If the caret position is outside the document text bounds + /// it is set to the correct position by calling ValidateCaretPos. + /// + public void ValidateCaretPos() + { + line = Math.Max(val1: 0, Math.Min(textArea.Document.TotalNumberOfLines - 1, line)); + column = Math.Max(val1: 0, column); + + if (column == int.MaxValue || !textArea.TextEditorProperties.AllowCaretBeyondEOL) + { + var lineSegment = textArea.Document.GetLineSegment(line); + column = Math.Min(column, lineSegment.Length); + } + } + + private void CreateCaret() + { + while (!caretCreated) + switch (caretMode) + { + case CaretMode.InsertMode: + caretCreated = caretImplementation.Create(width: 2, textArea.TextView.FontHeight); + break; + case CaretMode.OverwriteMode: + caretCreated = caretImplementation.Create(textArea.TextView.SpaceWidth, textArea.TextView.FontHeight); + break; + } + if (currentPos.X < 0) + { + ValidateCaretPos(); + currentPos = ScreenPosition; + } + + caretImplementation.SetPosition(currentPos.X, currentPos.Y); + caretImplementation.Show(); + } + + public void RecreateCaret() + { + DisposeCaret(); + if (!hidden) + CreateCaret(); + } + + private void DisposeCaret() + { + if (caretCreated) + { + caretCreated = false; + caretImplementation.Hide(); + caretImplementation.Destroy(); + } + } + + private void OnTextAreaGotFocus(object sender, EventArgs e) + { + hidden = false; + if (!textArea.MotherTextEditorControl.IsInUpdate) + { + CreateCaret(); + UpdateCaretPosition(); + } + } + + private void OnTextAreaLostFocus(object sender, EventArgs e) + { + hidden = true; + DisposeCaret(); + } + + internal void OnEndUpdate() + { + if (outstandingUpdate) + UpdateCaretPosition(); + } + + private void PaintCaretLine(Graphics g) + { + if (!textArea.Document.TextEditorProperties.CaretLine) + return; + + var caretLineColor = textArea.Document.HighlightingStrategy.GetColorFor("CaretLine"); + + g.DrawLine( + BrushRegistry.GetDotPen(caretLineColor.Color), + currentPos.X, + y1: 0, + currentPos.X, + textArea.DisplayRectangle.Height); + } + + public void UpdateCaretPosition() + { + if (textArea.TextEditorProperties.CaretLine) + { + textArea.Invalidate(); + } + else + { + if (caretImplementation.RequireRedrawOnPositionChange) + { + textArea.UpdateLine(oldLine); + if (line != oldLine) + textArea.UpdateLine(line); + } + else + { + if (textArea.MotherTextAreaControl.TextEditorProperties.LineViewerStyle == LineViewerStyle.FullRow && oldLine != line) + { + textArea.UpdateLine(oldLine); + textArea.UpdateLine(line); + } + } + } + + oldLine = line; + + if (hidden || textArea.MotherTextEditorControl.IsInUpdate) + { + outstandingUpdate = true; + return; + } + + outstandingUpdate = false; + ValidateCaretPos(); + var lineNr = line; + var xpos = textArea.TextView.GetDrawingXPos(lineNr, column); + //LineSegment lineSegment = textArea.Document.GetLineSegment(lineNr); + var pos = ScreenPosition; + if (xpos >= 0) + { + CreateCaret(); + var success = caretImplementation.SetPosition(pos.X, pos.Y); + if (!success) + { + caretImplementation.Destroy(); + caretCreated = false; + UpdateCaretPosition(); + } + } + else + { + caretImplementation.Destroy(); + } + + // set the input method editor location + if (ime == null) + { + ime = new Ime(textArea.Handle, textArea.Document.TextEditorProperties.Font); + } + else + { + ime.HWnd = textArea.Handle; + ime.Font = textArea.Document.TextEditorProperties.Font; + } + + ime.SetIMEWindowLocation(pos.X, pos.Y); + + currentPos = pos; + } + + private void FirePositionChangedAfterUpdateEnd(object sender, EventArgs e) + { + OnPositionChanged(EventArgs.Empty); + } + + protected virtual void OnPositionChanged(EventArgs e) + { + if (textArea.MotherTextEditorControl.IsInUpdate) + { + if (firePositionChangedAfterUpdateEnd == false) + { + firePositionChangedAfterUpdateEnd = true; + textArea.Document.UpdateCommited += FirePositionChangedAfterUpdateEnd; + } + + return; + } + + if (firePositionChangedAfterUpdateEnd) + { + textArea.Document.UpdateCommited -= FirePositionChangedAfterUpdateEnd; + firePositionChangedAfterUpdateEnd = false; + } + + var foldings = textArea.Document.FoldingManager.GetFoldingsFromPosition(line, column); + var shouldUpdate = false; + foreach (var foldMarker in foldings) + { + shouldUpdate |= foldMarker.IsFolded; + foldMarker.IsFolded = false; + } + + if (shouldUpdate) + textArea.Document.FoldingManager.NotifyFoldingsChanged(EventArgs.Empty); + + PositionChanged?.Invoke(this, e); + textArea.ScrollToCaret(); + } + + protected virtual void OnCaretModeChanged(EventArgs e) + { + CaretModeChanged?.Invoke(this, e); + caretImplementation.Hide(); + caretImplementation.Destroy(); + caretCreated = false; + CreateCaret(); + caretImplementation.Show(); + } + + /// + /// Is called each time the caret is moved. + /// + public event EventHandler PositionChanged; + + /// + /// Is called each time the CaretMode has changed. + /// + public event EventHandler CaretModeChanged; + + #region Caret implementation + + internal void PaintCaret(Graphics g) + { + caretImplementation.PaintCaret(g); + PaintCaretLine(g); + } + + abstract class CaretImplementation : IDisposable + { + public bool RequireRedrawOnPositionChange; + + public virtual void Dispose() + { + Destroy(); + } + + public abstract bool Create(int width, int height); + public abstract void Hide(); + public abstract void Show(); + public abstract bool SetPosition(int x, int y); + public abstract void PaintCaret(Graphics g); + public abstract void Destroy(); + } + + private class ManagedCaret : CaretImplementation + { + private readonly Caret parentCaret; + private readonly TextArea textArea; + private readonly Timer timer = new Timer {Interval = 300}; + private bool blink = true; + private bool visible; + private int x, y, width, height; + + public ManagedCaret(Caret caret) + { + RequireRedrawOnPositionChange = true; + textArea = caret.textArea; + parentCaret = caret; + timer.Tick += CaretTimerTick; + } + + private void CaretTimerTick(object sender, EventArgs e) + { + blink = !blink; + if (visible) + textArea.UpdateLine(parentCaret.Line); + } + + public override bool Create(int width, int height) + { + visible = true; + this.width = width - 2; + this.height = height; + timer.Enabled = true; + return true; + } + + public override void Hide() + { + visible = false; + } + + public override void Show() + { + visible = true; + } + + public override bool SetPosition(int x, int y) + { + this.x = x - 1; + this.y = y; + return true; + } + + public override void PaintCaret(Graphics g) + { + if (visible && blink) + g.DrawRectangle(Pens.Gray, x, y, width, height); + } + + public override void Destroy() + { + visible = false; + timer.Enabled = false; + } + + public override void Dispose() + { + base.Dispose(); + timer.Dispose(); + } + } + + private class Win32Caret : CaretImplementation + { + private readonly TextArea textArea; + + public Win32Caret(Caret caret) + { + textArea = caret.textArea; + } + + [DllImport("User32.dll")] + private static extern bool CreateCaret(IntPtr hWnd, int hBitmap, int nWidth, int nHeight); + + [DllImport("User32.dll")] + private static extern bool SetCaretPos(int x, int y); + + [DllImport("User32.dll")] + private static extern bool DestroyCaret(); + + [DllImport("User32.dll")] + private static extern bool ShowCaret(IntPtr hWnd); + + [DllImport("User32.dll")] + private static extern bool HideCaret(IntPtr hWnd); + + public override bool Create(int width, int height) + { + return CreateCaret(textArea.Handle, hBitmap: 0, width, height); + } + + public override void Hide() + { + HideCaret(textArea.Handle); + } + + public override void Show() + { + ShowCaret(textArea.Handle); + } + + public override bool SetPosition(int x, int y) + { + return SetCaretPos(x, y); + } + + public override void PaintCaret(Graphics g) + { + } + + public override void Destroy() + { + DestroyCaret(); + } + } + + #endregion + } +} diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/CompletionWindow/AbstractCompletionWindow.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/CompletionWindow/AbstractCompletionWindow.cs new file mode 100644 index 0000000..53596f8 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/CompletionWindow/AbstractCompletionWindow.cs @@ -0,0 +1,207 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace ICSharpCode.TextEditor.Gui.CompletionWindow +{ + /// + /// Description of AbstractCompletionWindow. + /// + public abstract class AbstractCompletionWindow : Form + { + private static int shadowStatus; + private readonly Form parentForm; + private readonly Rectangle workingScreen; + protected TextEditorControl control; + protected Size drawingSize; + + protected AbstractCompletionWindow(Form parentForm, TextEditorControl control) + { + workingScreen = Screen.GetWorkingArea(parentForm); +// SetStyle(ControlStyles.Selectable, false); + this.parentForm = parentForm; + this.control = control; + + SetLocation(); + StartPosition = FormStartPosition.Manual; + FormBorderStyle = FormBorderStyle.None; + ShowInTaskbar = false; + MinimumSize = new Size(width: 1, height: 1); + Size = new Size(width: 1, height: 1); + } + + protected override CreateParams CreateParams + { + get + { + var p = base.CreateParams; + AddShadowToWindow(p); + return p; + } + } + + protected override bool ShowWithoutActivation => true; + + protected virtual void SetLocation() + { + var textArea = control.ActiveTextAreaControl.TextArea; + var caretPos = textArea.Caret.Position; + + var xpos = textArea.TextView.GetDrawingXPos(caretPos.Y, caretPos.X); + var rulerHeight = textArea.TextEditorProperties.ShowHorizontalRuler ? textArea.TextView.FontHeight : 0; + var pos = new Point( + textArea.TextView.DrawingPosition.X + xpos, + textArea.TextView.DrawingPosition.Y + textArea.Document.GetVisibleLine(caretPos.Y)*textArea.TextView.FontHeight + - textArea.TextView.TextArea.VirtualTop.Y + textArea.TextView.FontHeight + rulerHeight); + + var location = control.ActiveTextAreaControl.PointToScreen(pos); + + // set bounds + var bounds = new Rectangle(location, drawingSize); + + if (!workingScreen.Contains(bounds)) + { + if (bounds.Right > workingScreen.Right) + bounds.X = workingScreen.Right - bounds.Width; + if (bounds.Left < workingScreen.Left) + bounds.X = workingScreen.Left; + if (bounds.Top < workingScreen.Top) + bounds.Y = workingScreen.Top; + if (bounds.Bottom > workingScreen.Bottom) + { + bounds.Y = bounds.Y - bounds.Height - control.ActiveTextAreaControl.TextArea.TextView.FontHeight; + if (bounds.Bottom > workingScreen.Bottom) + bounds.Y = workingScreen.Bottom - bounds.Height; + } + } + + Bounds = bounds; + } + + /// + /// Adds a shadow to the create params if it is supported by the operating system. + /// + public static void AddShadowToWindow(CreateParams createParams) + { + if (shadowStatus == 0) + { + // Test OS version + shadowStatus = -1; // shadow not supported + if (Environment.OSVersion.Platform == PlatformID.Win32NT) + { + var ver = Environment.OSVersion.Version; + if (ver.Major > 5 || ver.Major == 5 && ver.Minor >= 1) + shadowStatus = 1; + } + } + + if (shadowStatus == 1) + createParams.ClassStyle |= 0x00020000; // set CS_DROPSHADOW + } + + protected void ShowCompletionWindow() + { + Owner = parentForm; + Enabled = true; + Show(); + + control.Focus(); + + if (parentForm != null) + parentForm.LocationChanged += ParentFormLocationChanged; + + control.ActiveTextAreaControl.VScrollBar.ValueChanged += ParentFormLocationChanged; + control.ActiveTextAreaControl.HScrollBar.ValueChanged += ParentFormLocationChanged; + control.ActiveTextAreaControl.TextArea.DoProcessDialogKey += ProcessTextAreaKey; + control.ActiveTextAreaControl.Caret.PositionChanged += CaretOffsetChanged; + control.ActiveTextAreaControl.TextArea.LostFocus += TextEditorLostFocus; + control.Resize += ParentFormLocationChanged; + + foreach (Control c in Controls) + c.MouseMove += ControlMouseMove; + } + + private void ParentFormLocationChanged(object sender, EventArgs e) + { + SetLocation(); + } + + public virtual bool ProcessKeyEvent(char ch) + { + return false; + } + + protected virtual bool ProcessTextAreaKey(Keys keyData) + { + if (!Visible) + return false; + switch (keyData) + { + case Keys.Escape: + Close(); + return true; + } + + return false; + } + + protected virtual void CaretOffsetChanged(object sender, EventArgs e) + { + } + + protected void TextEditorLostFocus(object sender, EventArgs e) + { + if (!control.ActiveTextAreaControl.TextArea.Focused && !ContainsFocus) + Close(); + } + + protected override void OnClosed(EventArgs e) + { + base.OnClosed(e); + + // take out the inserted methods + parentForm.LocationChanged -= ParentFormLocationChanged; + + foreach (Control c in Controls) + c.MouseMove -= ControlMouseMove; + + if (control.ActiveTextAreaControl.VScrollBar != null) + control.ActiveTextAreaControl.VScrollBar.ValueChanged -= ParentFormLocationChanged; + if (control.ActiveTextAreaControl.HScrollBar != null) + control.ActiveTextAreaControl.HScrollBar.ValueChanged -= ParentFormLocationChanged; + + control.ActiveTextAreaControl.TextArea.LostFocus -= TextEditorLostFocus; + control.ActiveTextAreaControl.Caret.PositionChanged -= CaretOffsetChanged; + control.ActiveTextAreaControl.TextArea.DoProcessDialogKey -= ProcessTextAreaKey; + control.Resize -= ParentFormLocationChanged; + Dispose(); + } + + protected override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + ControlMouseMove(this, e); + } + + /// + /// Invoked when the mouse moves over this form or any child control. + /// Shows the mouse cursor on the text area if it has been hidden. + /// + /// + /// Derived classes should attach this handler to the MouseMove event + /// of all created controls which are not added to the Controls + /// collection. + /// + protected void ControlMouseMove(object sender, MouseEventArgs e) + { + control.ActiveTextAreaControl.TextArea.ShowHiddenCursor(forceShow: false); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/CompletionWindow/CodeCompletionListView.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/CompletionWindow/CodeCompletionListView.cs new file mode 100644 index 0000000..9c41e94 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/CompletionWindow/CodeCompletionListView.cs @@ -0,0 +1,300 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace ICSharpCode.TextEditor.Gui.CompletionWindow +{ + /// + /// Description of CodeCompletionListView. + /// + public class CodeCompletionListView : UserControl + { + private readonly ICompletionData[] completionData; + private int firstItem; + private int selectedItem = -1; + + public CodeCompletionListView(ICompletionData[] completionData) + { + Array.Sort(completionData, DefaultCompletionData.Compare); + this.completionData = completionData; + +// this.KeyDown += new System.Windows.Forms.KeyEventHandler(OnKey); +// SetStyle(ControlStyles.Selectable, false); +// SetStyle(ControlStyles.UserPaint, true); +// SetStyle(ControlStyles.DoubleBuffer, false); + } + + public ImageList ImageList { get; set; } + + public int FirstItem + { + get => firstItem; + set + { + if (firstItem != value) + { + firstItem = value; + OnFirstItemChanged(EventArgs.Empty); + } + } + } + + public ICompletionData SelectedCompletionData + { + get + { + if (selectedItem < 0) + return null; + return completionData[selectedItem]; + } + } + + public int ItemHeight => Math.Max(ImageList.ImageSize.Height, (int)(Font.Height*1.25)); + + public int MaxVisibleItem => Height/ItemHeight; + + public void Close() + { + if (completionData != null) + Array.Clear(completionData, index: 0, completionData.Length); + base.Dispose(); + } + + public void SelectIndex(int index) + { + var oldSelectedItem = selectedItem; + var oldFirstItem = firstItem; + + index = Math.Max(val1: 0, index); + selectedItem = Math.Max(val1: 0, Math.Min(completionData.Length - 1, index)); + if (selectedItem < firstItem) + FirstItem = selectedItem; + if (firstItem + MaxVisibleItem <= selectedItem) + FirstItem = selectedItem - MaxVisibleItem + 1; + if (oldSelectedItem != selectedItem) + { + if (firstItem != oldFirstItem) + { + Invalidate(); + } + else + { + var min = Math.Min(selectedItem, oldSelectedItem) - firstItem; + var max = Math.Max(selectedItem, oldSelectedItem) - firstItem; + Invalidate(new Rectangle(x: 0, 1 + min*ItemHeight, Width, (max - min + 1)*ItemHeight)); + } + + OnSelectedItemChanged(EventArgs.Empty); + } + } + + public void CenterViewOn(int index) + { + var oldFirstItem = FirstItem; + var firstItem = index - MaxVisibleItem/2; + if (firstItem < 0) + FirstItem = 0; + else if (firstItem >= completionData.Length - MaxVisibleItem) + FirstItem = completionData.Length - MaxVisibleItem; + else + FirstItem = firstItem; + if (FirstItem != oldFirstItem) + Invalidate(); + } + + public void ClearSelection() + { + if (selectedItem < 0) + return; + var itemNum = selectedItem - firstItem; + selectedItem = -1; + Invalidate(new Rectangle(x: 0, itemNum*ItemHeight, Width, (itemNum + 1)*ItemHeight + 1)); + Update(); + OnSelectedItemChanged(EventArgs.Empty); + } + + public void PageDown() + { + SelectIndex(selectedItem + MaxVisibleItem); + } + + public void PageUp() + { + SelectIndex(selectedItem - MaxVisibleItem); + } + + public void SelectNextItem() + { + SelectIndex(selectedItem + 1); + } + + public void SelectPrevItem() + { + SelectIndex(selectedItem - 1); + } + + public void SelectItemWithStart(string startText) + { + if (string.IsNullOrEmpty(startText)) return; + var originalStartText = startText; + startText = startText.ToLower(); + var bestIndex = -1; + var bestQuality = -1; + // Qualities: 0 = match start + // 1 = match start case sensitive + // 2 = full match + // 3 = full match case sensitive + double bestPriority = 0; + for (var i = 0; i < completionData.Length; ++i) + { + var itemText = completionData[i].Text; + var lowerText = itemText.ToLower(); + if (lowerText.StartsWith(startText)) + { + var priority = completionData[i].Priority; + int quality; + if (lowerText == startText) + { + if (itemText == originalStartText) + quality = 3; + else + quality = 2; + } + else if (itemText.StartsWith(originalStartText)) + { + quality = 1; + } + else + { + quality = 0; + } + + bool useThisItem; + if (bestQuality < quality) + { + useThisItem = true; + } + else + { + if (bestIndex == selectedItem) + useThisItem = false; + else if (i == selectedItem) + useThisItem = bestQuality == quality; + else + useThisItem = bestQuality == quality && bestPriority < priority; + } + + if (useThisItem) + { + bestIndex = i; + bestPriority = priority; + bestQuality = quality; + } + } + } + + if (bestIndex < 0) + { + ClearSelection(); + } + else + { + if (bestIndex < firstItem || firstItem + MaxVisibleItem <= bestIndex) + { + SelectIndex(bestIndex); + CenterViewOn(bestIndex); + } + else + { + SelectIndex(bestIndex); + } + } + } + + protected override void OnPaint(PaintEventArgs pe) + { + float yPos = 1; + float itemHeight = ItemHeight; + // Maintain aspect ratio + var imageWidth = (int)(itemHeight*ImageList.ImageSize.Width/ImageList.ImageSize.Height); + + var curItem = firstItem; + var g = pe.Graphics; + while (curItem < completionData.Length && yPos < Height) + { + var drawingBackground = new RectangleF(x: 1, yPos, Width - 2, itemHeight); + if (drawingBackground.IntersectsWith(pe.ClipRectangle)) + { + // draw Background + if (curItem == selectedItem) + g.FillRectangle(SystemBrushes.Highlight, drawingBackground); + else + g.FillRectangle(SystemBrushes.Window, drawingBackground); + + // draw Icon + var xPos = 0; + if (completionData[curItem].ImageIndex < ImageList.Images.Count) + { + g.DrawImage(ImageList.Images[completionData[curItem].ImageIndex], new RectangleF(x: 1, yPos, imageWidth, itemHeight)); + xPos = imageWidth; + } + + // draw text + if (curItem == selectedItem) + g.DrawString(completionData[curItem].Text, Font, SystemBrushes.HighlightText, xPos, yPos); + else + g.DrawString(completionData[curItem].Text, Font, SystemBrushes.WindowText, xPos, yPos); + } + + yPos += itemHeight; + ++curItem; + } + + g.DrawRectangle(SystemPens.Control, new Rectangle(x: 0, y: 0, Width - 1, Height - 1)); + } + + protected override void OnMouseDown(MouseEventArgs e) + { + float yPos = 1; + var curItem = firstItem; + float itemHeight = ItemHeight; + + while (curItem < completionData.Length && yPos < Height) + { + var drawingBackground = new RectangleF(x: 1, yPos, Width - 2, itemHeight); + if (drawingBackground.Contains(e.X, e.Y)) + { + SelectIndex(curItem); + break; + } + + yPos += itemHeight; + ++curItem; + } + } + + protected override void OnPaintBackground(PaintEventArgs pe) + { + } + + protected virtual void OnSelectedItemChanged(EventArgs e) + { + SelectedItemChanged?.Invoke(this, e); + } + + protected virtual void OnFirstItemChanged(EventArgs e) + { + FirstItemChanged?.Invoke(this, e); + } + + public event EventHandler SelectedItemChanged; + public event EventHandler FirstItemChanged; + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/CompletionWindow/CodeCompletionWindow.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/CompletionWindow/CodeCompletionWindow.cs new file mode 100644 index 0000000..a11fd8c --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/CompletionWindow/CodeCompletionWindow.cs @@ -0,0 +1,379 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Diagnostics; +using System.Drawing; +using System.Windows.Forms; +using ICSharpCode.TextEditor.Document; +using ICSharpCode.TextEditor.Util; + +namespace ICSharpCode.TextEditor.Gui.CompletionWindow +{ + public class CodeCompletionWindow : AbstractCompletionWindow + { + private const int ScrollbarWidth = 16; + private const int MaxListLength = 10; + private readonly ICompletionData[] completionData; + private readonly ICompletionDataProvider dataProvider; + private readonly IDocument document; + private readonly bool fixedListViewWidth = true; + + private readonly MouseWheelHandler mouseWheelHandler = new MouseWheelHandler(); + private readonly bool showDeclarationWindow = true; + private readonly VScrollBar vScrollBar = new VScrollBar(); + private readonly Rectangle workingScreen; + private CodeCompletionListView codeCompletionListView; + private DeclarationViewWindow declarationViewWindow; + private int endOffset; + + private bool inScrollUpdate; + + private int startOffset; + + private CodeCompletionWindow(ICompletionDataProvider completionDataProvider, ICompletionData[] completionData, Form parentForm, TextEditorControl control, bool showDeclarationWindow, bool fixedListViewWidth) : base(parentForm, control) + { + dataProvider = completionDataProvider; + this.completionData = completionData; + document = control.Document; + this.showDeclarationWindow = showDeclarationWindow; + this.fixedListViewWidth = fixedListViewWidth; + + workingScreen = Screen.GetWorkingArea(Location); + startOffset = control.ActiveTextAreaControl.Caret.Offset + 1; + endOffset = startOffset; + if (completionDataProvider.PreSelection != null) + { + startOffset -= completionDataProvider.PreSelection.Length + 1; + endOffset--; + } + + codeCompletionListView = new CodeCompletionListView(completionData); + codeCompletionListView.ImageList = completionDataProvider.ImageList; + codeCompletionListView.Dock = DockStyle.Fill; + codeCompletionListView.SelectedItemChanged += CodeCompletionListViewSelectedItemChanged; + codeCompletionListView.DoubleClick += CodeCompletionListViewDoubleClick; + codeCompletionListView.Click += CodeCompletionListViewClick; + Controls.Add(codeCompletionListView); + + if (completionData.Length > MaxListLength) + { + vScrollBar.Dock = DockStyle.Right; + vScrollBar.Minimum = 0; + vScrollBar.Maximum = completionData.Length - 1; + vScrollBar.SmallChange = 1; + vScrollBar.LargeChange = MaxListLength; + codeCompletionListView.FirstItemChanged += CodeCompletionListViewFirstItemChanged; + Controls.Add(vScrollBar); + } + + drawingSize = GetListViewSize(); + SetLocation(); + + if (declarationViewWindow == null) + declarationViewWindow = new DeclarationViewWindow(parentForm); + SetDeclarationViewLocation(); + declarationViewWindow.ShowDeclarationViewWindow(); + declarationViewWindow.MouseMove += ControlMouseMove; + control.Focus(); + CodeCompletionListViewSelectedItemChanged(this, EventArgs.Empty); + + if (completionDataProvider.DefaultIndex >= 0) + codeCompletionListView.SelectIndex(completionDataProvider.DefaultIndex); + + if (completionDataProvider.PreSelection != null) + CaretOffsetChanged(this, EventArgs.Empty); + + vScrollBar.ValueChanged += VScrollBarValueChanged; + document.DocumentAboutToBeChanged += DocumentAboutToBeChanged; + } + + /// + /// When this flag is set, code completion closes if the caret moves to the + /// beginning of the allowed range. This is useful in Ctrl+Space and "complete when typing", + /// but not in dot-completion. + /// + public bool CloseWhenCaretAtBeginning { get; set; } + + public static CodeCompletionWindow ShowCompletionWindow(Form parent, TextEditorControl control, string fileName, ICompletionDataProvider completionDataProvider, char firstChar) + { + return ShowCompletionWindow(parent, control, fileName, completionDataProvider, firstChar, showDeclarationWindow: true, fixedListViewWidth: true); + } + + public static CodeCompletionWindow ShowCompletionWindow(Form parent, TextEditorControl control, string fileName, ICompletionDataProvider completionDataProvider, char firstChar, bool showDeclarationWindow, bool fixedListViewWidth) + { + var completionData = completionDataProvider.GenerateCompletionData(fileName, control.ActiveTextAreaControl.TextArea, firstChar); + if (completionData == null || completionData.Length == 0) + return null; + var codeCompletionWindow = new CodeCompletionWindow(completionDataProvider, completionData, parent, control, showDeclarationWindow, fixedListViewWidth); + codeCompletionWindow.CloseWhenCaretAtBeginning = firstChar == '\0'; + codeCompletionWindow.ShowCompletionWindow(); + return codeCompletionWindow; + } + + private void CodeCompletionListViewFirstItemChanged(object sender, EventArgs e) + { + if (inScrollUpdate) return; + inScrollUpdate = true; + vScrollBar.Value = Math.Min(vScrollBar.Maximum, codeCompletionListView.FirstItem); + inScrollUpdate = false; + } + + private void VScrollBarValueChanged(object sender, EventArgs e) + { + if (inScrollUpdate) return; + inScrollUpdate = true; + codeCompletionListView.FirstItem = vScrollBar.Value; + codeCompletionListView.Refresh(); + control.ActiveTextAreaControl.TextArea.Focus(); + inScrollUpdate = false; + } + + private void SetDeclarationViewLocation() + { + // This method uses the side with more free space + var leftSpace = Bounds.Left - workingScreen.Left; + var rightSpace = workingScreen.Right - Bounds.Right; + Point pos; + // The declaration view window has better line break when used on + // the right side, so prefer the right side to the left. + if (rightSpace*2 > leftSpace) + { + declarationViewWindow.FixedWidth = false; + pos = new Point(Bounds.Right, Bounds.Top); + if (declarationViewWindow.Location != pos) + declarationViewWindow.Location = pos; + } + else + { + declarationViewWindow.Width = declarationViewWindow.GetRequiredLeftHandSideWidth(new Point(Bounds.Left, Bounds.Top)); + declarationViewWindow.FixedWidth = true; + if (Bounds.Left < declarationViewWindow.Width) + pos = new Point(x: 0, Bounds.Top); + else + pos = new Point(Bounds.Left - declarationViewWindow.Width, Bounds.Top); + if (declarationViewWindow.Location != pos) + declarationViewWindow.Location = pos; + declarationViewWindow.Refresh(); + } + } + + protected override void SetLocation() + { + base.SetLocation(); + if (declarationViewWindow != null) + SetDeclarationViewLocation(); + } + + public void HandleMouseWheel(MouseEventArgs e) + { + var scrollDistance = mouseWheelHandler.GetScrollAmount(e); + if (scrollDistance == 0) + return; + if (control.TextEditorProperties.MouseWheelScrollDown) + scrollDistance = -scrollDistance; + var newValue = vScrollBar.Value + vScrollBar.SmallChange*scrollDistance; + vScrollBar.Value = Math.Max(vScrollBar.Minimum, Math.Min(vScrollBar.Maximum - vScrollBar.LargeChange + 1, newValue)); + } + + private void CodeCompletionListViewSelectedItemChanged(object sender, EventArgs e) + { + var data = codeCompletionListView.SelectedCompletionData; + if (showDeclarationWindow && !string.IsNullOrEmpty(data?.Description)) + { + declarationViewWindow.Description = data.Description; + SetDeclarationViewLocation(); + } + else + { + declarationViewWindow.Description = null; + } + } + + public override bool ProcessKeyEvent(char ch) + { + switch (dataProvider.ProcessKey(ch)) + { + case CompletionDataProviderKeyResult.BeforeStartKey: + // increment start+end, then process as normal char + ++startOffset; + ++endOffset; + return base.ProcessKeyEvent(ch); + case CompletionDataProviderKeyResult.NormalKey: + // just process normally + return base.ProcessKeyEvent(ch); + case CompletionDataProviderKeyResult.InsertionKey: + return InsertSelectedItem(ch); + default: + throw new InvalidOperationException("Invalid return value of dataProvider.ProcessKey"); + } + } + + private void DocumentAboutToBeChanged(object sender, DocumentEventArgs e) + { + // => startOffset test required so that this startOffset/endOffset are not incremented again + // for BeforeStartKey characters + if (e.Offset >= startOffset && e.Offset <= endOffset) + { + if (e.Length > 0) + endOffset -= e.Length; + if (!string.IsNullOrEmpty(e.Text)) + endOffset += e.Text.Length; + } + } + + protected override void CaretOffsetChanged(object sender, EventArgs e) + { + var offset = control.ActiveTextAreaControl.Caret.Offset; + if (offset == startOffset) + { + if (CloseWhenCaretAtBeginning) + Close(); + return; + } + + if (offset < startOffset || offset > endOffset) + Close(); + else + codeCompletionListView.SelectItemWithStart(control.Document.GetText(startOffset, offset - startOffset)); + } + + protected override bool ProcessTextAreaKey(Keys keyData) + { + if (!Visible) + return false; + + switch (keyData) + { + case Keys.Home: + codeCompletionListView.SelectIndex(index: 0); + return true; + case Keys.End: + codeCompletionListView.SelectIndex(completionData.Length - 1); + return true; + case Keys.PageDown: + codeCompletionListView.PageDown(); + return true; + case Keys.PageUp: + codeCompletionListView.PageUp(); + return true; + case Keys.Down: + codeCompletionListView.SelectNextItem(); + return true; + case Keys.Up: + codeCompletionListView.SelectPrevItem(); + return true; + case Keys.Tab: + InsertSelectedItem(ch: '\t'); + return true; + case Keys.Return: + InsertSelectedItem(ch: '\n'); + return true; + } + + return base.ProcessTextAreaKey(keyData); + } + + private void CodeCompletionListViewDoubleClick(object sender, EventArgs e) + { + InsertSelectedItem(ch: '\0'); + } + + private void CodeCompletionListViewClick(object sender, EventArgs e) + { + control.ActiveTextAreaControl.TextArea.Focus(); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + document.DocumentAboutToBeChanged -= DocumentAboutToBeChanged; + if (codeCompletionListView != null) + { + codeCompletionListView.Dispose(); + codeCompletionListView = null; + } + + if (declarationViewWindow != null) + { + declarationViewWindow.Dispose(); + declarationViewWindow = null; + } + } + + base.Dispose(disposing); + } + + private bool InsertSelectedItem(char ch) + { + document.DocumentAboutToBeChanged -= DocumentAboutToBeChanged; + var data = codeCompletionListView.SelectedCompletionData; + var result = false; + if (data != null) + { + control.BeginUpdate(); + + try + { + if (endOffset - startOffset > 0) + control.Document.Remove(startOffset, endOffset - startOffset); + Debug.Assert(startOffset <= document.TextLength); + result = dataProvider.InsertAction(data, control.ActiveTextAreaControl.TextArea, startOffset, ch); + } + finally + { + control.EndUpdate(); + } + } + + Close(); + return result; + } + + private Size GetListViewSize() + { + var height = codeCompletionListView.ItemHeight*Math.Min(MaxListLength, completionData.Length); + var width = codeCompletionListView.ItemHeight*10; + if (!fixedListViewWidth) + width = GetListViewWidth(width, height); + return new Size(width, height); + } + + /// + /// Gets the list view width large enough to handle the longest completion data + /// text string. + /// + /// The default width of the list view. + /// + /// The height of the list view. This is + /// used to determine if the scrollbar is visible. + /// + /// + /// The list view width to accommodate the longest completion + /// data text string; otherwise the default width. + /// + private int GetListViewWidth(int defaultWidth, int height) + { + float width = defaultWidth; + using (var graphics = codeCompletionListView.CreateGraphics()) + { + for (var i = 0; i < completionData.Length; ++i) + { + var itemWidth = graphics.MeasureString(completionData[i].Text, codeCompletionListView.Font).Width; + if (itemWidth > width) + width = itemWidth; + } + } + + float totalItemsHeight = codeCompletionListView.ItemHeight*completionData.Length; + if (totalItemsHeight > height) + width += ScrollbarWidth; // Compensate for scroll bar. + return (int)width; + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/CompletionWindow/DeclarationViewWindow.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/CompletionWindow/DeclarationViewWindow.cs new file mode 100644 index 0000000..4f07dbe --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/CompletionWindow/DeclarationViewWindow.cs @@ -0,0 +1,117 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Drawing; +using System.Windows.Forms; +using ICSharpCode.TextEditor.Util; + +namespace ICSharpCode.TextEditor.Gui.CompletionWindow +{ + public interface IDeclarationViewWindow + { + string Description { get; set; } + + void ShowDeclarationViewWindow(); + void CloseDeclarationViewWindow(); + } + + public class DeclarationViewWindow : Form, IDeclarationViewWindow + { + private string description = string.Empty; + + public bool HideOnClick; + + public DeclarationViewWindow(Form parent) + { + SetStyle(ControlStyles.Selectable, value: false); + StartPosition = FormStartPosition.Manual; + FormBorderStyle = FormBorderStyle.None; + Owner = parent; + ShowInTaskbar = false; + Size = new Size(width: 0, height: 0); + CreateHandle(); + } + + public bool FixedWidth { get; set; } + + protected override CreateParams CreateParams + { + get + { + var p = base.CreateParams; + AbstractCompletionWindow.AddShadowToWindow(p); + return p; + } + } + + protected override bool ShowWithoutActivation => true; + + public string Description + { + get => description; + set + { + description = value; + if (value == null && Visible) + { + Visible = false; + } + else if (value != null) + { + if (!Visible) ShowDeclarationViewWindow(); + Refresh(); + } + } + } + + public void ShowDeclarationViewWindow() + { + Show(); + } + + public void CloseDeclarationViewWindow() + { + Close(); + Dispose(); + } + + public int GetRequiredLeftHandSideWidth(Point p) + { + if (!string.IsNullOrEmpty(description)) + using (var g = CreateGraphics()) + { + var s = TipPainterTools.GetLeftHandSideDrawingSizeHelpTipFromCombinedDescription(this, g, Font, countMessage: null, description, p); + return s.Width; + } + + return 0; + } + + protected override void OnClick(EventArgs e) + { + base.OnClick(e); + if (HideOnClick) Hide(); + } + + protected override void OnPaint(PaintEventArgs pe) + { + if (!string.IsNullOrEmpty(description)) + { + if (FixedWidth) + TipPainterTools.DrawFixedWidthHelpTipFromCombinedDescription(this, pe.Graphics, Font, countMessage: null, description); + else + TipPainterTools.DrawHelpTipFromCombinedDescription(this, pe.Graphics, Font, countMessage: null, description); + } + } + + protected override void OnPaintBackground(PaintEventArgs pe) + { + pe.Graphics.FillRectangle(SystemBrushes.Info, pe.ClipRectangle); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/CompletionWindow/ICompletionData.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/CompletionWindow/ICompletionData.cs new file mode 100644 index 0000000..f8afaad --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/CompletionWindow/ICompletionData.cs @@ -0,0 +1,81 @@ +// +// +// +// +// $Revision$ +// + +using System; + +namespace ICSharpCode.TextEditor.Gui.CompletionWindow +{ + public interface ICompletionData + { + int ImageIndex { get; } + + string Text { get; set; } + + string Description { get; } + + /// + /// Gets a priority value for the completion data item. + /// When selecting items by their start characters, the item with the highest + /// priority is selected first. + /// + double Priority { get; } + + /// + /// Insert the element represented by the completion data into the text + /// editor. + /// + /// TextArea to insert the completion data in. + /// + /// Character that should be inserted after the completion data. + /// \0 when no character should be inserted. + /// + /// + /// Returns true when the insert action has processed the character + /// ; false when the character was not processed. + /// + bool InsertAction(TextArea textArea, char ch); + } + + public class DefaultCompletionData : ICompletionData + { + public DefaultCompletionData(string text, int imageIndex) + { + Text = text; + ImageIndex = imageIndex; + } + + public DefaultCompletionData(string text, string description, int imageIndex) + { + Text = text; + Description = description; + ImageIndex = imageIndex; + } + + public int ImageIndex { get; } + + public string Text { get; set; } + + public virtual string Description { get; } + + public double Priority { get; set; } + + public virtual bool InsertAction(TextArea textArea, char ch) + { + textArea.InsertString(Text); + return false; + } + + public static int Compare(ICompletionData a, ICompletionData b) + { + if (a == null) + throw new ArgumentNullException(nameof(a)); + if (b == null) + throw new ArgumentNullException(nameof(b)); + return string.Compare(a.Text, b.Text, StringComparison.InvariantCultureIgnoreCase); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/CompletionWindow/ICompletionDataProvider.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/CompletionWindow/ICompletionDataProvider.cs new file mode 100644 index 0000000..aef8080 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/CompletionWindow/ICompletionDataProvider.cs @@ -0,0 +1,59 @@ +// +// +// +// +// $Revision$ +// + +using System.Windows.Forms; + +namespace ICSharpCode.TextEditor.Gui.CompletionWindow +{ + public interface ICompletionDataProvider + { + ImageList ImageList { get; } + + string PreSelection { get; } + + /// + /// Gets the index of the element in the list that is chosen by default. + /// + int DefaultIndex { get; } + + /// + /// Processes a keypress. Returns the action to be run with the key. + /// + CompletionDataProviderKeyResult ProcessKey(char key); + + /// + /// Executes the insertion. The provider should set the caret position and then + /// call data.InsertAction. + /// + bool InsertAction(ICompletionData data, TextArea textArea, int insertionOffset, char key); + + /// + /// Generates the completion data. This method is called by the text editor control. + /// + ICompletionData[] GenerateCompletionData(string fileName, TextArea textArea, char charTyped); + } + + public enum CompletionDataProviderKeyResult + { + /// + /// Normal key, used to choose an entry from the completion list + /// + NormalKey, + + /// + /// This key triggers insertion of the completed expression + /// + InsertionKey, + + /// + /// Increment both start and end offset of completion region when inserting this + /// key. Can be used to insert whitespace (or other characters) in front of the expression + /// while the completion window is open. + /// + BeforeStartKey + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/DrawableLine.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/DrawableLine.cs new file mode 100644 index 0000000..c69376d --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/DrawableLine.cs @@ -0,0 +1,193 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using System.Drawing; +using ICSharpCode.TextEditor.Document; + +namespace ICSharpCode.TextEditor +{ + /// + /// A class that is able to draw a line on any control (outside the text editor) + /// + public class DrawableLine + { + private static readonly StringFormat sf = (StringFormat)StringFormat.GenericTypographic.Clone(); + private readonly Font boldMonospacedFont; + private readonly Font monospacedFont; + + private readonly List words = new List(); + private SizeF spaceSize; + + public DrawableLine(IDocument document, LineSegment line, Font monospacedFont, Font boldMonospacedFont) + { + this.monospacedFont = monospacedFont; + this.boldMonospacedFont = boldMonospacedFont; + if (line.Words != null) + foreach (var word in line.Words) + if (word.Type == TextWordType.Space) + words.Add(SimpleTextWord.Space); + else if (word.Type == TextWordType.Tab) + words.Add(SimpleTextWord.Tab); + else + words.Add(new SimpleTextWord(TextWordType.Word, word.Word, word.Bold, word.Color)); + else + words.Add(new SimpleTextWord(TextWordType.Word, document.GetText(line), Bold: false, SystemColors.WindowText)); + } + + public int LineLength + { + get + { + var length = 0; + foreach (var word in words) + length += word.Word.Length; + return length; + } + } + + public void SetBold(int startIndex, int endIndex, bool bold) + { + if (startIndex < 0) + throw new ArgumentException("startIndex must be >= 0"); + if (startIndex > endIndex) + throw new ArgumentException("startIndex must be <= endIndex"); + if (startIndex == endIndex) return; + var pos = 0; + for (var i = 0; i < words.Count; i++) + { + var word = words[i]; + if (pos >= endIndex) + break; + var wordEnd = pos + word.Word.Length; + // 3 possibilities: + if (startIndex <= pos && endIndex >= wordEnd) + { + // word is fully in region: + word.Bold = bold; + } + else if (startIndex <= pos) + { + // beginning of word is in region + var inRegionLength = endIndex - pos; + var newWord = new SimpleTextWord(word.Type, word.Word.Substring(inRegionLength), word.Bold, word.Color); + words.Insert(i + 1, newWord); + + word.Bold = bold; + word.Word = word.Word.Substring(startIndex: 0, inRegionLength); + } + else if (startIndex < wordEnd) + { + // end of word is in region (or middle of word is in region) + var notInRegionLength = startIndex - pos; + + var newWord = new SimpleTextWord(word.Type, word.Word.Substring(notInRegionLength), word.Bold, word.Color); + // newWord.Bold will be set in the next iteration + words.Insert(i + 1, newWord); + + word.Word = word.Word.Substring(startIndex: 0, notInRegionLength); + } + + pos = wordEnd; + } + } + + public static float DrawDocumentWord(Graphics g, string word, PointF position, Font font, Color foreColor) + { + if (string.IsNullOrEmpty(word)) + return 0f; + var wordSize = g.MeasureString(word, font, width: 32768, sf); + + g.DrawString( + word, + font, + BrushRegistry.GetBrush(foreColor), + position, + sf); + return wordSize.Width; + } + + public SizeF GetSpaceSize(Graphics g) + { + if (spaceSize.IsEmpty) + spaceSize = g.MeasureString("-", boldMonospacedFont, new PointF(x: 0, y: 0), sf); + return spaceSize; + } + + public void DrawLine(Graphics g, ref float xPos, float xOffset, float yPos, Color c) + { + var spaceSize = GetSpaceSize(g); + foreach (var word in words) + switch (word.Type) + { + case TextWordType.Space: + xPos += spaceSize.Width; + break; + case TextWordType.Tab: + var tabWidth = spaceSize.Width*4; + xPos += tabWidth; + xPos = (int)((xPos + 2)/tabWidth)*tabWidth; + break; + case TextWordType.Word: + xPos += DrawDocumentWord( + g, + word.Word, + new PointF(xPos + xOffset, yPos), + word.Bold ? boldMonospacedFont : monospacedFont, + c == Color.Empty ? word.Color : c + ); + break; + } + } + + public void DrawLine(Graphics g, ref float xPos, float xOffset, float yPos) + { + DrawLine(g, ref xPos, xOffset, yPos, Color.Empty); + } + + public float MeasureWidth(Graphics g, float xPos) + { + var spaceSize = GetSpaceSize(g); + foreach (var word in words) + switch (word.Type) + { + case TextWordType.Space: + xPos += spaceSize.Width; + break; + case TextWordType.Tab: + var tabWidth = spaceSize.Width*4; + xPos += tabWidth; + xPos = (int)((xPos + 2)/tabWidth)*tabWidth; + break; + case TextWordType.Word: + if (!string.IsNullOrEmpty(word.Word)) + xPos += g.MeasureString(word.Word, word.Bold ? boldMonospacedFont : monospacedFont, width: 32768, sf).Width; + break; + } + return xPos; + } + + private class SimpleTextWord + { + internal static readonly SimpleTextWord Space = new SimpleTextWord(TextWordType.Space, " ", Bold: false, SystemColors.WindowText); + internal static readonly SimpleTextWord Tab = new SimpleTextWord(TextWordType.Tab, "\t", Bold: false, SystemColors.WindowText); + internal readonly Color Color; + internal readonly TextWordType Type; + internal bool Bold; + internal string Word; + + public SimpleTextWord(TextWordType Type, string Word, bool Bold, Color Color) + { + this.Type = Type; + this.Word = Word; + this.Bold = Bold; + this.Color = Color; + } + } + } +} diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/FoldMargin.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/FoldMargin.cs new file mode 100644 index 0000000..0553c5f --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/FoldMargin.cs @@ -0,0 +1,265 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Windows.Forms; +using ICSharpCode.TextEditor.Document; + +namespace ICSharpCode.TextEditor +{ + /// + /// This class views the line numbers and folding markers. + /// + public class FoldMargin : AbstractMargin + { + private int selectedFoldLine = -1; + + public FoldMargin(TextArea textArea) : base(textArea) + { + } + + public override int Width => textArea.TextView.FontHeight; + + public override bool IsVisible => textArea.TextEditorProperties.EnableFolding; + + public override void Paint(Graphics g, Rectangle rect) + { + if (rect.Width <= 0 || rect.Height <= 0) + return; + var lineNumberPainterColor = textArea.Document.HighlightingStrategy.GetColorFor("LineNumbers"); + + for (var y = 0; y < (DrawingPosition.Height + textArea.TextView.VisibleLineDrawingRemainder)/textArea.TextView.FontHeight + 1; ++y) + { + var markerRectangle = new Rectangle( + DrawingPosition.X, + DrawingPosition.Top + y*textArea.TextView.FontHeight - textArea.TextView.VisibleLineDrawingRemainder, + DrawingPosition.Width, + textArea.TextView.FontHeight); + + if (rect.IntersectsWith(markerRectangle)) + { + g.FillRectangle(BrushRegistry.GetBrush(textArea.Enabled + ? lineNumberPainterColor.BackgroundColor + : SystemColors.InactiveBorder), + markerRectangle); + + if (textArea.Document.TextEditorProperties.EnableFolding) + { + // draw dotted separator line + g.DrawLine( + BrushRegistry.GetDotPen(lineNumberPainterColor.Color), + drawingPosition.X, + markerRectangle.Y, + drawingPosition.X, + markerRectangle.Bottom); + + var currentLine = textArea.Document.GetFirstLogicalLine(textArea.TextView.FirstPhysicalLine + y); + if (currentLine < textArea.Document.TotalNumberOfLines) + PaintFoldMarker(g, currentLine, markerRectangle); + } + } + } + } + + private void PaintFoldMarker(Graphics g, int lineNumber, Rectangle drawingRectangle) + { + var foldLineColor = textArea.Document.HighlightingStrategy.GetColorFor("FoldLine"); + var selectedLine = textArea.Document.HighlightingStrategy.GetColorFor("SelectedFoldLine"); + + var foldingsWithStart = textArea.Document.FoldingManager.GetFoldingsWithStart(lineNumber); + var foldingsBetween = textArea.Document.FoldingManager.GetFoldingsContainsLineNumber(lineNumber); + var foldingsWithEnd = textArea.Document.FoldingManager.GetFoldingsWithEnd(lineNumber); + + var isFoldStart = foldingsWithStart.Count > 0; + var isBetween = foldingsBetween.Count > 0; + var isFoldEnd = foldingsWithEnd.Count > 0; + + var isStartSelected = SelectedFoldingFrom(foldingsWithStart); + var isBetweenSelected = SelectedFoldingFrom(foldingsBetween); + var isEndSelected = SelectedFoldingFrom(foldingsWithEnd); + + bool SelectedFoldingFrom(IEnumerable list) + => list?.Any(t => selectedFoldLine == t.StartLine) == true; + + var foldMarkerSize = (int)Math.Round(textArea.TextView.FontHeight*0.57f); + foldMarkerSize -= foldMarkerSize%2; + var foldMarkerYPos = drawingRectangle.Y + (drawingRectangle.Height - foldMarkerSize)/2; + var xPos = drawingRectangle.X + (drawingRectangle.Width - foldMarkerSize)/2 + foldMarkerSize/2; + + if (isFoldStart) + { + var isVisible = true; + var moreLinedOpenFold = false; + foreach (var foldMarker in foldingsWithStart) + if (foldMarker.IsFolded) + isVisible = false; + else + moreLinedOpenFold = foldMarker.EndLine > foldMarker.StartLine; + + var isFoldEndFromUpperFold = false; + foreach (var foldMarker in foldingsWithEnd) + if (foldMarker.EndLine > foldMarker.StartLine && !foldMarker.IsFolded) + isFoldEndFromUpperFold = true; + + DrawFoldMarker( + g, new RectangleF( + drawingRectangle.X + (drawingRectangle.Width - foldMarkerSize)/2f, + foldMarkerYPos, + foldMarkerSize, + foldMarkerSize), + isVisible, + isStartSelected + ); + + // draw line above fold marker + if (isBetween || isFoldEndFromUpperFold) + g.DrawLine( + BrushRegistry.GetPen(isBetweenSelected ? selectedLine.Color : foldLineColor.Color), + xPos, + drawingRectangle.Top, + xPos, + foldMarkerYPos - 1); + + // draw line below fold marker + if (isBetween || moreLinedOpenFold) + g.DrawLine( + BrushRegistry.GetPen(isEndSelected || isStartSelected && isVisible || isBetweenSelected ? selectedLine.Color : foldLineColor.Color), + xPos, + foldMarkerYPos + foldMarkerSize + 1, + xPos, + drawingRectangle.Bottom); + } + else + { + if (isFoldEnd) + { + var midy = drawingRectangle.Top + drawingRectangle.Height/2; + + // draw fold end marker + g.DrawLine( + BrushRegistry.GetPen(isEndSelected ? selectedLine.Color : foldLineColor.Color), + xPos, + midy, + xPos + foldMarkerSize/2, + midy); + + // draw line above fold end marker + // must be drawn after fold marker because it might have a different color than the fold marker + g.DrawLine( + BrushRegistry.GetPen(isBetweenSelected || isEndSelected ? selectedLine.Color : foldLineColor.Color), + xPos, + drawingRectangle.Top, + xPos, + midy); + + // draw line below fold end marker + if (isBetween) + g.DrawLine( + BrushRegistry.GetPen(isBetweenSelected ? selectedLine.Color : foldLineColor.Color), + xPos, + midy + 1, + xPos, + drawingRectangle.Bottom); + } + else if (isBetween) + { + // just draw the line :) + g.DrawLine( + BrushRegistry.GetPen(isBetweenSelected ? selectedLine.Color : foldLineColor.Color), + xPos, + drawingRectangle.Top, + xPos, + drawingRectangle.Bottom); + } + } + } + + public override void HandleMouseMove(Point mousepos, MouseButtons mouseButtons) + { + var showFolding = textArea.Document.TextEditorProperties.EnableFolding; + var physicalLine = +((mousepos.Y + textArea.VirtualTop.Y)/textArea.TextView.FontHeight); + var realline = textArea.Document.GetFirstLogicalLine(physicalLine); + + if (!showFolding || realline < 0 || realline + 1 >= textArea.Document.TotalNumberOfLines) + return; + + var foldMarkers = textArea.Document.FoldingManager.GetFoldingsWithStart(realline); + var oldSelection = selectedFoldLine; + if (foldMarkers.Count > 0) + selectedFoldLine = realline; + else + selectedFoldLine = -1; + if (oldSelection != selectedFoldLine) + textArea.Refresh(this); + } + + public override void HandleMouseDown(Point mousepos, MouseButtons mouseButtons) + { + var showFolding = textArea.Document.TextEditorProperties.EnableFolding; + var physicalLine = +((mousepos.Y + textArea.VirtualTop.Y)/textArea.TextView.FontHeight); + var realline = textArea.Document.GetFirstLogicalLine(physicalLine); + + // focus the textarea if the user clicks on the line number view + textArea.Focus(); + + if (!showFolding || realline < 0 || realline + 1 >= textArea.Document.TotalNumberOfLines) + return; + + var foldMarkers = textArea.Document.FoldingManager.GetFoldingsWithStart(realline); + foreach (var fm in foldMarkers) + fm.IsFolded = !fm.IsFolded; + textArea.Document.FoldingManager.NotifyFoldingsChanged(EventArgs.Empty); + } + + public override void HandleMouseLeave(EventArgs e) + { + if (selectedFoldLine != -1) + { + selectedFoldLine = -1; + textArea.Refresh(this); + } + } + + #region Drawing functions + + private void DrawFoldMarker(Graphics g, RectangleF rectangle, bool isOpened, bool isSelected) + { + var foldMarkerColor = textArea.Document.HighlightingStrategy.GetColorFor("FoldMarker"); + var foldLineColor = textArea.Document.HighlightingStrategy.GetColorFor("FoldLine"); + var selectedLine = textArea.Document.HighlightingStrategy.GetColorFor("SelectedFoldLine"); + + var intRect = new Rectangle((int)rectangle.X, (int)rectangle.Y, (int)rectangle.Width, (int)rectangle.Height); + g.FillRectangle(BrushRegistry.GetBrush(foldMarkerColor.BackgroundColor), intRect); + g.DrawRectangle(BrushRegistry.GetPen(isSelected ? selectedLine.Color : foldLineColor.Color), intRect); + + var space = (int)Math.Round(rectangle.Height/8d) + 1; + var mid = intRect.Height/2 + intRect.Height%2; + + // draw minus + g.DrawLine( + BrushRegistry.GetPen(foldMarkerColor.Color), + rectangle.X + space, + rectangle.Y + mid, + rectangle.X + rectangle.Width - space, + rectangle.Y + mid); + + // draw plus + if (!isOpened) + g.DrawLine( + BrushRegistry.GetPen(foldMarkerColor.Color), + rectangle.X + mid, + rectangle.Y + space, + rectangle.X + mid, + rectangle.Y + rectangle.Height - space); + } + + #endregion + } +} diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/GutterMargin.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/GutterMargin.cs new file mode 100644 index 0000000..9e8884a --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/GutterMargin.cs @@ -0,0 +1,158 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Drawing; +using System.Reflection; +using System.Windows.Forms; +using ICSharpCode.TextEditor.Document; + +namespace ICSharpCode.TextEditor +{ + /// + /// This class views the line numbers and folding markers. + /// + public class GutterMargin : AbstractMargin, IDisposable + { + public static Cursor RightLeftCursor; + + private readonly StringFormat numberStringFormat = (StringFormat)StringFormat.GenericTypographic.Clone(); + + static GutterMargin() + { + using (var cursorStream = Assembly.GetCallingAssembly().GetManifestResourceStream("ICSharpCode.TextEditor.Resources.RightArrow.cur")) + { + if (cursorStream == null) + throw new Exception("could not find cursor resource"); + RightLeftCursor = new Cursor(cursorStream); + } + } + + public GutterMargin(TextArea textArea) : base(textArea) + { + numberStringFormat.LineAlignment = StringAlignment.Far; + numberStringFormat.FormatFlags = StringFormatFlags.MeasureTrailingSpaces | StringFormatFlags.FitBlackBox | + StringFormatFlags.NoWrap | StringFormatFlags.NoClip; + } + + public override Cursor Cursor => RightLeftCursor; + + public override int Width + => textArea.TextView.WideSpaceWidth*Math.Max(4, (int)Math.Log10(textArea.Document.TotalNumberOfLines) + 4); + + public override bool IsVisible => textArea.TextEditorProperties.ShowLineNumbers; + + public void Dispose() + { + numberStringFormat.Dispose(); + } + + public override void Paint(Graphics g, Rectangle rect) + { + if (rect.Width <= 0 || rect.Height <= 0) + return; + + var lineNumberPainterColor = textArea.Document.HighlightingStrategy.GetColorFor("LineNumbers"); + var fontHeight = textArea.TextView.FontHeight; + var fillBrush = textArea.Enabled + ? BrushRegistry.GetBrush(lineNumberPainterColor.BackgroundColor) + : SystemBrushes.InactiveBorder; + var drawBrush = BrushRegistry.GetBrush(lineNumberPainterColor.Color); + + for (var y = 0; y < (drawingPosition.Height + textArea.TextView.VisibleLineDrawingRemainder)/fontHeight + 1; ++y) + { + var ypos = drawingPosition.Y + fontHeight*y - textArea.TextView.VisibleLineDrawingRemainder; + var backgroundRectangle = new Rectangle(drawingPosition.X, ypos, drawingPosition.Width, fontHeight); + if (rect.IntersectsWith(backgroundRectangle)) + { + g.FillRectangle(fillBrush, backgroundRectangle); + var curLine = textArea.Document.GetFirstLogicalLine(textArea.Document.GetVisibleLine(textArea.TextView.FirstVisibleLine) + y); + + if (curLine < textArea.Document.TotalNumberOfLines) + g.DrawString( + (curLine + 1).ToString(), + lineNumberPainterColor.GetFont(TextEditorProperties.FontContainer), + drawBrush, + backgroundRectangle, + numberStringFormat); + } + } + } + + public override void HandleMouseDown(Point mousepos, MouseButtons mouseButtons) + { + textArea.SelectionManager.selectFrom.where = WhereFrom.Gutter; + var realline = textArea.TextView.GetLogicalLine(mousepos.Y); + if (realline >= 0 && realline < textArea.Document.TotalNumberOfLines) + { + // shift-select + TextLocation selectionStartPos; + if ((Control.ModifierKeys & Keys.Shift) != 0) + { + if (!textArea.SelectionManager.HasSomethingSelected && realline != textArea.Caret.Position.Y) + { + if (realline >= textArea.Caret.Position.Y) + { + // at or below starting selection, place the cursor on the next line + // nothing is selected so make a new selection from cursor + selectionStartPos = textArea.Caret.Position; + // whole line selection - start of line to start of next line + if (realline < textArea.Document.TotalNumberOfLines - 1) + { + textArea.SelectionManager.SetSelection(new DefaultSelection(textArea.Document, selectionStartPos, new TextLocation(column: 0, realline + 1))); + textArea.Caret.Position = new TextLocation(column: 0, realline + 1); + } + else + { + textArea.SelectionManager.SetSelection(new DefaultSelection(textArea.Document, selectionStartPos, new TextLocation(textArea.Document.GetLineSegment(realline).Length + 1, realline))); + textArea.Caret.Position = new TextLocation(textArea.Document.GetLineSegment(realline).Length + 1, realline); + } + } + else + { + // prior lines to starting selection, place the cursor on the same line as the new selection + // nothing is selected so make a new selection from cursor + selectionStartPos = textArea.Caret.Position; + // whole line selection - start of line to start of next line + textArea.SelectionManager.SetSelection(new DefaultSelection(textArea.Document, selectionStartPos, new TextLocation(selectionStartPos.X, selectionStartPos.Y))); + textArea.SelectionManager.ExtendSelection(new TextLocation(selectionStartPos.X, selectionStartPos.Y), new TextLocation(column: 0, realline)); + textArea.Caret.Position = new TextLocation(column: 0, realline); + } + } + else + { + // let MouseMove handle a shift-click in a gutter + var e = new MouseEventArgs(mouseButtons, clicks: 1, mousepos.X, mousepos.Y, delta: 0); + textArea.RaiseMouseMove(e); + } + } + else + { + // this is a new selection with no shift-key + // sync the textareamousehandler mouse location + // (fixes problem with clicking out into a menu then back to the gutter whilst + // there is a selection) + textArea.mousepos = mousepos; + + selectionStartPos = new TextLocation(column: 0, realline); + textArea.SelectionManager.ClearSelection(); + // whole line selection - start of line to start of next line + if (realline < textArea.Document.TotalNumberOfLines - 1) + { + textArea.SelectionManager.SetSelection(new DefaultSelection(textArea.Document, selectionStartPos, new TextLocation(selectionStartPos.X, selectionStartPos.Y + 1))); + textArea.Caret.Position = new TextLocation(selectionStartPos.X, selectionStartPos.Y + 1); + } + else + { + textArea.SelectionManager.SetSelection(new DefaultSelection(textArea.Document, new TextLocation(column: 0, realline), new TextLocation(textArea.Document.GetLineSegment(realline).Length + 1, selectionStartPos.Y))); + textArea.Caret.Position = new TextLocation(textArea.Document.GetLineSegment(realline).Length + 1, selectionStartPos.Y); + } + } + } + } + } +} diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/HRuler.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/HRuler.cs new file mode 100644 index 0000000..e075b33 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/HRuler.cs @@ -0,0 +1,55 @@ +// +// +// +// +// $Revision$ +// + +using System.Drawing; +using System.Windows.Forms; + +namespace ICSharpCode.TextEditor +{ + /// + /// Horizontal ruler - text column measuring ruler at the top of the text area. + /// + public class HRuler : Control + { + private readonly TextArea textArea; + + public HRuler(TextArea textArea) + { + this.textArea = textArea; + } + + protected override void OnPaint(PaintEventArgs e) + { + var g = e.Graphics; + var num = 0; + for (float x = textArea.TextView.DrawingPosition.Left; x < textArea.TextView.DrawingPosition.Right; x += textArea.TextView.WideSpaceWidth) + { + var offset = Height*2/3; + if (num%5 == 0) + offset = Height*4/5; + + if (num%10 == 0) + offset = 1; + ++num; + g.DrawLine( + Pens.Black, + (int)x, offset, (int)x, Height - offset); + } + } + + protected override void OnPaintBackground(PaintEventArgs e) + { + e.Graphics.FillRectangle( + Brushes.White, + new Rectangle( + x: 0, + y: 0, + Width, + Height)); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/IconBarMargin.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/IconBarMargin.cs new file mode 100644 index 0000000..107e9f5 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/IconBarMargin.cs @@ -0,0 +1,259 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms; +using ICSharpCode.TextEditor.Document; + +namespace ICSharpCode.TextEditor +{ + /// + /// This class views the line numbers and folding markers. + /// + public class IconBarMargin : AbstractMargin + { + private const int iconBarWidth = 18; + + public IconBarMargin(TextArea textArea) : base(textArea) + { + } + + public override int Width => iconBarWidth; + + public override bool IsVisible => textArea.TextEditorProperties.IsIconBarVisible; + + public override void Paint(Graphics g, Rectangle rect) + { + if (rect.Width <= 0 || rect.Height <= 0) + return; + // paint background + g.FillRectangle(SystemBrushes.Control, new Rectangle(drawingPosition.X, rect.Top, drawingPosition.Width - 1, rect.Height)); + g.DrawLine(SystemPens.ControlDark, drawingPosition.Right - 1, rect.Top, drawingPosition.Right - 1, rect.Bottom); + + // paint icons + foreach (var mark in textArea.Document.BookmarkManager.Marks) + { + var lineNumber = textArea.Document.GetVisibleLine(mark.LineNumber); + var lineHeight = textArea.TextView.FontHeight; + var yPos = lineNumber*lineHeight - textArea.VirtualTop.Y; + if (IsLineInsideRegion(yPos, yPos + lineHeight, rect.Y, rect.Bottom)) + { + if (lineNumber == textArea.Document.GetVisibleLine(mark.LineNumber - 1)) + continue; + mark.Draw(this, g, new Point(x: 0, yPos)); + } + } + + base.Paint(g, rect); + } + + public override void HandleMouseDown(Point mousePos, MouseButtons mouseButtons) + { + var clickedVisibleLine = (mousePos.Y + textArea.VirtualTop.Y)/textArea.TextView.FontHeight; + var lineNumber = textArea.Document.GetFirstLogicalLine(clickedVisibleLine); + + if ((mouseButtons & MouseButtons.Right) == MouseButtons.Right) + if (textArea.Caret.Line != lineNumber) + textArea.Caret.Line = lineNumber; + + IList marks = textArea.Document.BookmarkManager.Marks; + var marksInLine = new List(); + var oldCount = marks.Count; + foreach (var mark in marks) + if (mark.LineNumber == lineNumber) + marksInLine.Add(mark); + for (var i = marksInLine.Count - 1; i >= 0; i--) + { + var mark = marksInLine[i]; + if (mark.Click(textArea, new MouseEventArgs(mouseButtons, clicks: 1, mousePos.X, mousePos.Y, delta: 0))) + { + if (oldCount != marks.Count) + textArea.UpdateLine(lineNumber); + return; + } + } + + base.HandleMouseDown(mousePos, mouseButtons); + } + + private static bool IsLineInsideRegion(int top, int bottom, int regionTop, int regionBottom) + { + if (top >= regionTop && top <= regionBottom) + return true; + + if (regionTop > top && regionTop < bottom) + return true; + return false; + } + + #region Drawing functions + + public void DrawBreakpoint(Graphics g, int y, bool isEnabled, bool isHealthy) + { + var diameter = Math.Min(iconBarWidth - 2, textArea.TextView.FontHeight); + var rect = new Rectangle( + x: 1, + y + (textArea.TextView.FontHeight - diameter)/2, + diameter, + diameter); + + using (var path = new GraphicsPath()) + { + path.AddEllipse(rect); + using (var pthGrBrush = new PathGradientBrush(path)) + { + pthGrBrush.CenterPoint = new PointF(rect.Left + rect.Width/3, rect.Top + rect.Height/3); + pthGrBrush.CenterColor = Color.MistyRose; + Color[] colors = {isHealthy ? Color.Firebrick : Color.Olive}; + pthGrBrush.SurroundColors = colors; + + if (isEnabled) + { + g.FillEllipse(pthGrBrush, rect); + } + else + { + g.FillEllipse(SystemBrushes.Control, rect); + using (var pen = new Pen(pthGrBrush)) + { + g.DrawEllipse(pen, new Rectangle(rect.X + 1, rect.Y + 1, rect.Width - 2, rect.Height - 2)); + } + } + } + } + } + + public void DrawBookmark(Graphics g, int y, bool isEnabled) + { + var delta = textArea.TextView.FontHeight/8; + var rect = new Rectangle(x: 1, y + delta, drawingPosition.Width - 4, textArea.TextView.FontHeight - delta*2); + + if (isEnabled) + using (Brush brush = new LinearGradientBrush( + new Point(rect.Left, rect.Top), + new Point(rect.Right, rect.Bottom), + Color.SkyBlue, + Color.White)) + { + FillRoundRect(g, brush, rect); + } + else + FillRoundRect(g, Brushes.White, rect); + + using (Brush brush = new LinearGradientBrush( + new Point(rect.Left, rect.Top), + new Point(rect.Right, rect.Bottom), + Color.SkyBlue, + Color.Blue)) + { + using (var pen = new Pen(brush)) + { + DrawRoundRect(g, pen, rect); + } + } + } + + public void DrawArrow(Graphics g, int y) + { + var delta = textArea.TextView.FontHeight/8; + var rect = new Rectangle(x: 1, y + delta, drawingPosition.Width - 4, textArea.TextView.FontHeight - delta*2); + using (Brush brush = new LinearGradientBrush( + new Point(rect.Left, rect.Top), + new Point(rect.Right, rect.Bottom), + Color.LightYellow, + Color.Yellow)) + { + FillArrow(g, brush, rect); + } + + using (Brush brush = new LinearGradientBrush( + new Point(rect.Left, rect.Top), + new Point(rect.Right, rect.Bottom), + Color.Yellow, + Color.Brown)) + { + using (var pen = new Pen(brush)) + { + DrawArrow(g, pen, rect); + } + } + } + + private static GraphicsPath CreateArrowGraphicsPath(Rectangle r) + { + var gp = new GraphicsPath(); + var halfX = r.Width/2; + var halfY = r.Height/2; + gp.AddLine(r.X, r.Y + halfY/2, r.X + halfX, r.Y + halfY/2); + gp.AddLine(r.X + halfX, r.Y + halfY/2, r.X + halfX, r.Y); + gp.AddLine(r.X + halfX, r.Y, r.Right, r.Y + halfY); + gp.AddLine(r.Right, r.Y + halfY, r.X + halfX, r.Bottom); + gp.AddLine(r.X + halfX, r.Bottom, r.X + halfX, r.Bottom - halfY/2); + gp.AddLine(r.X + halfX, r.Bottom - halfY/2, r.X, r.Bottom - halfY/2); + gp.AddLine(r.X, r.Bottom - halfY/2, r.X, r.Y + halfY/2); + gp.CloseFigure(); + return gp; + } + + private static GraphicsPath CreateRoundRectGraphicsPath(Rectangle r) + { + var gp = new GraphicsPath(); + var radius = r.Width/2; + gp.AddLine(r.X + radius, r.Y, r.Right - radius, r.Y); + gp.AddArc(r.Right - radius, r.Y, radius, radius, startAngle: 270, sweepAngle: 90); + + gp.AddLine(r.Right, r.Y + radius, r.Right, r.Bottom - radius); + gp.AddArc(r.Right - radius, r.Bottom - radius, radius, radius, startAngle: 0, sweepAngle: 90); + + gp.AddLine(r.Right - radius, r.Bottom, r.X + radius, r.Bottom); + gp.AddArc(r.X, r.Bottom - radius, radius, radius, startAngle: 90, sweepAngle: 90); + + gp.AddLine(r.X, r.Bottom - radius, r.X, r.Y + radius); + gp.AddArc(r.X, r.Y, radius, radius, startAngle: 180, sweepAngle: 90); + + gp.CloseFigure(); + return gp; + } + + private static void DrawRoundRect(Graphics g, Pen p, Rectangle r) + { + using (var gp = CreateRoundRectGraphicsPath(r)) + { + g.DrawPath(p, gp); + } + } + + private static void FillRoundRect(Graphics g, Brush b, Rectangle r) + { + using (var gp = CreateRoundRectGraphicsPath(r)) + { + g.FillPath(b, gp); + } + } + + private static void DrawArrow(Graphics g, Pen p, Rectangle r) + { + using (var gp = CreateArrowGraphicsPath(r)) + { + g.DrawPath(p, gp); + } + } + + private static void FillArrow(Graphics g, Brush b, Rectangle r) + { + using (var gp = CreateArrowGraphicsPath(r)) + { + g.FillPath(b, gp); + } + } + + #endregion + } +} diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/Ime.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/Ime.cs new file mode 100644 index 0000000..ccb8010 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/Ime.cs @@ -0,0 +1,199 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +namespace ICSharpCode.TextEditor +{ + /// + /// Used internally, not for own use. + /// + internal class Ime + { + private const int WM_IME_CONTROL = 0x0283; + + private const int IMC_SETCOMPOSITIONWINDOW = 0x000c; + private const int CFS_POINT = 0x0002; + private const int IMC_SETCOMPOSITIONFONT = 0x000a; + private static bool disableIME; + + private Font font; + private IntPtr hIMEWnd; + private IntPtr hWnd; + private LOGFONT lf; + + public Ime(IntPtr hWnd, Font font) + { + // For unknown reasons, the IME support is causing crashes when used in a WOW64 process + // or when used in .NET 4.0. We'll disable IME support in those cases. + //var PROCESSOR_ARCHITEW6432 = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITEW6432"); + //if (PROCESSOR_ARCHITEW6432 == "IA64" || PROCESSOR_ARCHITEW6432 == "AMD64" || Environment.OSVersion.Platform == PlatformID.Unix || Environment.Version >= new Version(major: 4, minor: 0)) + // disableIME = true; + //else + // hIMEWnd = ImmGetDefaultIMEWnd(hWnd); + + // The above code doesn't work in .NET Core 3.1 and neither does IME so we'll just disable it. + disableIME = true; + + this.hWnd = hWnd; + this.font = font; + SetIMEWindowFont(font); + } + + public Font Font + { + get => font; + set + { + if (!value.Equals(font)) + { + font = value; + lf = null; + SetIMEWindowFont(value); + } + } + } + + public IntPtr HWnd + { + set + { + if (hWnd != value) + { + hWnd = value; + if (!disableIME) + hIMEWnd = ImmGetDefaultIMEWnd(value); + SetIMEWindowFont(font); + } + } + } + + [DllImport("imm32.dll")] + private static extern IntPtr ImmGetDefaultIMEWnd(IntPtr hWnd); + + [DllImport("user32.dll")] + private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, COMPOSITIONFORM lParam); + + [DllImport("user32.dll")] + private static extern IntPtr SendMessage( + IntPtr hWnd, int msg, IntPtr wParam, [In] [MarshalAs(UnmanagedType.LPStruct)] + LOGFONT lParam); + + private void SetIMEWindowFont(Font f) + { + if (disableIME || hIMEWnd == IntPtr.Zero) return; + + if (lf == null) + { + lf = new LOGFONT(); + f.ToLogFont(lf); + lf.lfFaceName = f.Name; // This is very important! "Font.ToLogFont" Method sets invalid value to LOGFONT.lfFaceName + } + + try + { + SendMessage( + hIMEWnd, + WM_IME_CONTROL, + new IntPtr(IMC_SETCOMPOSITIONFONT), + lf + ); + } + catch (AccessViolationException ex) + { + Handle(ex); + } + } + + public void SetIMEWindowLocation(int x, int y) + { + if (disableIME || hIMEWnd == IntPtr.Zero) return; + + var p = new POINT(); + p.x = x; + p.y = y; + + var lParam = new COMPOSITIONFORM(); + lParam.dwStyle = CFS_POINT; + lParam.ptCurrentPos = p; + lParam.rcArea = new RECT(); + + try + { + SendMessage( + hIMEWnd, + WM_IME_CONTROL, + new IntPtr(IMC_SETCOMPOSITIONWINDOW), + lParam + ); + } + catch (AccessViolationException ex) + { + Handle(ex); + } + } + + private static void Handle(Exception ex) + { + Console.WriteLine(ex); + if (!disableIME) + { + disableIME = true; + MessageBox.Show("Error calling IME: " + ex.Message + "\nIME is disabled.", "IME error"); + } + } + + [StructLayout(LayoutKind.Sequential)] + private class COMPOSITIONFORM + { + public int dwStyle; + public POINT ptCurrentPos; + public RECT rcArea; + } + + [StructLayout(LayoutKind.Sequential)] + private class POINT + { + public int x; + public int y; + } + + [StructLayout(LayoutKind.Sequential)] + private class RECT + { + public int bottom = 0; + public int left = 0; + public int right = 0; + public int top = 0; + } + + [StructLayout(LayoutKind.Sequential)] + private class LOGFONT + { + public byte lfCharSet = 0; + public byte lfClipPrecision = 0; + public int lfEscapement = 0; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + public string lfFaceName; + + public int lfHeight = 0; + public byte lfItalic = 0; + public int lfOrientation = 0; + public byte lfOutPrecision = 0; + public byte lfPitchAndFamily = 0; + public byte lfQuality = 0; + public byte lfStrikeOut = 0; + public byte lfUnderline = 0; + public int lfWeight = 0; + public int lfWidth = 0; + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/InsightWindow/IInsightDataProvider.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/InsightWindow/IInsightDataProvider.cs new file mode 100644 index 0000000..072aa1c --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/InsightWindow/IInsightDataProvider.cs @@ -0,0 +1,53 @@ +// +// +// +// +// $Revision$ +// + +namespace ICSharpCode.TextEditor.Gui.InsightWindow +{ + public interface IInsightDataProvider + { + /// + /// Gets the number of available insight entries, e.g. the number of available + /// overloads to call. + /// + int InsightDataCount { get; } + + /// + /// Gets the index of the entry to initially select. + /// + int DefaultIndex { get; } + + /// + /// Tells the insight provider to prepare its data. + /// + /// The name of the edited file + /// The text area in which the file is being edited + void SetupDataProvider(string fileName, TextArea textArea); + + /// + /// Notifies the insight provider that the caret offset has changed. + /// + /// + /// Return true to close the insight window (e.g. when the + /// caret was moved outside the region where insight is displayed for). + /// Return false to keep the window open. + /// + bool CaretOffsetChanged(); + + /// + /// Gets the text to display in the insight window. + /// + /// + /// The number of the active insight entry. + /// Multiple insight entries might be multiple overloads of the same method. + /// + /// + /// The text to display, e.g. a multi-line string where + /// the first line is the method definition, followed by a description. + /// + string GetInsightData(int number); + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/InsightWindow/InsightWindow.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/InsightWindow/InsightWindow.cs new file mode 100644 index 0000000..8f7fd06 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/InsightWindow/InsightWindow.cs @@ -0,0 +1,209 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Windows.Forms; +using ICSharpCode.TextEditor.Gui.CompletionWindow; +using ICSharpCode.TextEditor.Util; + +namespace ICSharpCode.TextEditor.Gui.InsightWindow +{ + public class InsightWindow : AbstractCompletionWindow + { + private readonly MouseWheelHandler mouseWheelHandler = new MouseWheelHandler(); + + public InsightWindow(Form parentForm, TextEditorControl control) : base(parentForm, control) + { + SetStyle(ControlStyles.UserPaint, value: true); + SetStyle(ControlStyles.OptimizedDoubleBuffer, value: true); + } + + public void ShowInsightWindow() + { + if (!Visible) + { + if (insightDataProviderStack.Count > 0) + ShowCompletionWindow(); + } + else + { + Refresh(); + } + } + + public void HandleMouseWheel(MouseEventArgs e) + { + if (DataProvider != null && DataProvider.InsightDataCount > 0) + { + var distance = mouseWheelHandler.GetScrollAmount(e); + if (control.TextEditorProperties.MouseWheelScrollDown) + distance = -distance; + if (distance > 0) + CurrentData = (CurrentData + 1)%DataProvider.InsightDataCount; + else if (distance < 0) + CurrentData = (CurrentData + DataProvider.InsightDataCount - 1)%DataProvider.InsightDataCount; + Refresh(); + } + } + + #region Event handling routines + + protected override bool ProcessTextAreaKey(Keys keyData) + { + if (!Visible) + return false; + switch (keyData) + { + case Keys.Down: + if (DataProvider != null && DataProvider.InsightDataCount > 0) + { + CurrentData = (CurrentData + 1)%DataProvider.InsightDataCount; + Refresh(); + } + + return true; + case Keys.Up: + if (DataProvider != null && DataProvider.InsightDataCount > 0) + { + CurrentData = (CurrentData + DataProvider.InsightDataCount - 1)%DataProvider.InsightDataCount; + Refresh(); + } + + return true; + } + + return base.ProcessTextAreaKey(keyData); + } + + protected override void CaretOffsetChanged(object sender, EventArgs e) + { + // move the window under the caret (don't change the x position) + var caretPos = control.ActiveTextAreaControl.Caret.Position; +// var y = (1 + caretPos.Y)*control.ActiveTextAreaControl.TextArea.TextView.FontHeight +// - control.ActiveTextAreaControl.TextArea.VirtualTop.Y - 1 +// + control.ActiveTextAreaControl.TextArea.TextView.DrawingPosition.Y; + + var xpos = control.ActiveTextAreaControl.TextArea.TextView.GetDrawingXPos(caretPos.Y, caretPos.X); + var ypos = (control.ActiveTextAreaControl.Document.GetVisibleLine(caretPos.Y) + 1)*control.ActiveTextAreaControl.TextArea.TextView.FontHeight + - control.ActiveTextAreaControl.TextArea.VirtualTop.Y; + var rulerHeight = control.TextEditorProperties.ShowHorizontalRuler ? control.ActiveTextAreaControl.TextArea.TextView.FontHeight : 0; + + var p = control.ActiveTextAreaControl.PointToScreen(new Point(xpos, ypos + rulerHeight)); + if (p.Y != Location.Y) + Location = p; + + while (DataProvider != null && DataProvider.CaretOffsetChanged()) + CloseCurrentDataProvider(); + } + + protected override void OnMouseDown(MouseEventArgs e) + { + base.OnMouseDown(e); + control.ActiveTextAreaControl.TextArea.Focus(); + if (TipPainterTools.DrawingRectangle1.Contains(e.X, e.Y)) + { + CurrentData = (CurrentData + DataProvider.InsightDataCount - 1)%DataProvider.InsightDataCount; + Refresh(); + } + + if (TipPainterTools.DrawingRectangle2.Contains(e.X, e.Y)) + { + CurrentData = (CurrentData + 1)%DataProvider.InsightDataCount; + Refresh(); + } + } + + #endregion + + #region Insight Window Drawing routines + + protected override void OnPaint(PaintEventArgs pe) + { + string methodCountMessage = null, description; + if (DataProvider == null || DataProvider.InsightDataCount < 1) + { + description = "Unknown Method"; + } + else + { + if (DataProvider.InsightDataCount > 1) + methodCountMessage = control.GetRangeDescription(CurrentData + 1, DataProvider.InsightDataCount); + description = DataProvider.GetInsightData(CurrentData); + } + + drawingSize = TipPainterTools.GetDrawingSizeHelpTipFromCombinedDescription( + this, + pe.Graphics, + Font, + methodCountMessage, + description); + if (drawingSize != Size) + SetLocation(); + else + TipPainterTools.DrawHelpTipFromCombinedDescription(this, pe.Graphics, Font, methodCountMessage, description); + } + + protected override void OnPaintBackground(PaintEventArgs pe) + { + pe.Graphics.FillRectangle(SystemBrushes.Info, pe.ClipRectangle); + } + + #endregion + + #region InsightDataProvider handling + + private readonly Stack insightDataProviderStack = new Stack(); + + private int CurrentData + { + get => insightDataProviderStack.Peek().currentData; + set => insightDataProviderStack.Peek().currentData = value; + } + + private IInsightDataProvider DataProvider + { + get + { + if (insightDataProviderStack.Count == 0) + return null; + return insightDataProviderStack.Peek().dataProvider; + } + } + + public void AddInsightDataProvider(IInsightDataProvider provider, string fileName) + { + provider.SetupDataProvider(fileName, control.ActiveTextAreaControl.TextArea); + if (provider.InsightDataCount > 0) + insightDataProviderStack.Push(new InsightDataProviderStackElement(provider)); + } + + private void CloseCurrentDataProvider() + { + insightDataProviderStack.Pop(); + if (insightDataProviderStack.Count == 0) + Close(); + else + Refresh(); + } + + private class InsightDataProviderStackElement + { + public readonly IInsightDataProvider dataProvider; + public int currentData; + + public InsightDataProviderStackElement(IInsightDataProvider dataProvider) + { + currentData = Math.Max(dataProvider.DefaultIndex, val2: 0); + this.dataProvider = dataProvider; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/TextArea.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/TextArea.cs new file mode 100644 index 0000000..56e2c24 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/TextArea.cs @@ -0,0 +1,910 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using ICSharpCode.TextEditor.Document; +using ICSharpCode.TextEditor.Gui.CompletionWindow; + +namespace ICSharpCode.TextEditor +{ + public delegate bool KeyEventHandler(char ch); + + public delegate bool DialogKeyProcessor(Keys keyData); + + /// + /// This class paints the textarea. + /// + [ToolboxItem(defaultType: false)] + public class TextArea : Control + { + // static because the mouse can only be in one text area and we don't want to have + // tooltips of text areas from inactive tabs floating around. + private static DeclarationViewWindow toolTip; + private static string oldToolTip; + + private readonly List bracketshemes = new List(); + + private readonly List leftMargins = new List(); + //public Point selectionStartPos = new Point(0,0); + + private bool disposed; + private bool hiddenMouseCursor; + + private AbstractMargin lastMouseInMargin; + + /// + /// The position where the mouse cursor was when it was hidden. Sometimes the text editor gets MouseMove + /// events when typing text even if the mouse is not moved. + /// + private Point mouseCursorHidePosition; + + internal Point mousepos = new Point(x: 0, y: 0); + + private bool toolTipActive; + + /// + /// Rectangle in text area that caused the current tool tip. + /// Prevents tooltip from re-showing when it was closed because of a click or keyboard + /// input and the mouse was not used. + /// + private Rectangle toolTipRectangle; + + private AbstractMargin updateMargin; + + private Point virtualTop = new Point(x: 0, y: 0); + + public TextArea(TextEditorControl motherTextEditorControl, TextAreaControl motherTextAreaControl) + { + MotherTextAreaControl = motherTextAreaControl; + MotherTextEditorControl = motherTextEditorControl; + + Caret = new Caret(this); + SelectionManager = new SelectionManager(Document, this); + + ClipboardHandler = new TextAreaClipboardHandler(this); + + ResizeRedraw = true; + + SetStyle(ControlStyles.OptimizedDoubleBuffer, value: true); +// SetStyle(ControlStyles.AllPaintingInWmPaint, true); +// SetStyle(ControlStyles.UserPaint, true); + SetStyle(ControlStyles.Opaque, value: false); + SetStyle(ControlStyles.ResizeRedraw, value: true); + SetStyle(ControlStyles.Selectable, value: true); + + TextView = new TextView(this); + + GutterMargin = new GutterMargin(this); + FoldMargin = new FoldMargin(this); + IconBarMargin = new IconBarMargin(this); + leftMargins.AddRange(new AbstractMargin[] {IconBarMargin, GutterMargin, FoldMargin}); + OptionsChanged(); + + new TextAreaMouseHandler(this).Attach(); + new TextAreaDragDropHandler().Attach(this); + + bracketshemes.Add(new BracketHighlightingSheme(opentag: '{', closingtag: '}')); + bracketshemes.Add(new BracketHighlightingSheme(opentag: '(', closingtag: ')')); + bracketshemes.Add(new BracketHighlightingSheme(opentag: '[', closingtag: ']')); + + Caret.PositionChanged += SearchMatchingBracket; + Document.TextContentChanged += TextContentChanged; + Document.FoldingManager.FoldingsChanged += DocumentFoldingsChanged; + } + + [Browsable(browsable: false)] + public IList LeftMargins => leftMargins.AsReadOnly(); + + public TextEditorControl MotherTextEditorControl { get; private set; } + + public TextAreaControl MotherTextAreaControl { get; private set; } + + public SelectionManager SelectionManager { get; } + + public Caret Caret { get; } + + public TextView TextView { get; } + + public GutterMargin GutterMargin { get; } + + public FoldMargin FoldMargin { get; } + + public IconBarMargin IconBarMargin { get; } + + public Encoding Encoding => MotherTextEditorControl.Encoding; + + public int MaxVScrollValue => (Document.GetVisibleLine(Document.TotalNumberOfLines - 1) + 1 + TextView.VisibleLineCount*2/3)*TextView.FontHeight; + + public Point VirtualTop + { + get => virtualTop; + set + { + var newVirtualTop = new Point(value.X, Math.Min(MaxVScrollValue, Math.Max(val1: 0, value.Y))); + var scrollBar = MotherTextAreaControl.VScrollBar; + if (virtualTop != newVirtualTop && newVirtualTop.Y >= scrollBar.Minimum && newVirtualTop.Y <= scrollBar.Maximum ) + { + virtualTop = newVirtualTop; + scrollBar.Value = virtualTop.Y; + Invalidate(); + } + + Caret.UpdateCaretPosition(); + } + } + + public bool AutoClearSelection { get; set; } + + [Browsable(browsable: false)] + public IDocument Document => MotherTextEditorControl.Document; + + public TextAreaClipboardHandler ClipboardHandler { get; } + + public ITextEditorProperties TextEditorProperties => MotherTextEditorControl.TextEditorProperties; + + public bool EnableCutOrPaste + { + get + { + if (MotherTextAreaControl == null) + return false; + if (SelectionManager.HasSomethingSelected) + return !SelectionManager.SelectionIsReadonly; + return !IsReadOnly(Caret.Offset); + } + } + + public void InsertLeftMargin(int index, AbstractMargin margin) + { + leftMargins.Insert(index, margin); + Refresh(); + } + + public void UpdateMatchingBracket() + { + SearchMatchingBracket(sender: null, e: null); + } + + private void TextContentChanged(object sender, EventArgs e) + { + Caret.Position = new TextLocation(column: 0, line: 0); + SelectionManager.SelectionCollection.Clear(); + } + + private void SearchMatchingBracket(object sender, EventArgs e) + { + if (!TextEditorProperties.ShowMatchingBracket) + { + TextView.Highlight = null; + return; + } + + int oldLine1 = -1, oldLine2 = -1; + if (TextView.Highlight != null && TextView.Highlight.OpenBrace.Y >= 0 && TextView.Highlight.OpenBrace.Y < Document.TotalNumberOfLines) + oldLine1 = TextView.Highlight.OpenBrace.Y; + if (TextView.Highlight != null && TextView.Highlight.CloseBrace.Y >= 0 && TextView.Highlight.CloseBrace.Y < Document.TotalNumberOfLines) + oldLine2 = TextView.Highlight.CloseBrace.Y; + TextView.Highlight = FindMatchingBracketHighlight(); + if (oldLine1 >= 0) + UpdateLine(oldLine1); + if (oldLine2 >= 0 && oldLine2 != oldLine1) + UpdateLine(oldLine2); + if (TextView.Highlight != null) + { + var newLine1 = TextView.Highlight.OpenBrace.Y; + var newLine2 = TextView.Highlight.CloseBrace.Y; + if (newLine1 != oldLine1 && newLine1 != oldLine2) + UpdateLine(newLine1); + if (newLine2 != oldLine1 && newLine2 != oldLine2 && newLine2 != newLine1) + UpdateLine(newLine2); + } + } + + public Highlight FindMatchingBracketHighlight() + { + if (Caret.Offset == 0) + return null; + foreach (var bracketsheme in bracketshemes) + { + var highlight = bracketsheme.GetHighlight(Document, Caret.Offset - 1); + if (highlight != null) + return highlight; + } + + return null; + } + + public void SetDesiredColumn() + { + Caret.DesiredColumn = TextView.GetDrawingXPos(Caret.Line, Caret.Column) + VirtualTop.X; + } + + public void SetCaretToDesiredColumn() + { + FoldMarker dummy; + Caret.Position = TextView.GetLogicalColumn(Caret.Line, Caret.DesiredColumn + VirtualTop.X, out dummy); + } + + public void OptionsChanged() + { + UpdateMatchingBracket(); + TextView.OptionsChanged(); + Caret.RecreateCaret(); + Caret.UpdateCaretPosition(); + Refresh(); + } + + protected override void OnMouseLeave(EventArgs e) + { + base.OnMouseLeave(e); + Cursor = Cursors.Default; + if (lastMouseInMargin != null) + { + lastMouseInMargin.HandleMouseLeave(EventArgs.Empty); + lastMouseInMargin = null; + } + + CloseToolTip(); + } + + protected override void OnMouseDown(MouseEventArgs e) + { + // this corrects weird problems when text is selected, + // then a menu item is selected, then the text is + // clicked again - it correctly synchronises the + // click position + mousepos = new Point(e.X, e.Y); + + base.OnMouseDown(e); + CloseToolTip(); + + foreach (var margin in leftMargins) + if (margin.DrawingPosition.Contains(e.X, e.Y)) + margin.HandleMouseDown(new Point(e.X, e.Y), e.Button); + } + + /// + /// Shows the mouse cursor if it has been hidden. + /// + /// + /// true to always show the cursor or false to show it only if it has been moved + /// since it was hidden. + /// + internal void ShowHiddenCursor(bool forceShow) + { + if (hiddenMouseCursor) + if (mouseCursorHidePosition != Cursor.Position || forceShow) + { + Cursor.Show(); + hiddenMouseCursor = false; + } + } + + private void SetToolTip(string text, int lineNumber) + { + if (toolTip == null || toolTip.IsDisposed) + toolTip = new DeclarationViewWindow(FindForm()); + if (oldToolTip == text) + return; + if (text == null) + { + toolTip.Hide(); + } + else + { + var p = MousePosition; + var cp = PointToClient(p); + if (lineNumber >= 0) + { + lineNumber = Document.GetVisibleLine(lineNumber); + p.Y = p.Y - cp.Y + lineNumber*TextView.FontHeight - virtualTop.Y; + } + + p.Offset(dx: 3, dy: 3); + toolTip.Owner = FindForm(); + toolTip.Location = p; + toolTip.Description = text; + toolTip.HideOnClick = true; + toolTip.Show(); + } + + oldToolTip = text; + } + + public event ToolTipRequestEventHandler ToolTipRequest; + + protected virtual void OnToolTipRequest(ToolTipRequestEventArgs e) + { + ToolTipRequest?.Invoke(this, e); + } + + private void CloseToolTip() + { + if (toolTipActive) + { + //Console.WriteLine("Closing tooltip"); + toolTipActive = false; + SetToolTip(text: null, lineNumber: -1); + } + + ResetMouseEventArgs(); + } + + protected override void OnMouseHover(EventArgs e) + { + base.OnMouseHover(e); + //Console.WriteLine("Hover raised at " + PointToClient(Control.MousePosition)); + if (MouseButtons == MouseButtons.None) + RequestToolTip(PointToClient(MousePosition)); + else + CloseToolTip(); + } + + protected void RequestToolTip(Point mousePos) + { + if (toolTipRectangle.Contains(mousePos)) + { + if (!toolTipActive) + ResetMouseEventArgs(); + return; + } + + //Console.WriteLine("Request tooltip for " + mousePos); + + toolTipRectangle = new Rectangle(mousePos.X - 4, mousePos.Y - 4, width: 8, height: 8); + + var logicPos = TextView.GetLogicalPosition( + mousePos.X - TextView.DrawingPosition.Left, + mousePos.Y - TextView.DrawingPosition.Top); + var inDocument = TextView.DrawingPosition.Contains(mousePos) + && logicPos.Y >= 0 && logicPos.Y < Document.TotalNumberOfLines; + var args = new ToolTipRequestEventArgs(mousePos, logicPos, inDocument); + OnToolTipRequest(args); + if (args.ToolTipShown) + { + //Console.WriteLine("Set tooltip to " + args.toolTipText); + toolTipActive = true; + SetToolTip(args.toolTipText, inDocument ? logicPos.Y + 1 : -1); + } + else + { + CloseToolTip(); + } + } + + // external interface to the attached event + internal void RaiseMouseMove(MouseEventArgs e) + { + OnMouseMove(e); + } + + protected override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + if (!toolTipRectangle.Contains(e.Location)) + { + toolTipRectangle = Rectangle.Empty; + if (toolTipActive) + RequestToolTip(e.Location); + } + + foreach (var margin in leftMargins) + if (margin.DrawingPosition.Contains(e.X, e.Y)) + { + Cursor = margin.Cursor; + margin.HandleMouseMove(new Point(e.X, e.Y), e.Button); + if (lastMouseInMargin != margin) + { + lastMouseInMargin?.HandleMouseLeave(EventArgs.Empty); + lastMouseInMargin = margin; + } + + return; + } + + if (lastMouseInMargin != null) + { + lastMouseInMargin.HandleMouseLeave(EventArgs.Empty); + lastMouseInMargin = null; + } + + if (TextView.DrawingPosition.Contains(e.X, e.Y)) + { + var realmousepos = TextView.GetLogicalPosition(e.X - TextView.DrawingPosition.X, e.Y - TextView.DrawingPosition.Y); + if (SelectionManager.IsSelected(Document.PositionToOffset(realmousepos)) && MouseButtons == MouseButtons.None) + Cursor = Cursors.Default; + else + Cursor = TextView.Cursor; + return; + } + + Cursor = Cursors.Default; + } + + public void Refresh(AbstractMargin margin) + { + updateMargin = margin; + Invalidate(updateMargin.DrawingPosition); + Update(); + updateMargin = null; + } + + protected override void OnPaintBackground(PaintEventArgs pevent) + { + } + + protected override void OnPaint(PaintEventArgs e) + { + var currentXPos = 0; + var currentYPos = 0; + var adjustScrollBars = false; + var g = e.Graphics; + var clipRectangle = e.ClipRectangle; + + var isFullRepaint = clipRectangle.X == 0 && clipRectangle.Y == 0 + && clipRectangle.Width == Width && clipRectangle.Height == Height; + + g.TextRenderingHint = TextEditorProperties.TextRenderingHint; + + updateMargin?.Paint(g, updateMargin.DrawingPosition); + + if (clipRectangle.Width <= 0 || clipRectangle.Height <= 0) + return; + + foreach (var margin in leftMargins) + if (margin.IsVisible) + { + var marginRectangle = new Rectangle(currentXPos, currentYPos, margin.Width, Height - currentYPos); + if (marginRectangle != margin.DrawingPosition) + { + // margin changed size + if (!isFullRepaint && !clipRectangle.Contains(marginRectangle)) + Invalidate(); // do a full repaint + adjustScrollBars = true; + margin.DrawingPosition = marginRectangle; + } + + currentXPos += margin.DrawingPosition.Width; + if (clipRectangle.IntersectsWith(marginRectangle)) + { + marginRectangle.Intersect(clipRectangle); + if (!marginRectangle.IsEmpty) + margin.Paint(g, marginRectangle); + } + } + + var textViewArea = new Rectangle(currentXPos, currentYPos, Width - currentXPos, Height - currentYPos); + if (textViewArea != TextView.DrawingPosition) + { + adjustScrollBars = true; + TextView.DrawingPosition = textViewArea; + // update caret position (but outside of WM_PAINT!) + BeginInvoke((MethodInvoker)Caret.UpdateCaretPosition); + } + + if (clipRectangle.IntersectsWith(textViewArea)) + { + textViewArea.Intersect(clipRectangle); + if (!textViewArea.IsEmpty) + TextView.Paint(g, textViewArea); + } + + if (adjustScrollBars) + MotherTextAreaControl.UpdateLayout(); + + // we cannot update the caret position here, it's not allowed to call the caret API inside WM_PAINT + //Caret.UpdateCaretPosition(); + + base.OnPaint(e); + } + + private void DocumentFoldingsChanged(object sender, EventArgs e) + { + Caret.UpdateCaretPosition(); + Invalidate(); + MotherTextAreaControl.UpdateLayout(); + } + + public void ScrollToCaret() + { + MotherTextAreaControl.ScrollToCaret(); + } + + public void ScrollTo(int line) + { + MotherTextAreaControl.ScrollTo(line); + } + + public void BeginUpdate() + { + MotherTextEditorControl.BeginUpdate(); + } + + public void EndUpdate() + { + MotherTextEditorControl.EndUpdate(); + } + + private static string GenerateWhitespaceString(int length) + { + return new string(c: ' ', length); + } + + /// + /// Inserts a single character at the caret position + /// + public void InsertChar(char ch) + { + var updating = MotherTextEditorControl.IsInUpdate; + if (!updating) + BeginUpdate(); + + // filter out forgein whitespace chars and replace them with standard space (ASCII 32) + if (char.IsWhiteSpace(ch) && ch != '\t' && ch != '\n') + ch = ' '; + + Document.UndoStack.StartUndoGroup(); + if (Document.TextEditorProperties.DocumentSelectionMode == DocumentSelectionMode.Normal && + SelectionManager.SelectionCollection.Count > 0) + { + Caret.Position = SelectionManager.SelectionCollection[index: 0].StartPosition; + SelectionManager.RemoveSelectedText(); + } + + var caretLine = Document.GetLineSegment(Caret.Line); + var offset = Caret.Offset; + // use desired column for generated whitespaces + var dc = Caret.Column; + if (caretLine.Length < dc && ch != '\n') + Document.Insert(offset, GenerateWhitespaceString(dc - caretLine.Length) + ch); + else + Document.Insert(offset, ch.ToString()); + Document.UndoStack.EndUndoGroup(); + ++Caret.Column; + + if (!updating) + { + EndUpdate(); + UpdateLineToEnd(Caret.Line, Caret.Column); + } + + // I prefer to set NOT the standard column, if you type something +// ++Caret.DesiredColumn; + } + + /// + /// Inserts a whole string at the caret position + /// + public void InsertString(string str) + { + var updating = MotherTextEditorControl.IsInUpdate; + if (!updating) + BeginUpdate(); + try + { + Document.UndoStack.StartUndoGroup(); + if (Document.TextEditorProperties.DocumentSelectionMode == DocumentSelectionMode.Normal && + SelectionManager.SelectionCollection.Count > 0) + { + Caret.Position = SelectionManager.SelectionCollection[index: 0].StartPosition; + SelectionManager.RemoveSelectedText(); + } + + var oldOffset = Document.PositionToOffset(Caret.Position); + var oldLine = Caret.Line; + var caretLine = Document.GetLineSegment(Caret.Line); + if (caretLine.Length < Caret.Column) + { + var whiteSpaceLength = Caret.Column - caretLine.Length; + Document.Insert(oldOffset, GenerateWhitespaceString(whiteSpaceLength) + str); + Caret.Position = Document.OffsetToPosition(oldOffset + str.Length + whiteSpaceLength); + } + else + { + Document.Insert(oldOffset, str); + Caret.Position = Document.OffsetToPosition(oldOffset + str.Length); + } + + Document.UndoStack.EndUndoGroup(); + if (oldLine != Caret.Line) + UpdateToEnd(oldLine); + else + UpdateLineToEnd(Caret.Line, Caret.Column); + } + finally + { + if (!updating) + EndUpdate(); + } + } + + /// + /// Replaces a char at the caret position + /// + public void ReplaceChar(char ch) + { + var updating = MotherTextEditorControl.IsInUpdate; + if (!updating) + BeginUpdate(); + if (Document.TextEditorProperties.DocumentSelectionMode == DocumentSelectionMode.Normal && SelectionManager.SelectionCollection.Count > 0) + { + Caret.Position = SelectionManager.SelectionCollection[index: 0].StartPosition; + SelectionManager.RemoveSelectedText(); + } + + var lineNr = Caret.Line; + var line = Document.GetLineSegment(lineNr); + var offset = Document.PositionToOffset(Caret.Position); + if (offset < line.Offset + line.Length) + Document.Replace(offset, length: 1, ch.ToString()); + else + Document.Insert(offset, ch.ToString()); + if (!updating) + { + EndUpdate(); + UpdateLineToEnd(lineNr, Caret.Column); + } + + ++Caret.Column; +// ++Caret.DesiredColumn; + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (disposing) + if (!disposed) + { + disposed = true; + if (Caret != null) + { + Caret.PositionChanged -= SearchMatchingBracket; + Caret.Dispose(); + } + + SelectionManager?.Dispose(); + Document.TextContentChanged -= TextContentChanged; + Document.FoldingManager.FoldingsChanged -= DocumentFoldingsChanged; + MotherTextAreaControl = null; + MotherTextEditorControl = null; + foreach (var margin in leftMargins) + if (margin is IDisposable disposable) + disposable.Dispose(); + TextView.Dispose(); + } + } + + public event KeyEventHandler KeyEventHandler; + public event DialogKeyProcessor DoProcessDialogKey; + + #region keyboard handling methods + + /// + /// This method is called on each Keypress + /// + /// + /// True, if the key is handled by this method and should NOT be + /// inserted in the textarea. + /// + protected internal virtual bool HandleKeyPress(char ch) + { + KeyEventHandler handler = KeyEventHandler; + if (handler != null) + return handler(ch); + return false; + } + + // Fixes SD2-747: Form containing the text editor and a button with a shortcut + protected override bool IsInputChar(char charCode) + { + return true; + } + + internal bool IsReadOnly(int offset) + { + if (Document.ReadOnly) + return true; + if (TextEditorProperties.SupportReadOnlySegments) + return Document.MarkerStrategy.GetMarkers(offset).Exists(m => m.IsReadOnly); + + return false; + } + + internal bool IsReadOnly(int offset, int length) + { + if (Document.ReadOnly) + return true; + if (TextEditorProperties.SupportReadOnlySegments) + return Document.MarkerStrategy.GetMarkers(offset, length).Exists(m => m.IsReadOnly); + + return false; + } + + public void SimulateKeyPress(char ch) + { + if (SelectionManager.HasSomethingSelected) + { + if (SelectionManager.SelectionIsReadonly) + return; + } + else if (IsReadOnly(Caret.Offset)) + { + return; + } + + if (ch < ' ') + return; + + if (!hiddenMouseCursor && TextEditorProperties.HideMouseCursor) + if (ClientRectangle.Contains(PointToClient(Cursor.Position))) + { + mouseCursorHidePosition = Cursor.Position; + hiddenMouseCursor = true; + Cursor.Hide(); + } + + CloseToolTip(); + + BeginUpdate(); + Document.UndoStack.StartUndoGroup(); + try + { + // INSERT char + if (!HandleKeyPress(ch)) + switch (Caret.CaretMode) + { + case CaretMode.InsertMode: + InsertChar(ch); + break; + case CaretMode.OverwriteMode: + ReplaceChar(ch); + break; + default: + Debug.Assert(condition: false, "Unknown caret mode " + Caret.CaretMode); + break; + } + + var currentLineNr = Caret.Line; + Document.FormattingStrategy.FormatLine(this, currentLineNr, Document.PositionToOffset(Caret.Position), ch); + + EndUpdate(); + } + finally + { + Document.UndoStack.EndUndoGroup(); + } + } + + protected override void OnKeyPress(KeyPressEventArgs e) + { + base.OnKeyPress(e); + SimulateKeyPress(e.KeyChar); + e.Handled = true; + } + + /// + /// This method executes a dialog key + /// + public bool ExecuteDialogKey(Keys keyData) + { + // try, if a dialog key processor was set to use this + DialogKeyProcessor handler = DoProcessDialogKey; + if (handler != null && handler(keyData)) + return true; + + // if not (or the process was 'silent', use the standard edit actions + var action = MotherTextEditorControl?.GetEditAction(keyData); + AutoClearSelection = true; + if (action != null) + { + BeginUpdate(); + try + { + lock (Document) + { + action.Execute(this); + if (SelectionManager.HasSomethingSelected && AutoClearSelection /*&& caretchanged*/) + if (Document.TextEditorProperties.DocumentSelectionMode == DocumentSelectionMode.Normal) + SelectionManager.ClearSelection(); + } + } + finally + { + EndUpdate(); + Caret.UpdateCaretPosition(); + } + + return true; + } + + return false; + } + + protected override bool ProcessDialogKey(Keys keyData) + { + return ExecuteDialogKey(keyData) || base.ProcessDialogKey(keyData); + } + + #endregion + + #region UPDATE Commands + + internal void UpdateLine(int line) + { + UpdateLines(xPos: 0, line, line); + } + + internal void UpdateLines(int lineBegin, int lineEnd) + { + UpdateLines(xPos: 0, lineBegin, lineEnd); + } + + internal void UpdateToEnd(int lineBegin) + { +// if (lineBegin > FirstPhysicalLine + textView.VisibleLineCount) { +// return; +// } + + lineBegin = Document.GetVisibleLine(lineBegin); + var y = Math.Max(val1: 0, lineBegin*TextView.FontHeight); + y = Math.Max(val1: 0, y - virtualTop.Y); + var r = new Rectangle( + x: 0, + y, + Width, + Height - y); + Invalidate(r); + } + + internal void UpdateLineToEnd(int lineNr, int xStart) + { + UpdateLines(xStart, lineNr, lineNr); + } + + internal void UpdateLine(int line, int begin, int end) + { + UpdateLines(line, line); + } + + private int FirstPhysicalLine => VirtualTop.Y/TextView.FontHeight; + + internal void UpdateLines(int xPos, int lineBegin, int lineEnd) + { +// if (lineEnd < FirstPhysicalLine || lineBegin > FirstPhysicalLine + textView.VisibleLineCount) { +// return; +// } + + InvalidateLines(lineBegin, lineEnd); + } + + private void InvalidateLines(int lineBegin, int lineEnd) + { + lineBegin = Math.Max(Document.GetVisibleLine(lineBegin), FirstPhysicalLine); + lineEnd = Math.Min(Document.GetVisibleLine(lineEnd), FirstPhysicalLine + TextView.VisibleLineCount); + var y = Math.Max(val1: 0, lineBegin*TextView.FontHeight); + var height = Math.Min(TextView.DrawingPosition.Height, (1 + lineEnd - lineBegin)*(TextView.FontHeight + 1)); + + var r = new Rectangle( + x: 0, + y - 1 - virtualTop.Y, + Width, + height + 3); + + Invalidate(r); + } + + #endregion + + //internal void + } +} diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/TextAreaClipboardHandler.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/TextAreaClipboardHandler.cs new file mode 100644 index 0000000..8156630 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/TextAreaClipboardHandler.cs @@ -0,0 +1,273 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using ICSharpCode.TextEditor.Actions; +using ICSharpCode.TextEditor.Util; + +namespace ICSharpCode.TextEditor +{ + public class TextAreaClipboardHandler + { + public delegate bool ClipboardContainsTextDelegate(); + + private const string LineSelectedType = "MSDEVLineSelect"; // This is the type VS 2003 and 2005 use for flagging a whole line copy + + /// + /// Is called when CachedClipboardContainsText should be updated. + /// If this property is null (the default value), the text editor uses + /// System.Windows.Forms.Clipboard.ContainsText. + /// + /// + /// This property is useful if you want to prevent the default Clipboard.ContainsText + /// behaviour that waits for the clipboard to be available - the clipboard might + /// never become available if it is owned by a process that is paused by the debugger. + /// + public static ClipboardContainsTextDelegate GetClipboardContainsText; + + // Code duplication: TextAreaClipboardHandler.cs also has SafeSetClipboard + [ThreadStatic] private static int SafeSetClipboardDataVersion; + private readonly TextArea textArea; + + public TextAreaClipboardHandler(TextArea textArea) + { + this.textArea = textArea; + textArea.SelectionManager.SelectionChanged += DocumentSelectionChanged; + } + + public bool EnableCut => textArea.EnableCutOrPaste; + + public bool EnableCopy => true; + + public bool EnablePaste + { + get + { + if (!textArea.EnableCutOrPaste) + return false; + var d = GetClipboardContainsText; + if (d != null) + return d(); + + try + { + return Clipboard.ContainsText(); + } + catch (ExternalException) + { + return false; + } + } + } + + public bool EnableDelete => textArea.SelectionManager.HasSomethingSelected && !textArea.SelectionManager.SelectionIsReadonly; + + public bool EnableSelectAll => true; + + private static void DocumentSelectionChanged(object sender, EventArgs e) + { +// ((DefaultWorkbench)WorkbenchSingleton.Workbench).UpdateToolbars(); + } + + private bool CopyTextToClipboard(string stringToCopy, bool asLine) + { + if (stringToCopy.Length > 0) + { + var dataObject = new DataObject(); + dataObject.SetData(DataFormats.UnicodeText, autoConvert: true, stringToCopy); + if (asLine) + { + var lineSelected = new MemoryStream(capacity: 1); + lineSelected.WriteByte(value: 1); + dataObject.SetData(LineSelectedType, autoConvert: false, lineSelected); + } + + // Default has no highlighting, therefore we don't need RTF output + if (textArea.Document.HighlightingStrategy.Name != "Default") + dataObject.SetData(DataFormats.Rtf, RtfWriter.GenerateRtf(textArea)); + OnCopyText(new CopyTextEventArgs(stringToCopy)); + + SafeSetClipboard(dataObject); + return true; + } + + return false; + } + + private static void SafeSetClipboard(object dataObject) + { + // Work around ExternalException bug. (SD2-426) + // Best reproducable inside Virtual PC. + var version = unchecked(++SafeSetClipboardDataVersion); + try + { + Clipboard.SetDataObject(dataObject, copy: true); + } + catch (ExternalException) + { + var timer = new Timer(); + timer.Interval = 100; + timer.Tick += delegate + { + timer.Stop(); + timer.Dispose(); + if (SafeSetClipboardDataVersion == version) + try + { + Clipboard.SetDataObject(dataObject, copy: true, retryTimes: 10, retryDelay: 50); + } + catch (ExternalException) + { + } + }; + timer.Start(); + } + } + + private bool CopyTextToClipboard(string stringToCopy) + { + return CopyTextToClipboard(stringToCopy, asLine: false); + } + + public void Cut(object sender, EventArgs e) + { + if (textArea.SelectionManager.HasSomethingSelected) + { + if (CopyTextToClipboard(textArea.SelectionManager.SelectedText)) + { + if (textArea.SelectionManager.SelectionIsReadonly) + return; + // Remove text + textArea.BeginUpdate(); + textArea.Caret.Position = textArea.SelectionManager.SelectionCollection[index: 0].StartPosition; + textArea.SelectionManager.RemoveSelectedText(); + textArea.EndUpdate(); + } + } + else if (textArea.Document.TextEditorProperties.CutCopyWholeLine) + { + // No text was selected, select and cut the entire line + var curLineNr = textArea.Document.GetLineNumberForOffset(textArea.Caret.Offset); + var lineWhereCaretIs = textArea.Document.GetLineSegment(curLineNr); + var caretLineText = textArea.Document.GetText(lineWhereCaretIs.Offset, lineWhereCaretIs.TotalLength); + textArea.SelectionManager.SetSelection(textArea.Document.OffsetToPosition(lineWhereCaretIs.Offset), textArea.Document.OffsetToPosition(lineWhereCaretIs.Offset + lineWhereCaretIs.TotalLength)); + if (CopyTextToClipboard(caretLineText, asLine: true)) + { + if (textArea.SelectionManager.SelectionIsReadonly) + return; + // remove line + textArea.BeginUpdate(); + textArea.Caret.Position = textArea.Document.OffsetToPosition(lineWhereCaretIs.Offset); + textArea.SelectionManager.RemoveSelectedText(); + textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.PositionToEnd, new TextLocation(column: 0, curLineNr))); + textArea.EndUpdate(); + } + } + } + + public void Copy(object sender, EventArgs e) + { + if (!CopyTextToClipboard(textArea.SelectionManager.SelectedText) && textArea.Document.TextEditorProperties.CutCopyWholeLine) + { + // No text was selected, select the entire line, copy it, and then deselect + var curLineNr = textArea.Document.GetLineNumberForOffset(textArea.Caret.Offset); + var lineWhereCaretIs = textArea.Document.GetLineSegment(curLineNr); + var caretLineText = textArea.Document.GetText(lineWhereCaretIs.Offset, lineWhereCaretIs.TotalLength); + CopyTextToClipboard(caretLineText, asLine: true); + } + } + + public void Paste(object sender, EventArgs e) + { + if (!textArea.EnableCutOrPaste) + return; + // Clipboard.GetDataObject may throw an exception... + for (var i = 0;; i++) + try + { + var data = Clipboard.GetDataObject(); + if (data == null) + return; + var fullLine = data.GetDataPresent(LineSelectedType); + if (data.GetDataPresent(DataFormats.UnicodeText)) + { + var text = (string)data.GetData(DataFormats.UnicodeText); + // we got NullReferenceExceptions here, apparently the clipboard can contain null strings + if (!string.IsNullOrEmpty(text)) + { + textArea.Document.UndoStack.StartUndoGroup(); + try + { + if (textArea.SelectionManager.HasSomethingSelected) + { + textArea.Caret.Position = textArea.SelectionManager.SelectionCollection[index: 0].StartPosition; + textArea.SelectionManager.RemoveSelectedText(); + } + + if (fullLine) + { + var col = textArea.Caret.Column; + textArea.Caret.Column = 0; + if (!textArea.IsReadOnly(textArea.Caret.Offset)) + textArea.InsertString(text); + textArea.Caret.Column = col; + } + else + { + // textArea.EnableCutOrPaste already checked readonly for this case + textArea.InsertString(text); + } + } + finally + { + textArea.Document.UndoStack.EndUndoGroup(); + } + } + } + + return; + } + catch (ExternalException) + { + // GetDataObject does not provide RetryTimes parameter + if (i > 5) throw; + } + } + + public void Delete(object sender, EventArgs e) + { + new Delete().Execute(textArea); + } + + public void SelectAll(object sender, EventArgs e) + { + new SelectWholeDocument().Execute(textArea); + } + + protected virtual void OnCopyText(CopyTextEventArgs e) + { + CopyText?.Invoke(this, e); + } + + public event CopyTextEventHandler CopyText; + } + + public delegate void CopyTextEventHandler(object sender, CopyTextEventArgs e); + + public class CopyTextEventArgs : EventArgs + { + public CopyTextEventArgs(string text) + { + Text = text; + } + + public string Text { get; } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/TextAreaControl.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/TextAreaControl.cs new file mode 100644 index 0000000..b77ff40 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/TextAreaControl.cs @@ -0,0 +1,552 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Linq; +using System.Windows.Forms; +using ICSharpCode.TextEditor.Document; +using ICSharpCode.TextEditor.Util; + +namespace ICSharpCode.TextEditor +{ + /// + /// This class paints the textarea. + /// + [ToolboxItem(defaultType: false)] + public class TextAreaControl : Panel + { + private const int LineLengthCacheAdditionalSize = 100; + + private readonly MouseWheelHandler mouseWheelHandler = new MouseWheelHandler(); + + private readonly int scrollMarginHeight = 3; + + private bool adjustScrollBarsOnNextUpdate; + + private bool disposed; + + private HRuler hRuler; + + private int[] lineLengthCache; + private TextEditorControl motherTextEditorControl; + private Point scrollToPosOnNextUpdate; + + public TextAreaControl(TextEditorControl motherTextEditorControl) + { + this.motherTextEditorControl = motherTextEditorControl; + + TextArea = new TextArea(motherTextEditorControl, this); + Controls.Add(TextArea); + + VScrollBar.ValueChanged += VScrollBarValueChanged; + Controls.Add(VScrollBar); + + HScrollBar.ValueChanged += HScrollBarValueChanged; + Controls.Add(HScrollBar); + ResizeRedraw = true; + + Document.TextContentChanged += DocumentTextContentChanged; + Document.DocumentChanged += AdjustScrollBarsOnDocumentChange; + Document.UpdateCommited += DocumentUpdateCommitted; + } + + public TextArea TextArea { get; } + + public SelectionManager SelectionManager => TextArea.SelectionManager; + + public Caret Caret => TextArea.Caret; + + [Browsable(browsable: false)] + public IDocument Document => motherTextEditorControl?.Document; + + public ITextEditorProperties TextEditorProperties => motherTextEditorControl?.TextEditorProperties; + + public VScrollBar VScrollBar { get; private set; } = new VScrollBar(); + + public HScrollBar HScrollBar { get; private set; } = new HScrollBar(); + + public bool DoHandleMousewheel { get; set; } = true; + + protected override void Dispose(bool disposing) + { + if (disposing) + if (!disposed) + { + disposed = true; + Document.TextContentChanged -= DocumentTextContentChanged; + Document.DocumentChanged -= AdjustScrollBarsOnDocumentChange; + Document.UpdateCommited -= DocumentUpdateCommitted; + motherTextEditorControl = null; + if (VScrollBar != null) + { + VScrollBar.Dispose(); + VScrollBar = null; + } + + if (HScrollBar != null) + { + HScrollBar.Dispose(); + HScrollBar = null; + } + + if (hRuler != null) + { + hRuler.Dispose(); + hRuler = null; + } + } + + base.Dispose(disposing); + } + + private void DocumentTextContentChanged(object sender, EventArgs e) + { + // after the text content is changed abruptly, we need to validate the + // caret position - otherwise the caret position is invalid for a short amount + // of time, which can break client code that expects that the caret position is always valid + Caret.ValidateCaretPos(); + } + + protected override void OnResize(EventArgs e) + { + base.OnResize(e); + UpdateLayout(); + } + + private void AdjustScrollBarsOnDocumentChange(object sender, DocumentEventArgs e) + { + if (motherTextEditorControl.IsInUpdate == false) + { + AdjustScrollBarsClearCache(); + UpdateLayout(); + } + else + { + adjustScrollBarsOnNextUpdate = true; + } + } + + private void DocumentUpdateCommitted(object sender, EventArgs e) + { + if (motherTextEditorControl.IsInUpdate == false) + { + Caret.ValidateCaretPos(); + + // AdjustScrollBarsOnCommittedUpdate + if (!scrollToPosOnNextUpdate.IsEmpty) + ScrollTo(scrollToPosOnNextUpdate.Y, scrollToPosOnNextUpdate.X); + if (adjustScrollBarsOnNextUpdate) + { + AdjustScrollBarsClearCache(); + UpdateLayout(); + } + } + } + + private void AdjustScrollBarsClearCache() + { + if (lineLengthCache != null) + { + if (lineLengthCache.Length < Document.TotalNumberOfLines + 2*LineLengthCacheAdditionalSize) + lineLengthCache = null; + else + Array.Clear(lineLengthCache, index: 0, lineLengthCache.Length); + } + } + + public void UpdateLayout() + { + if (TextArea == null) + return; + + adjustScrollBarsOnNextUpdate = false; + + var view = TextArea.TextView; + + var currentVisibilites = GetScrollVisibilities(HScrollBar.Visible, VScrollBar.Visible); + var visited = new HashSet(); + + // Start walking through any layout state transitions and see whether it ends up in a stable state. + var fromVisibilities = currentVisibilites; + while (true) + { + if (!visited.Add(fromVisibilities)) + // Returning to a visited state -- unstable, so make no change + break; + var bounds = Measure(fromVisibilities); + var to = ComputeScrollBarVisibilities(bounds.textArea.Size); + if (to.visibilities == fromVisibilities) + { + // Layout is stable -- apply it + ApplyLayout(fromVisibilities, to.maxLength, bounds); + break; + } + + fromVisibilities = to.visibilities; + } + + ScrollVisibilities GetScrollVisibilities(bool h, bool v) + { + return (h ? ScrollVisibilities.H : ScrollVisibilities.None) + | (v ? ScrollVisibilities.V : ScrollVisibilities.None); + } + + (Rectangle hRule, Rectangle textControl, Rectangle textArea, Rectangle hScroll, Rectangle vScroll) + Measure(ScrollVisibilities scrollVisibilities) + { + var v = scrollVisibilities.HasFlag(ScrollVisibilities.V); + var h = scrollVisibilities.HasFlag(ScrollVisibilities.H); + var vScrollSize = v ? SystemInformation.VerticalScrollBarArrowHeight : 0; + var hScrollSize = h ? SystemInformation.HorizontalScrollBarArrowWidth : 0; + var x0 = TextArea.LeftMargins.Where(margin => margin.IsVisible).Sum(margin => margin.Width); + + var hRuleBounds = hRuler != null + ? new Rectangle( + x: 0, + y: 0, + Width - vScrollSize, + TextArea.TextView.FontHeight) + : default; + + var textControlBounds = new Rectangle( + x: 0, + hRuleBounds.Bottom, + Width - vScrollSize, + Height - hRuleBounds.Bottom - hScrollSize); + + var textAreaBounds = new Rectangle( + x0, + hRuleBounds.Bottom, + Width - x0 - vScrollSize, + Height - hRuleBounds.Bottom - hScrollSize); + + var vScrollBounds = v + ? new Rectangle( + textAreaBounds.Right, + y: 0, + SystemInformation.HorizontalScrollBarArrowWidth, + Height - hScrollSize) + : default; + + var hScrollBounds = h + ? new Rectangle( + x: 0, + textAreaBounds.Bottom, + Width - vScrollSize, + SystemInformation.VerticalScrollBarArrowHeight) + : default; + + return (hRuleBounds, textControlBounds, textAreaBounds, hScrollBounds, vScrollBounds); + } + + (ScrollVisibilities visibilities, int maxLength) ComputeScrollBarVisibilities(Size size) + { + var visibleLineCount = 1 + size.Height/view.FontHeight; + var visibleColumnCount = size.Width/view.WideSpaceWidth - 1; + + var firstLine = view.FirstVisibleLine; + + var lastLine = Document.GetFirstLogicalLine(firstLine + visibleLineCount); + if (lastLine >= Document.TotalNumberOfLines) + lastLine = Document.TotalNumberOfLines - 1; + + if (lineLengthCache == null || lineLengthCache.Length <= lastLine) + lineLengthCache = new int[lastLine + LineLengthCacheAdditionalSize]; + + var maxLength = 0; + for (var lineNumber = firstLine; lineNumber <= lastLine; lineNumber++) + { + var lineSegment = Document.GetLineSegment(lineNumber); + if (Document.FoldingManager.IsLineVisible(lineNumber)) + { + if (lineLengthCache[lineNumber] > 0) + { + maxLength = Math.Max(maxLength, lineLengthCache[lineNumber]); + } + else + { + var visualLength = view.GetVisualColumnFast(lineSegment, lineSegment.Length); + lineLengthCache[lineNumber] = Math.Max(1, visualLength); + maxLength = Math.Max(maxLength, visualLength); + } + } + } + + var vScrollBarVisible = VScrollBar.Value != 0 || TextArea.Document.TotalNumberOfLines >= visibleLineCount; + var hScrollBarVisible = HScrollBar.Value != 0 || maxLength > visibleColumnCount; + + return (GetScrollVisibilities(hScrollBarVisible, vScrollBarVisible), maxLength); + } + + void ApplyLayout(ScrollVisibilities scrollVisibilities, int maxColumn, (Rectangle hRule, Rectangle textControl, Rectangle textArea, Rectangle hScroll, Rectangle vScroll) bounds) + { + var visibleColumnCount = bounds.textArea.Width/view.WideSpaceWidth - 1; + + VScrollBar.Minimum = 0; + VScrollBar.Maximum = TextArea.MaxVScrollValue; // number of visible lines in document (folding!) + VScrollBar.LargeChange = Math.Max(0, bounds.textArea.Height); + VScrollBar.SmallChange = Math.Max(0, view.FontHeight); + VScrollBar.Visible = scrollVisibilities.HasFlag(ScrollVisibilities.V); + VScrollBar.Bounds = bounds.vScroll; + + HScrollBar.Minimum = 0; + HScrollBar.Maximum = Math.Max(maxColumn, visibleColumnCount - 1); + HScrollBar.LargeChange = Math.Max(0, visibleColumnCount - 1); + HScrollBar.SmallChange = 4; + HScrollBar.Visible = scrollVisibilities.HasFlag(ScrollVisibilities.H); + HScrollBar.Bounds = bounds.hScroll; + + if (hRuler != null) + hRuler.Bounds = bounds.hRule; + + TextArea.Bounds = bounds.textControl; + } + } + + public void OptionsChanged() + { + TextArea.OptionsChanged(); + + if (TextArea.TextEditorProperties.ShowHorizontalRuler) + { + if (hRuler == null) + { + hRuler = new HRuler(TextArea); + Controls.Add(hRuler); + UpdateLayout(); + } + else + { + hRuler.Invalidate(); + } + } + else + { + if (hRuler != null) + { + Controls.Remove(hRuler); + hRuler.Dispose(); + hRuler = null; + UpdateLayout(); + } + } + + UpdateLayout(); + } + + private void VScrollBarValueChanged(object sender, EventArgs e) + { + TextArea.VirtualTop = new Point(TextArea.VirtualTop.X, VScrollBar.Value); + TextArea.Invalidate(); + UpdateLayout(); + } + + private void HScrollBarValueChanged(object sender, EventArgs e) + { + TextArea.VirtualTop = new Point(HScrollBar.Value*TextArea.TextView.WideSpaceWidth, TextArea.VirtualTop.Y); + TextArea.Invalidate(); + } + + public void HandleMouseWheel(MouseEventArgs e) + { + var scrollDistance = mouseWheelHandler.GetScrollAmount(e); + if (scrollDistance == 0) + return; + if (ModifierKeys.HasFlag(Keys.Control) && TextEditorProperties.MouseWheelTextZoom) + { + if (scrollDistance > 0) + motherTextEditorControl.Font = new Font( + motherTextEditorControl.Font.Name, + motherTextEditorControl.Font.Size + 1); + else + motherTextEditorControl.Font = new Font( + motherTextEditorControl.Font.Name, + Math.Max(6, motherTextEditorControl.Font.Size - 1)); + } + else + { + if (TextEditorProperties.MouseWheelScrollDown) + scrollDistance = -scrollDistance; + if (ModifierKeys.HasFlag(Keys.Shift)) + { + var newValue = HScrollBar.Value + HScrollBar.SmallChange*scrollDistance; + HScrollBar.Value = Math.Max(HScrollBar.Minimum, Math.Min(HScrollBar.Maximum - HScrollBar.LargeChange + 1, newValue)); + } + else + { + var newValue = VScrollBar.Value + VScrollBar.SmallChange*scrollDistance; + VScrollBar.Value = Math.Max(VScrollBar.Minimum, Math.Min(VScrollBar.Maximum - VScrollBar.LargeChange + 1, newValue)); + } + } + } + + protected override void OnMouseWheel(MouseEventArgs e) + { + base.OnMouseWheel(e); + if (DoHandleMousewheel) + HandleMouseWheel(e); + } + + public void ScrollToCaret() + { + ScrollTo(TextArea.Caret.Line, TextArea.Caret.Column); + } + + public void ScrollTo(int line, int column) + { + if (motherTextEditorControl.IsInUpdate) + { + scrollToPosOnNextUpdate = new Point(column, line); + return; + } + + scrollToPosOnNextUpdate = Point.Empty; + + ScrollTo(line); + + var curCharMin = HScrollBar.Value - HScrollBar.Minimum; + var curCharMax = curCharMin + TextArea.TextView.VisibleColumnCount; + + var pos = TextArea.TextView.GetVisualColumn(line, column); + + if (TextArea.TextView.VisibleColumnCount < 0) + { + HScrollBar.Value = 0; + } + else + { + if (pos < curCharMin) + { + HScrollBar.Value = Math.Max(0, pos - scrollMarginHeight); + } + else + { + if (pos > curCharMax) + HScrollBar.Value = Math.Max(0, Math.Min(HScrollBar.Maximum, pos - TextArea.TextView.VisibleColumnCount + scrollMarginHeight)); + } + } + } + + /// + /// Ensure that is visible. + /// + public void ScrollTo(int line) + { + line = Math.Max(0, Math.Min(Document.TotalNumberOfLines - 1, line)); + line = Document.GetVisibleLine(line); + var curLineMin = TextArea.TextView.FirstPhysicalLine; + if (TextArea.TextView.LineHeightRemainder > 0) + curLineMin++; + + if (line - scrollMarginHeight + 3 < curLineMin) + { + VScrollBar.Value = Math.Max(0, Math.Min(VScrollBar.Maximum, (line - scrollMarginHeight + 3)*TextArea.TextView.FontHeight)); + VScrollBarValueChanged(this, EventArgs.Empty); + } + else + { + var curLineMax = curLineMin + TextArea.TextView.VisibleLineCount; + if (line + scrollMarginHeight - 1 > curLineMax) + { + if (TextArea.TextView.VisibleLineCount == 1) + VScrollBar.Value = Math.Max(0, Math.Min(VScrollBar.Maximum, (line - scrollMarginHeight - 1)*TextArea.TextView.FontHeight)); + else + VScrollBar.Value = Math.Min( + VScrollBar.Maximum, + (line - TextArea.TextView.VisibleLineCount + scrollMarginHeight - 1)*TextArea.TextView.FontHeight); + VScrollBarValueChanged(this, EventArgs.Empty); + } + } + } + + /// + /// Scroll so that the specified line is centered. + /// + /// Line to center view on + /// + /// If this action would cause scrolling by less than or equal to + /// lines in any direction, don't scroll. + /// Use -1 to always center the view. + /// + public void CenterViewOn(int line, int treshold) + { + line = Math.Max(0, Math.Min(Document.TotalNumberOfLines - 1, line)); + // convert line to visible line: + line = Document.GetVisibleLine(line); + // subtract half the visible line count + line -= TextArea.TextView.VisibleLineCount/2; + + var curLineMin = TextArea.TextView.FirstPhysicalLine; + if (TextArea.TextView.LineHeightRemainder > 0) + curLineMin++; + if (Math.Abs(curLineMin - line) > treshold) + { + // scroll: + VScrollBar.Value = Math.Max(0, Math.Min(VScrollBar.Maximum, (line - scrollMarginHeight + 3)*TextArea.TextView.FontHeight)); + VScrollBarValueChanged(this, EventArgs.Empty); + } + } + + public void JumpTo(int line) + { + line = Math.Max(0, Math.Min(line, Document.TotalNumberOfLines - 1)); + var text = Document.GetText(Document.GetLineSegment(line)); + JumpTo(line, text.Length - text.TrimStart().Length); + } + + public void JumpTo(int line, int column) + { + TextArea.Focus(); + TextArea.SelectionManager.ClearSelection(); + TextArea.Caret.Position = new TextLocation(column, line); + TextArea.SetDesiredColumn(); + ScrollToCaret(); + } + + public event MouseEventHandler ShowContextMenu; + + protected override void WndProc(ref Message m) + { + if (m.Msg == 0x007B) + if (ShowContextMenu != null) + { + Point location = m.LParam.ToPoint(); + if (location.X == -1 && location.Y == -1) + { + var pos = Caret.ScreenPosition; + ShowContextMenu?.Invoke(this, new MouseEventArgs(MouseButtons.None, clicks: 0, pos.X, pos.Y + TextArea.TextView.FontHeight, delta: 0)); + } + else + { + var pos = PointToClient(location); + ShowContextMenu?.Invoke(this, new MouseEventArgs(MouseButtons.Right, clicks: 1, pos.X, pos.Y, delta: 0)); + } + } + + base.WndProc(ref m); + } + + protected override void OnEnter(EventArgs e) + { + // SD2-1072 - Make sure the caret line is valid if anyone + // has handlers for the Enter event. + Caret.ValidateCaretPos(); + base.OnEnter(e); + } + + [Flags] + private enum ScrollVisibilities + { + None = 0, + H = 1, + V = 2 + } + } +} diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/TextAreaDragDropHandler.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/TextAreaDragDropHandler.cs new file mode 100644 index 0000000..20b2c9f --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/TextAreaDragDropHandler.cs @@ -0,0 +1,182 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Drawing; +using System.Windows.Forms; +using ICSharpCode.TextEditor.Document; + +namespace ICSharpCode.TextEditor +{ + public class TextAreaDragDropHandler + { + public static Action OnDragDropException = ex => MessageBox.Show(ex.ToString()); + + private TextArea textArea; + + public void Attach(TextArea textArea) + { + this.textArea = textArea; + textArea.AllowDrop = true; + + textArea.DragEnter += MakeDragEventHandler(OnDragEnter); + textArea.DragDrop += MakeDragEventHandler(OnDragDrop); + textArea.DragOver += MakeDragEventHandler(OnDragOver); + } + + /// + /// Create a drag'n'drop event handler. + /// Windows Forms swallows unhandled exceptions during drag'n'drop, so we report them here. + /// + private static DragEventHandler MakeDragEventHandler(DragEventHandler h) + { + return (sender, e) => + { + try + { + h(sender, e); + } + catch (Exception ex) + { + OnDragDropException(ex); + } + }; + } + + private static DragDropEffects GetDragDropEffect(DragEventArgs e) + { + if ((e.AllowedEffect & DragDropEffects.Move) > 0 && + (e.AllowedEffect & DragDropEffects.Copy) > 0) + return (e.KeyState & 8) > 0 ? DragDropEffects.Copy : DragDropEffects.Move; + + if ((e.AllowedEffect & DragDropEffects.Move) > 0) + return DragDropEffects.Move; + + if ((e.AllowedEffect & DragDropEffects.Copy) > 0) + return DragDropEffects.Copy; + return DragDropEffects.None; + } + + protected void OnDragEnter(object sender, DragEventArgs e) + { + if (IsSupportedData(e.Data)) + e.Effect = GetDragDropEffect(e); + } + + private void InsertString(int offset, string str) + { + if (str == null) + return; + + textArea.Document.Insert(offset, str); + + textArea.SelectionManager.SetSelection( + new DefaultSelection( + textArea.Document, + textArea.Document.OffsetToPosition(offset), + textArea.Document.OffsetToPosition(offset + str.Length))); + textArea.Caret.Position = textArea.Document.OffsetToPosition(offset + str.Length); + textArea.Refresh(); + } + + protected void OnDragDrop(object sender, DragEventArgs e) + { + if (!IsSupportedData(e.Data)) + { + return; + } + + textArea.BeginUpdate(); + textArea.Document.UndoStack.StartUndoGroup(); + try + { + var offset = textArea.Caret.Offset; + if (textArea.IsReadOnly(offset)) + return; + + try + { + if (e.Data.GetDataPresent(typeof(DefaultSelection))) + { + var sel = (ISelection)e.Data.GetData(typeof(DefaultSelection)); + if (sel.ContainsPosition(textArea.Caret.Position)) + return; + if (GetDragDropEffect(e) == DragDropEffects.Move) + { + if (SelectionManager.SelectionIsReadOnly(textArea.Document, sel)) + return; + var len = sel.Length; + textArea.Document.Remove(sel.Offset, len); + if (sel.Offset < offset) + offset -= len; + } + } + } + catch (System.InvalidCastException) + { + /* + If GetDataPresent(typeof(DefaultSelection)) threw this + exception, then it's an interprocess DefaultSelection + COM object that is not serializable! In general, + GetDataPresent(typeof(...)) throws InvalidCastException + for drags and drops from other GitExt processes (maybe + we need to make the data objects [Serializable]?). We + can get around this exception by doing + GetDataPresent(String s) [using the string of the type + name seems to work fine!] Since it is interprocess + data, just get the string data from it - special + handling logic in try {} is only valid for selections + within the current process's text editor! + */ + } + + textArea.SelectionManager.ClearSelection(); + InsertString(offset, (string)e.Data.GetData("System.String")); + textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.WholeTextArea)); + } + finally + { + textArea.Document.UndoStack.EndUndoGroup(); + textArea.EndUpdate(); + } + } + + protected void OnDragOver(object sender, DragEventArgs e) + { + if (!textArea.Focused) + textArea.Focus(); + + var p = textArea.PointToClient(new Point(e.X, e.Y)); + + if (textArea.TextView.DrawingPosition.Contains(p.X, p.Y)) + { + var realmousepos = textArea.TextView.GetLogicalPosition( + p.X - textArea.TextView.DrawingPosition.X, + p.Y - textArea.TextView.DrawingPosition.Y); + var lineNr = Math.Min(textArea.Document.TotalNumberOfLines - 1, Math.Max(val1: 0, realmousepos.Y)); + + textArea.Caret.Position = new TextLocation(realmousepos.X, lineNr); + textArea.SetDesiredColumn(); + if (IsSupportedData(e.Data) && !textArea.IsReadOnly(textArea.Caret.Offset)) + e.Effect = GetDragDropEffect(e); + else + e.Effect = DragDropEffects.None; + } + else + { + e.Effect = DragDropEffects.None; + } + } + + private static bool IsSupportedData(IDataObject data) + { + return data.GetDataPresent(DataFormats.StringFormat) + || data.GetDataPresent(DataFormats.Text) + || data.GetDataPresent(DataFormats.UnicodeText); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/TextAreaMouseHandler.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/TextAreaMouseHandler.cs new file mode 100644 index 0000000..f226827 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/TextAreaMouseHandler.cs @@ -0,0 +1,522 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using ICSharpCode.TextEditor.Document; + +namespace ICSharpCode.TextEditor +{ + /// + /// This class handles all mouse stuff for a textArea. + /// + public class TextAreaMouseHandler + { + private static readonly Point nilPoint = new Point(x: -1, y: -1); + private readonly TextArea textArea; + + private MouseButtons button; + private bool clickedOnSelectedText; + private bool dodragdrop; + private bool doubleclick; + + private bool gotmousedown; + private Point lastmousedownpos = nilPoint; + private TextLocation maxSelection = TextLocation.Empty; + + private TextLocation minSelection = TextLocation.Empty; + private Point mousedownpos = nilPoint; + + public TextAreaMouseHandler(TextArea ttextArea) + { + textArea = ttextArea; + } + + public void Attach() + { + textArea.Click += TextAreaClick; + textArea.MouseMove += TextAreaMouseMove; + + textArea.MouseDown += OnMouseDown; + textArea.DoubleClick += OnDoubleClick; + textArea.MouseLeave += OnMouseLeave; + textArea.MouseUp += OnMouseUp; + textArea.LostFocus += TextAreaLostFocus; + textArea.ToolTipRequest += OnToolTipRequest; + } + + private void OnToolTipRequest(object sender, ToolTipRequestEventArgs e) + { + if (e.ToolTipShown) + return; + var mousepos = e.MousePosition; + var marker = textArea.TextView.GetFoldMarkerFromPosition( + mousepos.X - textArea.TextView.DrawingPosition.X, + mousepos.Y - textArea.TextView.DrawingPosition.Y); + if (marker != null && marker.IsFolded) + { + var sb = new StringBuilder(marker.InnerText); + + // max 10 lines + var endLines = 0; + for (var i = 0; i < sb.Length; ++i) + if (sb[i] == '\n') + { + ++endLines; + if (endLines >= 10) + { + sb.Remove(i + 1, sb.Length - i - 1); + sb.Append(Environment.NewLine); + sb.Append("..."); + break; + } + } + + sb.Replace("\t", " "); + e.ShowToolTip(sb.ToString()); + return; + } + + var markers = textArea.Document.MarkerStrategy.GetMarkers(e.LogicalPosition); + foreach (var tm in markers) + if (tm.ToolTip != null) + { + e.ShowToolTip(tm.ToolTip.Replace("\t", " ")); + return; + } + } + + private void ShowHiddenCursorIfMovedOrLeft() + { + textArea.ShowHiddenCursor( + !textArea.Focused || + !textArea.ClientRectangle.Contains(textArea.PointToClient(Cursor.Position))); + } + + private void TextAreaLostFocus(object sender, EventArgs e) + { + // The call to ShowHiddenCursorIfMovedOrLeft is delayed + // until pending messages have been processed + // so that it can properly detect whether the TextArea + // has really lost focus. + // For example, the CodeCompletionWindow gets focus when it is shown, + // but immediately gives back focus to the TextArea. + textArea.BeginInvoke(new MethodInvoker(ShowHiddenCursorIfMovedOrLeft)); + } + + private void OnMouseLeave(object sender, EventArgs e) + { + ShowHiddenCursorIfMovedOrLeft(); + gotmousedown = false; + mousedownpos = nilPoint; + } + + private void OnMouseUp(object sender, MouseEventArgs e) + { + textArea.SelectionManager.selectFrom.where = WhereFrom.None; + gotmousedown = false; + mousedownpos = nilPoint; + } + + private void TextAreaClick(object sender, EventArgs e) + { + if (dodragdrop) + return; + + var mousepos = textArea.mousepos; + + if (clickedOnSelectedText && textArea.TextView.DrawingPosition.Contains(mousepos.X, mousepos.Y)) + { + textArea.SelectionManager.ClearSelection(); + + var clickPosition = textArea.TextView.GetLogicalPosition( + mousepos.X - textArea.TextView.DrawingPosition.X, + mousepos.Y - textArea.TextView.DrawingPosition.Y); + textArea.Caret.Position = clickPosition; + textArea.SetDesiredColumn(); + } + } + + private void TextAreaMouseMove(object sender, MouseEventArgs e) + { + textArea.mousepos = e.Location; + + // honour the starting selection strategy + switch (textArea.SelectionManager.selectFrom.where) + { + case WhereFrom.Gutter: + ExtendSelectionToMouse(); + return; + + case WhereFrom.TArea: + break; + } + + textArea.ShowHiddenCursor(forceShow: false); + if (dodragdrop) + { + dodragdrop = false; + return; + } + + doubleclick = false; + textArea.mousepos = new Point(e.X, e.Y); + + if (clickedOnSelectedText) + { + if (Math.Abs(mousedownpos.X - e.X) >= SystemInformation.DragSize.Width/2 || + Math.Abs(mousedownpos.Y - e.Y) >= SystemInformation.DragSize.Height/2) + { + clickedOnSelectedText = false; + var selection = textArea.SelectionManager.GetSelectionAt(textArea.Caret.Offset); + if (selection != null) + { + var text = selection.SelectedText; + var isReadOnly = SelectionManager.SelectionIsReadOnly(textArea.Document, selection); + if (!string.IsNullOrEmpty(text)) + { + var dataObject = new DataObject(); + dataObject.SetData(DataFormats.UnicodeText, autoConvert: true, text); + dataObject.SetData(selection); + dodragdrop = true; + textArea.DoDragDrop(dataObject, isReadOnly ? DragDropEffects.All & ~DragDropEffects.Move : DragDropEffects.All); + } + } + } + + return; + } + + if (e.Button == MouseButtons.Left) + if (gotmousedown && textArea.SelectionManager.selectFrom.where == WhereFrom.TArea) + ExtendSelectionToMouse(); + } + + private void ExtendSelectionToMouse() + { + var mousepos = textArea.mousepos; + var realmousepos = textArea.TextView.GetLogicalPosition( + Math.Max(val1: 0, mousepos.X - textArea.TextView.DrawingPosition.X), + mousepos.Y - textArea.TextView.DrawingPosition.Y); +// var y = realmousepos.Y; + realmousepos = textArea.Caret.ValidatePosition(realmousepos); + var oldPos = textArea.Caret.Position; + if (oldPos == realmousepos && textArea.SelectionManager.selectFrom.where != WhereFrom.Gutter) + return; + + // the selection is from the gutter + if (textArea.SelectionManager.selectFrom.where == WhereFrom.Gutter) + { + if (realmousepos.Y < textArea.SelectionManager.SelectionStart.Y) + textArea.Caret.Position = new TextLocation(column: 0, realmousepos.Y); + else + textArea.Caret.Position = textArea.SelectionManager.NextValidPosition(realmousepos.Y); + } + else + { + textArea.Caret.Position = realmousepos; + } + + // moves selection across whole words for double-click initiated selection + if (!minSelection.IsEmpty && textArea.SelectionManager.SelectionCollection.Count > 0 && textArea.SelectionManager.selectFrom.where == WhereFrom.TArea) + { + // Extend selection when selection was started with double-click +// var selection = textArea.SelectionManager.SelectionCollection[index: 0]; + var min = textArea.SelectionManager.GreaterEqPos(minSelection, maxSelection) ? maxSelection : minSelection; + var max = textArea.SelectionManager.GreaterEqPos(minSelection, maxSelection) ? minSelection : maxSelection; + if (textArea.SelectionManager.GreaterEqPos(max, realmousepos) && textArea.SelectionManager.GreaterEqPos(realmousepos, min)) + { + textArea.SelectionManager.SetSelection(min, max); + } + else if (textArea.SelectionManager.GreaterEqPos(max, realmousepos)) + { + var moff = textArea.Document.PositionToOffset(realmousepos); + min = textArea.Document.OffsetToPosition(FindWordStart(textArea.Document, moff)); + textArea.SelectionManager.SetSelection(min, max); + } + else + { + var moff = textArea.Document.PositionToOffset(realmousepos); + max = textArea.Document.OffsetToPosition(FindWordEnd(textArea.Document, moff)); + textArea.SelectionManager.SetSelection(min, max); + } + } + else + { + textArea.SelectionManager.ExtendSelection(oldPos, textArea.Caret.Position); + } + + textArea.SetDesiredColumn(); + } + + private void DoubleClickSelectionExtend() + { + var mousepos = textArea.mousepos; + + textArea.SelectionManager.ClearSelection(); + if (textArea.TextView.DrawingPosition.Contains(mousepos.X, mousepos.Y)) + { + var marker = textArea.TextView.GetFoldMarkerFromPosition( + mousepos.X - textArea.TextView.DrawingPosition.X, + mousepos.Y - textArea.TextView.DrawingPosition.Y); + if (marker != null && marker.IsFolded) + { + marker.IsFolded = false; + textArea.MotherTextAreaControl.UpdateLayout(); + } + + if (textArea.Caret.Offset < textArea.Document.TextLength) + { + switch (textArea.Document.GetCharAt(textArea.Caret.Offset)) + { + case '"': + if (textArea.Caret.Offset < textArea.Document.TextLength) + { + var next = FindNext(textArea.Document, textArea.Caret.Offset + 1, ch: '"'); + minSelection = textArea.Caret.Position; + if (next > textArea.Caret.Offset && next < textArea.Document.TextLength) + next += 1; + maxSelection = textArea.Document.OffsetToPosition(next); + } + + break; + default: + minSelection = textArea.Document.OffsetToPosition(FindWordStart(textArea.Document, textArea.Caret.Offset)); + maxSelection = textArea.Document.OffsetToPosition(FindWordEnd(textArea.Document, textArea.Caret.Offset)); + break; + } + + textArea.Caret.Position = maxSelection; + textArea.SelectionManager.ExtendSelection(minSelection, maxSelection); + } + + if (textArea.SelectionManager.selectionCollection.Count > 0) + { + var selection = textArea.SelectionManager.selectionCollection[index: 0]; + + selection.StartPosition = minSelection; + selection.EndPosition = maxSelection; + textArea.SelectionManager.SelectionStart = minSelection; + } + + // after a double-click selection, the caret is placed correctly, + // but it is not positioned internally. The effect is when the cursor + // is moved up or down a line, the caret will take on the column first + // clicked on for the double-click + textArea.SetDesiredColumn(); + + // HACK WARNING !!! + // must refresh here, because when a error tooltip is showed and the underlined + // code is double clicked the textArea don't update corrctly, updateline doesn't + // work ... but the refresh does. + // Mike + textArea.Refresh(); + } + } + + private void OnMouseDown(object sender, MouseEventArgs e) + { + textArea.mousepos = e.Location; + var mousepos = e.Location; + + if (dodragdrop) + return; + + if (doubleclick) + { + doubleclick = false; + return; + } + + if (textArea.TextView.DrawingPosition.Contains(mousepos.X, mousepos.Y)) + { + gotmousedown = true; + textArea.SelectionManager.selectFrom.where = WhereFrom.TArea; + button = e.Button; + + // double-click + if (button == MouseButtons.Left && e.Clicks == 2) + { + var deltaX = Math.Abs(lastmousedownpos.X - e.X); + var deltaY = Math.Abs(lastmousedownpos.Y - e.Y); + if (deltaX <= SystemInformation.DoubleClickSize.Width && + deltaY <= SystemInformation.DoubleClickSize.Height) + { + DoubleClickSelectionExtend(); + lastmousedownpos = new Point(e.X, e.Y); + + if (textArea.SelectionManager.selectFrom.where == WhereFrom.Gutter) + if (!minSelection.IsEmpty && !maxSelection.IsEmpty && textArea.SelectionManager.SelectionCollection.Count > 0) + { + textArea.SelectionManager.SelectionCollection[index: 0].StartPosition = minSelection; + textArea.SelectionManager.SelectionCollection[index: 0].EndPosition = maxSelection; + textArea.SelectionManager.SelectionStart = minSelection; + + minSelection = TextLocation.Empty; + maxSelection = TextLocation.Empty; + } + + return; + } + } + + minSelection = TextLocation.Empty; + maxSelection = TextLocation.Empty; + + lastmousedownpos = mousedownpos = new Point(e.X, e.Y); + + if (button == MouseButtons.Left) + { + var marker = textArea.TextView.GetFoldMarkerFromPosition( + mousepos.X - textArea.TextView.DrawingPosition.X, + mousepos.Y - textArea.TextView.DrawingPosition.Y); + if (marker != null && marker.IsFolded) + { + if (textArea.SelectionManager.HasSomethingSelected) + clickedOnSelectedText = true; + + var startLocation = new TextLocation(marker.StartColumn, marker.StartLine); + var endLocation = new TextLocation(marker.EndColumn, marker.EndLine); + textArea.SelectionManager.SetSelection(new DefaultSelection(textArea.TextView.Document, startLocation, endLocation)); + textArea.Caret.Position = startLocation; + textArea.SetDesiredColumn(); + textArea.Focus(); + return; + } + + if ((Control.ModifierKeys & Keys.Shift) == Keys.Shift) + { + ExtendSelectionToMouse(); + } + else + { + var realmousepos = textArea.TextView.GetLogicalPosition(mousepos.X - textArea.TextView.DrawingPosition.X, mousepos.Y - textArea.TextView.DrawingPosition.Y); + clickedOnSelectedText = false; + + var offset = textArea.Document.PositionToOffset(realmousepos); + + if (textArea.SelectionManager.HasSomethingSelected && + textArea.SelectionManager.IsSelected(offset)) + { + clickedOnSelectedText = true; + } + else + { + textArea.SelectionManager.ClearSelection(); + if (mousepos.Y > 0 && mousepos.Y < textArea.TextView.DrawingPosition.Height) + { + var pos = new TextLocation(); + pos.Y = Math.Min(textArea.Document.TotalNumberOfLines - 1, realmousepos.Y); + pos.X = realmousepos.X; + textArea.Caret.Position = pos; + textArea.SetDesiredColumn(); + } + } + } + } + else if (button == MouseButtons.Right) + { + // Rightclick sets the cursor to the click position unless + // the previous selection was clicked + var realmousepos = textArea.TextView.GetLogicalPosition(mousepos.X - textArea.TextView.DrawingPosition.X, mousepos.Y - textArea.TextView.DrawingPosition.Y); + var offset = textArea.Document.PositionToOffset(realmousepos); + if (!textArea.SelectionManager.HasSomethingSelected || + !textArea.SelectionManager.IsSelected(offset)) + { + textArea.SelectionManager.ClearSelection(); + if (mousepos.Y > 0 && mousepos.Y < textArea.TextView.DrawingPosition.Height) + { + var pos = new TextLocation(); + pos.Y = Math.Min(textArea.Document.TotalNumberOfLines - 1, realmousepos.Y); + pos.X = realmousepos.X; + textArea.Caret.Position = pos; + textArea.SetDesiredColumn(); + } + } + } + } + + textArea.Focus(); + } + + private static int FindNext(IDocument document, int offset, char ch) + { + var line = document.GetLineSegmentForOffset(offset); + var endPos = line.Offset + line.Length; + + while (offset < endPos && document.GetCharAt(offset) != ch) + ++offset; + return offset; + } + + private static bool IsSelectableChar(char ch) + { + return char.IsLetterOrDigit(ch) || ch == '_'; + } + + private static int FindWordStart(IDocument document, int offset) + { + var line = document.GetLineSegmentForOffset(offset); + + if (offset > 0 && char.IsWhiteSpace(document.GetCharAt(offset - 1)) && char.IsWhiteSpace(document.GetCharAt(offset))) + { + while (offset > line.Offset && char.IsWhiteSpace(document.GetCharAt(offset - 1))) + --offset; + } + else if (IsSelectableChar(document.GetCharAt(offset)) || offset > 0 && char.IsWhiteSpace(document.GetCharAt(offset)) && IsSelectableChar(document.GetCharAt(offset - 1))) + { + while (offset > line.Offset && IsSelectableChar(document.GetCharAt(offset - 1))) + --offset; + } + else + { + if (offset > 0 && !char.IsWhiteSpace(document.GetCharAt(offset - 1)) && !IsSelectableChar(document.GetCharAt(offset - 1))) + return Math.Max(val1: 0, offset - 1); + } + + return offset; + } + + private static int FindWordEnd(IDocument document, int offset) + { + var line = document.GetLineSegmentForOffset(offset); + if (line.Length == 0) + return offset; + var endPos = line.Offset + line.Length; + offset = Math.Min(offset, endPos - 1); + + if (IsSelectableChar(document.GetCharAt(offset))) + { + while (offset < endPos && IsSelectableChar(document.GetCharAt(offset))) + ++offset; + } + else if (char.IsWhiteSpace(document.GetCharAt(offset))) + { + if (offset > 0 && char.IsWhiteSpace(document.GetCharAt(offset - 1))) + while (offset < endPos && char.IsWhiteSpace(document.GetCharAt(offset))) + ++offset; + } + else + { + return Math.Max(val1: 0, offset + 1); + } + + return offset; + } + + private void OnDoubleClick(object sender, EventArgs e) + { + if (dodragdrop) + return; + + textArea.SelectionManager.selectFrom.where = WhereFrom.TArea; + doubleclick = true; + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/TextAreaUpdate.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/TextAreaUpdate.cs new file mode 100644 index 0000000..ec24acd --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/TextAreaUpdate.cs @@ -0,0 +1,72 @@ +// +// +// +// +// $Revision$ +// + +namespace ICSharpCode.TextEditor +{ + /// + /// This enum describes all implemented request types + /// + public enum TextAreaUpdateType + { + WholeTextArea, + SingleLine, + SinglePosition, + PositionToLineEnd, + PositionToEnd, + LinesBetween + } + + /// + /// This class is used to request an update of the textarea + /// + public class TextAreaUpdate + { + /// + /// Creates a new instance of + /// + public TextAreaUpdate(TextAreaUpdateType type) + { + TextAreaUpdateType = type; + } + + /// + /// Creates a new instance of + /// + public TextAreaUpdate(TextAreaUpdateType type, TextLocation position) + { + TextAreaUpdateType = type; + Position = position; + } + + /// + /// Creates a new instance of + /// + public TextAreaUpdate(TextAreaUpdateType type, int startLine, int endLine) + { + TextAreaUpdateType = type; + Position = new TextLocation(startLine, endLine); + } + + /// + /// Creates a new instance of + /// + public TextAreaUpdate(TextAreaUpdateType type, int singleLine) + { + TextAreaUpdateType = type; + Position = new TextLocation(column: 0, singleLine); + } + + public TextAreaUpdateType TextAreaUpdateType { get; } + + public TextLocation Position { get; } + + public override string ToString() + { + return string.Format("[TextAreaUpdate: Type={0}, Position={1}]", TextAreaUpdateType, Position); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/TextEditorControl.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/TextEditorControl.cs new file mode 100644 index 0000000..01698cf --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/TextEditorControl.cs @@ -0,0 +1,380 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.ComponentModel; +using System.Drawing; +using System.Drawing.Printing; +using System.Windows.Forms; +using ICSharpCode.TextEditor.Document; + +namespace ICSharpCode.TextEditor +{ + /// + /// This class is used for a basic text area control + /// + [ToolboxBitmap("ICSharpCode.TextEditor.Resources.TextEditorControl.bmp")] + [ToolboxItem(defaultType: true)] + public class TextEditorControl : TextEditorControlBase + { + private readonly TextAreaControl primaryTextArea; + + private TextAreaControl activeTextAreaControl; + + private PrintDocument printDocument; + private TextAreaControl secondaryTextArea; + protected Panel textAreaPanel = new Panel(); + private Splitter textAreaSplitter; + + public TextEditorControl() + { + SetStyle(ControlStyles.ContainerControl, value: true); + + textAreaPanel.Dock = DockStyle.Fill; + + Document = new DocumentFactory().CreateDocument(); + Document.HighlightingStrategy = HighlightingStrategyFactory.CreateHighlightingStrategy(); + + primaryTextArea = new TextAreaControl(this); + activeTextAreaControl = primaryTextArea; + primaryTextArea.TextArea.GotFocus += delegate { SetActiveTextAreaControl(primaryTextArea); }; + primaryTextArea.Dock = DockStyle.Fill; + textAreaPanel.Controls.Add(primaryTextArea); + InitializeTextAreaControl(primaryTextArea); + Controls.Add(textAreaPanel); + ResizeRedraw = true; + Document.UpdateCommited += CommitUpdateRequested; + OptionsChanged(); + } + + [Browsable(browsable: false)] + public PrintDocument PrintDocument + { + get + { + if (printDocument == null) + { + printDocument = new PrintDocument(); + printDocument.BeginPrint += BeginPrint; + printDocument.PrintPage += PrintPage; + } + + return printDocument; + } + } + + public override TextAreaControl ActiveTextAreaControl => activeTextAreaControl; + + [Browsable(browsable: false)] + public bool EnableUndo => Document.UndoStack.CanUndo; + + [Browsable(browsable: false)] + public bool EnableRedo => Document.UndoStack.CanRedo; + + protected void SetActiveTextAreaControl(TextAreaControl value) + { + if (activeTextAreaControl != value) + { + activeTextAreaControl = value; + + ActiveTextAreaControlChanged?.Invoke(this, EventArgs.Empty); + } + } + + public event EventHandler ActiveTextAreaControlChanged; + + protected virtual void InitializeTextAreaControl(TextAreaControl newControl) + { + } + + public override void OptionsChanged() + { + primaryTextArea.OptionsChanged(); + secondaryTextArea?.OptionsChanged(); + } + + public void Split() + { + if (secondaryTextArea == null) + { + secondaryTextArea = new TextAreaControl(this); + secondaryTextArea.Dock = DockStyle.Bottom; + secondaryTextArea.Height = Height/2; + + secondaryTextArea.TextArea.GotFocus += delegate { SetActiveTextAreaControl(secondaryTextArea); }; + + textAreaSplitter = new Splitter(); + textAreaSplitter.BorderStyle = BorderStyle.FixedSingle; + textAreaSplitter.Height = 8; + textAreaSplitter.Dock = DockStyle.Bottom; + textAreaPanel.Controls.Add(textAreaSplitter); + textAreaPanel.Controls.Add(secondaryTextArea); + InitializeTextAreaControl(secondaryTextArea); + secondaryTextArea.OptionsChanged(); + } + else + { + SetActiveTextAreaControl(primaryTextArea); + + textAreaPanel.Controls.Remove(secondaryTextArea); + textAreaPanel.Controls.Remove(textAreaSplitter); + + secondaryTextArea.Dispose(); + textAreaSplitter.Dispose(); + secondaryTextArea = null; + textAreaSplitter = null; + } + } + + public void Undo() + { + if (Document.ReadOnly) + return; + if (Document.UndoStack.CanUndo) + { + BeginUpdate(); + Document.UndoStack.Undo(); + + Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.WholeTextArea)); + primaryTextArea.TextArea.UpdateMatchingBracket(); + secondaryTextArea?.TextArea.UpdateMatchingBracket(); + EndUpdate(); + } + } + + public void Redo() + { + if (Document.ReadOnly) + return; + if (Document.UndoStack.CanRedo) + { + BeginUpdate(); + Document.UndoStack.Redo(); + + Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.WholeTextArea)); + primaryTextArea.TextArea.UpdateMatchingBracket(); + secondaryTextArea?.TextArea.UpdateMatchingBracket(); + EndUpdate(); + } + } + + public virtual void SetHighlighting(string name) + { + Document.HighlightingStrategy = HighlightingStrategyFactory.CreateHighlightingStrategy(name); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (printDocument != null) + { + printDocument.BeginPrint -= BeginPrint; + printDocument.PrintPage -= PrintPage; + printDocument = null; + } + + Document.UndoStack.ClearAll(); + Document.UpdateCommited -= CommitUpdateRequested; + if (textAreaPanel != null) + { + if (secondaryTextArea != null) + { + secondaryTextArea.Dispose(); + textAreaSplitter.Dispose(); + secondaryTextArea = null; + textAreaSplitter = null; + } + + primaryTextArea?.Dispose(); + textAreaPanel.Dispose(); + textAreaPanel = null; + } + } + + base.Dispose(disposing); + } + + #region Update Methods + + public override void EndUpdate() + { + base.EndUpdate(); + Document.CommitUpdate(); + if (!IsInUpdate) + ActiveTextAreaControl.Caret.OnEndUpdate(); + } + + private void CommitUpdateRequested(object sender, EventArgs e) + { + if (IsInUpdate) + return; + foreach (var update in Document.UpdateQueue) + switch (update.TextAreaUpdateType) + { + case TextAreaUpdateType.PositionToEnd: + primaryTextArea.TextArea.UpdateToEnd(update.Position.Y); + secondaryTextArea?.TextArea.UpdateToEnd(update.Position.Y); + break; + case TextAreaUpdateType.PositionToLineEnd: + case TextAreaUpdateType.SingleLine: + primaryTextArea.TextArea.UpdateLine(update.Position.Y); + secondaryTextArea?.TextArea.UpdateLine(update.Position.Y); + break; + case TextAreaUpdateType.SinglePosition: + primaryTextArea.TextArea.UpdateLine(update.Position.Y, update.Position.X, update.Position.X); + secondaryTextArea?.TextArea.UpdateLine(update.Position.Y, update.Position.X, update.Position.X); + break; + case TextAreaUpdateType.LinesBetween: + primaryTextArea.TextArea.UpdateLines(update.Position.X, update.Position.Y); + secondaryTextArea?.TextArea.UpdateLines(update.Position.X, update.Position.Y); + break; + case TextAreaUpdateType.WholeTextArea: + primaryTextArea.TextArea.Invalidate(); + secondaryTextArea?.TextArea.Invalidate(); + break; + } + Document.UpdateQueue.Clear(); +// this.primaryTextArea.TextArea.Update(); +// if (this.secondaryTextArea != null) { +// this.secondaryTextArea.TextArea.Update(); +// } + } + + #endregion + + #region Printing routines + + private int curLineNr; + private float curTabIndent; + private StringFormat printingStringFormat; + + private void BeginPrint(object sender, PrintEventArgs ev) + { + curLineNr = 0; + printingStringFormat = (StringFormat)StringFormat.GenericTypographic.Clone(); + + // 100 should be enough for everyone ...err ? + var tabStops = new float[100]; + for (var i = 0; i < tabStops.Length; ++i) + tabStops[i] = TabIndent*primaryTextArea.TextArea.TextView.WideSpaceWidth; + + printingStringFormat.SetTabStops(firstTabOffset: 0, tabStops); + } + + private void Advance(ref float x, ref float y, float maxWidth, float size, float fontHeight) + { + if (x + size < maxWidth) + { + x += size; + } + else + { + x = curTabIndent; + y += fontHeight; + } + } + + // btw. I hate source code duplication ... but this time I don't care !!!! + private float MeasurePrintingHeight(Graphics g, LineSegment line, float maxWidth) + { + float xPos = 0; + float yPos = 0; + var fontHeight = Font.GetHeight(g); +// bool gotNonWhitespace = false; + curTabIndent = 0; + var fontContainer = TextEditorProperties.FontContainer; + foreach (var word in line.Words) + switch (word.Type) + { + case TextWordType.Space: + Advance(ref xPos, ref yPos, maxWidth, primaryTextArea.TextArea.TextView.SpaceWidth, fontHeight); +// if (!gotNonWhitespace) { +// curTabIndent = xPos; +// } + break; + case TextWordType.Tab: + Advance(ref xPos, ref yPos, maxWidth, TabIndent*primaryTextArea.TextArea.TextView.WideSpaceWidth, fontHeight); +// if (!gotNonWhitespace) { +// curTabIndent = xPos; +// } + break; + case TextWordType.Word: +// if (!gotNonWhitespace) { +// gotNonWhitespace = true; +// curTabIndent += TabIndent * primaryTextArea.TextArea.TextView.GetWidth(' '); +// } + var drawingSize = g.MeasureString(word.Word, word.GetFont(fontContainer), new SizeF(maxWidth, fontHeight*100), printingStringFormat); + Advance(ref xPos, ref yPos, maxWidth, drawingSize.Width, fontHeight); + break; + } + return yPos + fontHeight; + } + + private void DrawLine(Graphics g, LineSegment line, float yPos, RectangleF margin) + { + float xPos = 0; + var fontHeight = Font.GetHeight(g); +// bool gotNonWhitespace = false; + curTabIndent = 0; + + var fontContainer = TextEditorProperties.FontContainer; + foreach (var word in line.Words) + switch (word.Type) + { + case TextWordType.Space: + Advance(ref xPos, ref yPos, margin.Width, primaryTextArea.TextArea.TextView.SpaceWidth, fontHeight); +// if (!gotNonWhitespace) { +// curTabIndent = xPos; +// } + break; + case TextWordType.Tab: + Advance(ref xPos, ref yPos, margin.Width, TabIndent*primaryTextArea.TextArea.TextView.WideSpaceWidth, fontHeight); +// if (!gotNonWhitespace) { +// curTabIndent = xPos; +// } + break; + case TextWordType.Word: +// if (!gotNonWhitespace) { +// gotNonWhitespace = true; +// curTabIndent += TabIndent * primaryTextArea.TextArea.TextView.GetWidth(' '); +// } + g.DrawString(word.Word, word.GetFont(fontContainer), BrushRegistry.GetBrush(word.Color), xPos + margin.X, yPos); + var drawingSize = g.MeasureString(word.Word, word.GetFont(fontContainer), new SizeF(margin.Width, fontHeight*100), printingStringFormat); + Advance(ref xPos, ref yPos, margin.Width, drawingSize.Width, fontHeight); + break; + } + } + + private void PrintPage(object sender, PrintPageEventArgs ev) + { + var g = ev.Graphics; + float yPos = ev.MarginBounds.Top; + + while (curLineNr < Document.TotalNumberOfLines) + { + var curLine = Document.GetLineSegment(curLineNr); + if (curLine.Words != null) + { + var drawingHeight = MeasurePrintingHeight(g, curLine, ev.MarginBounds.Width); + if (drawingHeight + yPos > ev.MarginBounds.Bottom) + break; + + DrawLine(g, curLine, yPos, ev.MarginBounds); + yPos += drawingHeight; + } + + ++curLineNr; + } + + // If more lines exist, print another page. + ev.HasMorePages = curLineNr < Document.TotalNumberOfLines; + } + + #endregion + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/TextEditorControlBase.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/TextEditorControlBase.cs new file mode 100644 index 0000000..f763ad2 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/TextEditorControlBase.cs @@ -0,0 +1,765 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Design; +using System.Drawing.Text; +using System.IO; +using System.Text; +using System.Windows.Forms; +using ICSharpCode.TextEditor.Actions; +using ICSharpCode.TextEditor.Document; +using ICSharpCode.TextEditor.Util; + +namespace ICSharpCode.TextEditor +{ + /// + /// This class is used for a basic text area control + /// + [ToolboxItem(defaultType: false)] + public abstract class TextEditorControlBase : UserControl + { + private string currentFileName; + private IDocument document; + + /// + /// This hashtable contains all editor keys, where + /// the key is the key combination and the value the + /// action. + /// + protected Dictionary editactions = new Dictionary(); + + private Encoding encoding; + private int updateLevel; + + protected TextEditorControlBase() + { + GenerateDefaultActions(); + HighlightingManager.Manager.ReloadSyntaxHighlighting += OnReloadHighlighting; + } + + [Browsable(browsable: false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public ITextEditorProperties TextEditorProperties + { + get => document.TextEditorProperties; + set + { + document.TextEditorProperties = value; + OptionsChanged(); + } + } + + /// + /// Current file's character encoding + /// + [Browsable(browsable: false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public Encoding Encoding + { + get + { + if (encoding == null) + return TextEditorProperties.Encoding; + return encoding; + } + set => encoding = value; + } + + /// + /// The current file name + /// + [Browsable(browsable: false)] + [ReadOnly(isReadOnly: true)] + public string FileName + { + get => currentFileName; + set + { + if (currentFileName != value) + { + currentFileName = value; + OnFileNameChanged(EventArgs.Empty); + } + } + } + + /// + /// The current document + /// + [Browsable(browsable: false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public IDocument Document + { + get => document; + set + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + if (document != null) + document.DocumentChanged -= OnDocumentChanged; + document = value; + document.UndoStack.TextEditorControl = this; + document.DocumentChanged += OnDocumentChanged; + } + } + + [EditorBrowsable(EditorBrowsableState.Always)] + [Browsable(browsable: true)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] + [Editor("System.ComponentModel.Design.MultilineStringEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))] + public override string Text + { + get => Document.TextContent; + set => Document.TextContent = value; + } + + /// + /// If set to true the contents can't be altered. + /// + [Browsable(browsable: false)] + public bool IsReadOnly + { + get => Document.ReadOnly; + set => Document.ReadOnly = value; + } + + /// + /// true, if the textarea is updating it's status, while + /// it updates it status no redraw operation occurs. + /// + [Browsable(browsable: false)] + public bool IsInUpdate => updateLevel > 0; + + /// + /// supposedly this is the way to do it according to .NET docs, + /// as opposed to setting the size in the constructor + /// + protected override Size DefaultSize => new Size(width: 100, height: 100); + + public abstract TextAreaControl ActiveTextAreaControl { get; } + + private void OnDocumentChanged(object sender, EventArgs e) + { + OnTextChanged(e); + } + + [EditorBrowsable(EditorBrowsableState.Always)] + [Browsable(browsable: true)] + public new event EventHandler TextChanged + { + add => base.TextChanged += value; + remove => base.TextChanged -= value; + } + + private static Font ParseFont(string font) + { + var descr = font.Split(',', '='); + return new Font(descr[1], float.Parse(descr[3])); + } + + protected virtual void OnReloadHighlighting(object sender, EventArgs e) + { + if (Document.HighlightingStrategy != null) + { + try + { + Document.HighlightingStrategy = HighlightingStrategyFactory.CreateHighlightingStrategy(Document.HighlightingStrategy.Name); + } + catch (HighlightingDefinitionInvalidException ex) + { + MessageBox.Show(ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + + OptionsChanged(); + } + } + + public bool IsEditAction(Keys keyData) + { + return editactions.ContainsKey(keyData); + } + + internal IEditAction GetEditAction(Keys keyData) + { + if (!IsEditAction(keyData)) + return null; + return editactions[keyData]; + } + + private void GenerateDefaultActions() + { + editactions[Keys.Left] = new CaretLeft(); + editactions[Keys.Left | Keys.Shift] = new ShiftCaretLeft(); + editactions[Keys.Left | Keys.Control] = new WordLeft(); + editactions[Keys.Left | Keys.Control | Keys.Shift] = new ShiftWordLeft(); + editactions[Keys.Right] = new CaretRight(); + editactions[Keys.Right | Keys.Shift] = new ShiftCaretRight(); + editactions[Keys.Right | Keys.Control] = new WordRight(); + editactions[Keys.Right | Keys.Control | Keys.Shift] = new ShiftWordRight(); + editactions[Keys.Up] = new CaretUp(); + editactions[Keys.Up | Keys.Shift] = new ShiftCaretUp(); + editactions[Keys.Up | Keys.Control] = new ScrollLineUp(); + editactions[Keys.Down] = new CaretDown(); + editactions[Keys.Down | Keys.Shift] = new ShiftCaretDown(); + editactions[Keys.Down | Keys.Control] = new ScrollLineDown(); + + editactions[Keys.Insert] = new ToggleEditMode(); + editactions[Keys.Insert | Keys.Control] = new Copy(); + editactions[Keys.Insert | Keys.Shift] = new Paste(); + editactions[Keys.Delete] = new Delete(); + editactions[Keys.Delete | Keys.Shift] = new Cut(); + editactions[Keys.Home] = new Home(); + editactions[Keys.Home | Keys.Shift] = new ShiftHome(); + editactions[Keys.Home | Keys.Control] = new MoveToStart(); + editactions[Keys.Home | Keys.Control | Keys.Shift] = new ShiftMoveToStart(); + editactions[Keys.End] = new End(); + editactions[Keys.End | Keys.Shift] = new ShiftEnd(); + editactions[Keys.End | Keys.Control] = new MoveToEnd(); + editactions[Keys.End | Keys.Control | Keys.Shift] = new ShiftMoveToEnd(); + editactions[Keys.PageUp] = new MovePageUp(); + editactions[Keys.PageUp | Keys.Shift] = new ShiftMovePageUp(); + editactions[Keys.PageDown] = new MovePageDown(); + editactions[Keys.PageDown | Keys.Shift] = new ShiftMovePageDown(); + + editactions[Keys.Return] = new Return(); + editactions[Keys.Tab] = new Tab(); + editactions[Keys.Tab | Keys.Shift] = new ShiftTab(); + editactions[Keys.Back] = new Backspace(); + editactions[Keys.Back | Keys.Shift] = new Backspace(); + + editactions[Keys.X | Keys.Control] = new Cut(); + editactions[Keys.C | Keys.Control] = new Copy(); + editactions[Keys.V | Keys.Control] = new Paste(); + + editactions[Keys.A | Keys.Control] = new SelectWholeDocument(); + editactions[Keys.Escape] = new ClearAllSelections(); + + editactions[Keys.Divide | Keys.Control] = new ToggleComment(); + editactions[Keys.OemQuestion | Keys.Control] = new ToggleComment(); + + editactions[Keys.Back | Keys.Alt] = new Actions.Undo(); + editactions[Keys.Z | Keys.Control] = new Actions.Undo(); + editactions[Keys.Y | Keys.Control] = new Redo(); + + editactions[Keys.Delete | Keys.Control] = new DeleteWord(); + editactions[Keys.Back | Keys.Control] = new WordBackspace(); + editactions[Keys.D | Keys.Control] = new DeleteLine(); + editactions[Keys.D | Keys.Shift | Keys.Control] = new DeleteToLineEnd(); + + editactions[Keys.B | Keys.Control] = new GotoMatchingBrace(); + } + + /// + /// Call this method before a long update operation this + /// 'locks' the text area so that no screen update occurs. + /// + public virtual void BeginUpdate() + { + ++updateLevel; + } + + /// + /// Call this method to 'unlock' the text area. After this call + /// screen update can occur. But no automatical refresh occurs you + /// have to commit the updates in the queue. + /// + public virtual void EndUpdate() + { + Debug.Assert(updateLevel > 0); + updateLevel = Math.Max(val1: 0, updateLevel - 1); + } + + public void LoadFile(string fileName) + { + LoadFile(fileName, autoLoadHighlighting: true, autodetectEncoding: true); + } + + /// + /// Loads a file given by fileName + /// + /// The name of the file to open + /// Automatically load the highlighting for the file + /// Automatically detect file encoding and set Encoding property to the detected encoding. + public void LoadFile(string fileName, bool autoLoadHighlighting, bool autodetectEncoding) + { + using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read)) + { + LoadFile(fileName, fs, autoLoadHighlighting, autodetectEncoding); + } + } + + /// + /// Loads a file from the specified stream. + /// + /// + /// The name of the file to open. Used to find the correct highlighting strategy + /// if autoLoadHighlighting is active, and sets the filename property to this value. + /// + /// The stream to actually load the file content from. + /// Automatically load the highlighting for the file + /// Automatically detect file encoding and set Encoding property to the detected encoding. + public void LoadFile(string fileName, Stream stream, bool autoLoadHighlighting, bool autodetectEncoding) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + BeginUpdate(); + document.TextContent = string.Empty; + document.UndoStack.ClearAll(); + document.BookmarkManager.Clear(); + if (autoLoadHighlighting) + try + { + document.HighlightingStrategy = HighlightingStrategyFactory.CreateHighlightingStrategyForFile(fileName); + } + catch (HighlightingDefinitionInvalidException ex) + { + MessageBox.Show(ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + + if (autodetectEncoding) + { + var encoding = Encoding; + Document.TextContent = FileReader.ReadFileContent(stream, ref encoding); + Encoding = encoding; + } + else + { + using (var reader = new StreamReader(fileName, Encoding)) + { + Document.TextContent = reader.ReadToEnd(); + } + } + + FileName = fileName; + Document.UpdateQueue.Clear(); + EndUpdate(); + + OptionsChanged(); + Refresh(); + } + + /// + /// Gets if the document can be saved with the current encoding without losing data. + /// + public bool CanSaveWithCurrentEncoding() + { + if (encoding == null || FileReader.IsUnicode(encoding)) + return true; + // not a unicode codepage + var text = document.TextContent; + return encoding.GetString(encoding.GetBytes(text)) == text; + } + + /// + /// Saves the text editor content into the file. + /// + public void SaveFile(string fileName) + { + using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write)) + { + SaveFile(fs); + } + + FileName = fileName; + } + + /// + /// Saves the text editor content into the specified stream. + /// Does not close the stream. + /// + public void SaveFile(Stream stream) + { + var streamWriter = new StreamWriter(stream, Encoding ?? Encoding.UTF8); + + // save line per line to apply the LineTerminator to all lines + // (otherwise we might save files with mixed-up line endings) + foreach (var line in Document.LineSegmentCollection) + { + streamWriter.Write(Document.GetText(line.Offset, line.Length)); + if (line.DelimiterLength > 0) + { + var charAfterLine = Document.GetCharAt(line.Offset + line.Length); + if (charAfterLine != '\n' && charAfterLine != '\r') + throw new InvalidOperationException("The document cannot be saved because it is corrupted."); + // only save line terminator if the line has one + streamWriter.Write(document.TextEditorProperties.LineTerminator); + } + } + + streamWriter.Flush(); + } + + public abstract void OptionsChanged(); + + // Localization ISSUES + + // used in insight window + public virtual string GetRangeDescription(int selectedItem, int itemCount) + { + var sb = new StringBuilder(selectedItem.ToString()); + sb.Append(" from "); + sb.Append(itemCount.ToString()); + return sb.ToString(); + } + + /// + /// Overwritten refresh method that does nothing if the control is in + /// an update cycle. + /// + public override void Refresh() + { + if (IsInUpdate) + return; + base.Refresh(); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + HighlightingManager.Manager.ReloadSyntaxHighlighting -= OnReloadHighlighting; + document.HighlightingStrategy = null; + document.UndoStack.TextEditorControl = null; + } + + base.Dispose(disposing); + } + + protected virtual void OnFileNameChanged(EventArgs e) + { + FileNameChanged?.Invoke(this, e); + } + + public event EventHandler FileNameChanged; + + #region Document Properties + + /// + /// If true spaces are shown in the textarea + /// + [Category("Appearance")] + [DefaultValue(value: false)] + [Description("If true spaces are shown in the textarea")] + public bool ShowSpaces + { + get => document.TextEditorProperties.ShowSpaces; + set + { + document.TextEditorProperties.ShowSpaces = value; + OptionsChanged(); + } + } + + /// + /// Specifies the quality of text rendering (whether to use hinting and/or anti-aliasing). + /// + [Category("Appearance")] + [DefaultValue(TextRenderingHint.SystemDefault)] + [Description("Specifies the quality of text rendering (whether to use hinting and/or anti-aliasing).")] + public TextRenderingHint TextRenderingHint + { + get => document.TextEditorProperties.TextRenderingHint; + set + { + document.TextEditorProperties.TextRenderingHint = value; + OptionsChanged(); + } + } + + /// + /// If true tabs are shown in the textarea + /// + [Category("Appearance")] + [DefaultValue(value: false)] + [Description("If true tabs are shown in the textarea")] + public bool ShowTabs + { + get => document.TextEditorProperties.ShowTabs; + set + { + document.TextEditorProperties.ShowTabs = value; + OptionsChanged(); + } + } + + /// + /// If true EOL markers are shown in the textarea + /// + [Category("Appearance")] + [DefaultValue(value: false)] + [Description("If true EOL markers are shown in the textarea")] + public bool ShowEOLMarkers + { + get => document.TextEditorProperties.ShowEOLMarker; + set + { + document.TextEditorProperties.ShowEOLMarker = value; + OptionsChanged(); + } + } + + /// + /// If true the horizontal ruler is shown in the textarea + /// + [Category("Appearance")] + [DefaultValue(value: false)] + [Description("If true the horizontal ruler is shown in the textarea")] + public bool ShowHRuler + { + get => document.TextEditorProperties.ShowHorizontalRuler; + set + { + document.TextEditorProperties.ShowHorizontalRuler = value; + OptionsChanged(); + } + } + + /// + /// If true the vertical ruler is shown in the textarea + /// + [Category("Appearance")] + [DefaultValue(value: true)] + [Description("If true the vertical ruler is shown in the textarea")] + public bool ShowVRuler + { + get => document.TextEditorProperties.ShowVerticalRuler; + set + { + document.TextEditorProperties.ShowVerticalRuler = value; + OptionsChanged(); + } + } + + /// + /// The row in which the vertical ruler is displayed + /// + [Category("Appearance")] + [DefaultValue(value: 80)] + [Description("The row in which the vertical ruler is displayed")] + public int VRulerRow + { + get => document.TextEditorProperties.VerticalRulerRow; + set + { + document.TextEditorProperties.VerticalRulerRow = value; + OptionsChanged(); + } + } + + /// + /// If true line numbers are shown in the textarea + /// + [Category("Appearance")] + [DefaultValue(value: true)] + [Description("If true line numbers are shown in the textarea")] + public bool ShowLineNumbers + { + get => document.TextEditorProperties.ShowLineNumbers; + set + { + document.TextEditorProperties.ShowLineNumbers = value; + OptionsChanged(); + } + } + + /// + /// If true invalid lines are marked in the textarea + /// + [Category("Appearance")] + [DefaultValue(value: false)] + [Description("If true invalid lines are marked in the textarea")] + public bool ShowInvalidLines + { + get => document.TextEditorProperties.ShowInvalidLines; + set + { + document.TextEditorProperties.ShowInvalidLines = value; + OptionsChanged(); + } + } + + /// + /// If true folding is enabled in the textarea + /// + [Category("Appearance")] + [DefaultValue(value: true)] + [Description("If true folding is enabled in the textarea")] + public bool EnableFolding + { + get => document.TextEditorProperties.EnableFolding; + set + { + document.TextEditorProperties.EnableFolding = value; + OptionsChanged(); + } + } + + [Category("Appearance")] + [DefaultValue(value: true)] + [Description("If true matching brackets are highlighted")] + public bool ShowMatchingBracket + { + get => document.TextEditorProperties.ShowMatchingBracket; + set + { + document.TextEditorProperties.ShowMatchingBracket = value; + OptionsChanged(); + } + } + + [Category("Appearance")] + [DefaultValue(value: false)] + [Description("If true the icon bar is displayed")] + public bool IsIconBarVisible + { + get => document.TextEditorProperties.IsIconBarVisible; + set + { + document.TextEditorProperties.IsIconBarVisible = value; + OptionsChanged(); + } + } + + /// + /// The width in spaces of a tab character + /// + [Category("Appearance")] + [DefaultValue(value: 4)] + [Description("The width in spaces of a tab character")] + public int TabIndent + { + get => document.TextEditorProperties.TabIndent; + set + { + document.TextEditorProperties.TabIndent = value; + OptionsChanged(); + } + } + + /// + /// The line viewer style + /// + [Category("Appearance")] + [DefaultValue(LineViewerStyle.None)] + [Description("The line viewer style")] + public LineViewerStyle LineViewerStyle + { + get => document.TextEditorProperties.LineViewerStyle; + set + { + document.TextEditorProperties.LineViewerStyle = value; + OptionsChanged(); + } + } + + /// + /// The indent style + /// + [Category("Behavior")] + [DefaultValue(IndentStyle.Smart)] + [Description("The indent style")] + public IndentStyle IndentStyle + { + get => document.TextEditorProperties.IndentStyle; + set + { + document.TextEditorProperties.IndentStyle = value; + OptionsChanged(); + } + } + + /// + /// if true spaces are converted to tabs + /// + [Category("Behavior")] + [DefaultValue(value: false)] + [Description("Converts tabs to spaces while typing")] + public bool ConvertTabsToSpaces + { + get => document.TextEditorProperties.ConvertTabsToSpaces; + set + { + document.TextEditorProperties.ConvertTabsToSpaces = value; + OptionsChanged(); + } + } + + /// + /// if true spaces are converted to tabs + /// + [Category("Behavior")] + [DefaultValue(value: false)] + [Description("Hide the mouse cursor while typing")] + public bool HideMouseCursor + { + get => document.TextEditorProperties.HideMouseCursor; + set + { + document.TextEditorProperties.HideMouseCursor = value; + OptionsChanged(); + } + } + + /// + /// if true spaces are converted to tabs + /// + [Category("Behavior")] + [DefaultValue(value: false)] + [Description("Allows the caret to be placed beyond the end of line")] + public bool AllowCaretBeyondEOL + { + get => document.TextEditorProperties.AllowCaretBeyondEOL; + set + { + document.TextEditorProperties.AllowCaretBeyondEOL = value; + OptionsChanged(); + } + } + + /// + /// if true spaces are converted to tabs + /// + [Category("Behavior")] + [DefaultValue(BracketMatchingStyle.After)] + [Description("Specifies if the bracket matching should match the bracket before or after the caret.")] + public BracketMatchingStyle BracketMatchingStyle + { + get => document.TextEditorProperties.BracketMatchingStyle; + set + { + document.TextEditorProperties.BracketMatchingStyle = value; + OptionsChanged(); + } + } + + /// + /// The base font of the text area. No bold or italic fonts + /// can be used because bold/italic is reserved for highlighting + /// purposes. + /// + [Browsable(browsable: true)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] + [Description("The base font of the text area. No bold or italic fonts can be used because bold/italic is reserved for highlighting purposes.")] + public override Font Font + { + get => document.TextEditorProperties.Font; + set + { + document.TextEditorProperties.Font = value; + OptionsChanged(); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/TextView.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/TextView.cs new file mode 100644 index 0000000..1b4a345 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/TextView.cs @@ -0,0 +1,1095 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Windows.Forms; +using ICSharpCode.TextEditor.Document; + +namespace ICSharpCode.TextEditor +{ + /// + /// This class paints the textarea. + /// + public class TextView : AbstractMargin, IDisposable + { + private Font lastFont; + + private int physicalColumn; // used for calculating physical column during paint + + public TextView(TextArea textArea) : base(textArea) + { + Cursor = Cursors.IBeam; + OptionsChanged(); + } + + public Highlight Highlight { get; set; } + + public int FirstPhysicalLine => textArea.VirtualTop.Y/FontHeight; + + public int LineHeightRemainder => textArea.VirtualTop.Y%FontHeight; + + /// Gets the first visible logical line. + public int FirstVisibleLine + { + get => textArea.Document.GetFirstLogicalLine(textArea.VirtualTop.Y/FontHeight); + set + { + if (FirstVisibleLine != value) + textArea.VirtualTop = new Point(textArea.VirtualTop.X, textArea.Document.GetVisibleLine(value)*FontHeight); + } + } + + public int VisibleLineDrawingRemainder => textArea.VirtualTop.Y%FontHeight; + + public int FontHeight { get; private set; } + + public int VisibleLineCount => 1 + DrawingPosition.Height/FontHeight; + + public int VisibleColumnCount => DrawingPosition.Width/WideSpaceWidth - 1; + + /// + /// Gets the width of a space character. + /// This value can be quite small in some fonts - consider using WideSpaceWidth instead. + /// + public int SpaceWidth { get; private set; } + + /// + /// Gets the width of a 'wide space' (=one quarter of a tab, if tab is set to 4 spaces). + /// On monospaced fonts, this is the same value as spaceWidth. + /// + public int WideSpaceWidth { get; private set; } + + public void Dispose() + { + measureCache.Clear(); + } + + private static int GetFontHeight(Font font) + { + var max = Math.Max( + TextRenderer.MeasureText("_", font).Height, + (int)Math.Ceiling(font.GetHeight())); + + return max + 1; + } + + public void OptionsChanged() + { + lastFont = TextEditorProperties.FontContainer.RegularFont; + FontHeight = GetFontHeight(lastFont); + // use minimum width - in some fonts, space has no width but kerning is used instead + // -> DivideByZeroException + SpaceWidth = Math.Max(GetWidth(ch: ' ', lastFont), val2: 1); + // tab should have the width of 4*'x' + WideSpaceWidth = Math.Max(SpaceWidth, GetWidth(ch: 'x', lastFont)); + } + + #region Paint functions + + public override void Paint(Graphics g, Rectangle rect) + { + if (rect.Width <= 0 || rect.Height <= 0) + return; + + // Just to ensure that fontHeight and char widths are always correct... + if (lastFont != TextEditorProperties.FontContainer.RegularFont) + { + OptionsChanged(); + textArea.Invalidate(); + } + + var horizontalDelta = textArea.VirtualTop.X; + if (horizontalDelta > 0) + g.SetClip(DrawingPosition); + + for (var y = 0; y < (DrawingPosition.Height + VisibleLineDrawingRemainder)/FontHeight + 1; ++y) + { + var lineRectangle = new Rectangle( + DrawingPosition.X - horizontalDelta, + DrawingPosition.Top + y*FontHeight - VisibleLineDrawingRemainder, + DrawingPosition.Width + horizontalDelta, + FontHeight); + + if (rect.IntersectsWith(lineRectangle)) + { +// var fvl = textArea.Document.GetVisibleLine(FirstVisibleLine); + var currentLine = textArea.Document.GetFirstLogicalLine(textArea.Document.GetVisibleLine(FirstVisibleLine) + y); + PaintDocumentLine(g, currentLine, lineRectangle); + } + } + + DrawMarkerDraw(g); + + if (horizontalDelta > 0) + g.ResetClip(); + textArea.Caret.PaintCaret(g); + } + + private void PaintDocumentLine(Graphics g, int lineNumber, Rectangle lineRectangle) + { + Debug.Assert(lineNumber >= 0); + var bgColorBrush = GetBgColorBrush(lineNumber); + var backgroundBrush = textArea.Enabled ? bgColorBrush : SystemBrushes.InactiveBorder; + + if (lineNumber >= textArea.Document.TotalNumberOfLines) + { + g.FillRectangle(backgroundBrush, lineRectangle); + if (TextEditorProperties.ShowInvalidLines) + DrawInvalidLineMarker(g, lineRectangle.Left, lineRectangle.Top); + if (TextEditorProperties.ShowVerticalRuler) + DrawVerticalRuler(g, lineRectangle); + return; + } + + var physicalXPos = lineRectangle.X; + // there can't be a folding which starts in an above line and ends here, because the line is a new one, + // there must be a return before this line. + var column = 0; + physicalColumn = 0; + if (TextEditorProperties.EnableFolding) + while (true) + { + var starts = textArea.Document.FoldingManager.GetFoldedFoldingsWithStartAfterColumn(lineNumber, column - 1); + if (starts == null || starts.Count <= 0) + { + if (lineNumber < textArea.Document.TotalNumberOfLines) + physicalXPos = PaintLinePart(g, lineNumber, column, textArea.Document.GetLineSegment(lineNumber).Length, lineRectangle, physicalXPos); + break; + } + + // search the first starting folding + var firstFolding = starts[index: 0]; + foreach (var fm in starts) + if (fm.StartColumn < firstFolding.StartColumn) + firstFolding = fm; + starts.Clear(); + + physicalXPos = PaintLinePart(g, lineNumber, column, firstFolding.StartColumn, lineRectangle, physicalXPos); + column = firstFolding.EndColumn; + lineNumber = firstFolding.EndLine; + if (lineNumber >= textArea.Document.TotalNumberOfLines) + { + Debug.Assert(condition: false, "Folding ends after document end"); + break; + } + + var selectionRange2 = textArea.SelectionManager.GetSelectionAtLine(lineNumber); + var drawSelected = ColumnRange.WholeColumn.Equals(selectionRange2) || firstFolding.StartColumn >= selectionRange2.StartColumn && firstFolding.EndColumn <= selectionRange2.EndColumn; + + physicalXPos = PaintFoldingText(g, lineNumber, physicalXPos, lineRectangle, firstFolding.FoldText, drawSelected); + } + else + physicalXPos = PaintLinePart(g, lineNumber, startColumn: 0, textArea.Document.GetLineSegment(lineNumber).Length, lineRectangle, physicalXPos); + + if (lineNumber < textArea.Document.TotalNumberOfLines) + { + // Paint things after end of line + var selectionRange = textArea.SelectionManager.GetSelectionAtLine(lineNumber); + var currentLine = textArea.Document.GetLineSegment(lineNumber); + var selectionColor = textArea.Document.HighlightingStrategy.GetColorFor("Selection"); + + var selectionBeyondEOL = selectionRange.EndColumn > currentLine.Length || ColumnRange.WholeColumn.Equals(selectionRange); + + if (TextEditorProperties.ShowEOLMarker) + { + var eolMarkerColor = textArea.Document.HighlightingStrategy.GetColorFor("EOLMarkers"); + physicalXPos += DrawEOLMarker(g, eolMarkerColor.Color, selectionBeyondEOL ? bgColorBrush : backgroundBrush, physicalXPos, lineRectangle.Y); + } + else + { + if (selectionBeyondEOL) + { + g.FillRectangle(BrushRegistry.GetBrush(selectionColor.BackgroundColor), new RectangleF(physicalXPos, lineRectangle.Y, WideSpaceWidth, lineRectangle.Height)); + physicalXPos += WideSpaceWidth; + } + } + + var fillBrush = selectionBeyondEOL && TextEditorProperties.AllowCaretBeyondEOL ? bgColorBrush : backgroundBrush; + g.FillRectangle( + fillBrush, + new RectangleF(physicalXPos, lineRectangle.Y, lineRectangle.Width - physicalXPos + lineRectangle.X, lineRectangle.Height)); + } + + if (TextEditorProperties.ShowVerticalRuler) + DrawVerticalRuler(g, lineRectangle); +// bgColorBrush.Dispose(); + } + + private bool DrawLineMarkerAtLine(int lineNumber) + { + return lineNumber == textArea.Caret.Line && textArea.MotherTextAreaControl.TextEditorProperties.LineViewerStyle == LineViewerStyle.FullRow; + } + + private Brush GetBgColorBrush(int lineNumber) + { + if (DrawLineMarkerAtLine(lineNumber)) + { + var caretLine = textArea.Document.HighlightingStrategy.GetColorFor("CaretMarker"); + return BrushRegistry.GetBrush(caretLine.Color); + } + + var background = textArea.Document.HighlightingStrategy.GetColorFor("Default"); + var bgColor = background.BackgroundColor; + return BrushRegistry.GetBrush(bgColor); + } + + private const int additionalFoldTextSize = 1; + + private int PaintFoldingText(Graphics g, int lineNumber, int physicalXPos, Rectangle lineRectangle, string text, bool drawSelected) + { + // TODO: get font and color from the highlighting file + var selectionColor = textArea.Document.HighlightingStrategy.GetColorFor("Selection"); + var bgColorBrush = drawSelected ? BrushRegistry.GetBrush(selectionColor.BackgroundColor) : GetBgColorBrush(lineNumber); + var backgroundBrush = textArea.Enabled ? bgColorBrush : SystemBrushes.InactiveBorder; + + var font = textArea.TextEditorProperties.FontContainer.RegularFont; + + var wordWidth = MeasureStringWidth(g, text, font) + additionalFoldTextSize; + var rect = new Rectangle(physicalXPos, lineRectangle.Y, wordWidth, lineRectangle.Height - 1); + + g.FillRectangle(backgroundBrush, rect); + + physicalColumn += text.Length; + DrawString( + g, + text, + font, + drawSelected ? selectionColor.Color : Color.Gray, + rect.X + 1, rect.Y); + g.DrawRectangle(BrushRegistry.GetPen(drawSelected ? Color.DarkGray : Color.Gray), rect.X, rect.Y, rect.Width, rect.Height); + + return physicalXPos + wordWidth + 1; + } + + private struct MarkerToDraw + { + internal readonly TextMarker marker; + internal readonly RectangleF drawingRect; + + public MarkerToDraw(TextMarker marker, RectangleF drawingRect) + { + this.marker = marker; + this.drawingRect = drawingRect; + } + } + + private readonly List markersToDraw = new List(); + + private void DrawMarker(TextMarker marker, RectangleF drawingRect) + { + // draw markers later so they can overdraw the following text + markersToDraw.Add(new MarkerToDraw(marker, drawingRect)); + } + + private void DrawMarkerDraw(Graphics g) + { + foreach (var m in markersToDraw) + { + var marker = m.marker; + var drawingRect = m.drawingRect; + var drawYPos = drawingRect.Bottom - 1; + switch (marker.TextMarkerType) + { + case TextMarkerType.Underlined: + g.DrawLine(BrushRegistry.GetPen(marker.Color), drawingRect.X, drawYPos, drawingRect.Right, drawYPos); + break; + case TextMarkerType.WaveLine: + var reminder = (int)drawingRect.X%6; + for (float i = (int)drawingRect.X - reminder; i < drawingRect.Right; i += 6) + { + g.DrawLine(BrushRegistry.GetPen(marker.Color), i, drawYPos + 3 - 4, i + 3, drawYPos + 1 - 4); + if (i + 3 < drawingRect.Right) + g.DrawLine(BrushRegistry.GetPen(marker.Color), i + 3, drawYPos + 1 - 4, i + 6, drawYPos + 3 - 4); + } + + break; + case TextMarkerType.SolidBlock: + g.FillRectangle(BrushRegistry.GetBrush(marker.Color), drawingRect); + break; + } + } + + markersToDraw.Clear(); + } + + /// + /// Get the marker brush (for solid block markers) at a given position. + /// + /// The offset. + /// The length. + /// All markers that have been found. + /// The Brush or null when no marker was found. + private static Brush GetMarkerBrush(IList markers, ref Color foreColor) + { + foreach (var marker in markers) + if (marker.TextMarkerType == TextMarkerType.SolidBlock) + { + if (marker.OverrideForeColor) + foreColor = marker.ForeColor; + return BrushRegistry.GetBrush(marker.Color); + } + + return null; + } + + private int PaintLinePart(Graphics g, int lineNumber, int startColumn, int endColumn, Rectangle lineRectangle, int physicalXPos) + { + var drawLineMarker = DrawLineMarkerAtLine(lineNumber); + var backgroundBrush = textArea.Enabled ? GetBgColorBrush(lineNumber) : SystemBrushes.InactiveBorder; + + var selectionColor = textArea.Document.HighlightingStrategy.GetColorFor("Selection"); + var selectionRange = textArea.SelectionManager.GetSelectionAtLine(lineNumber); + var tabMarkerColor = textArea.Document.HighlightingStrategy.GetColorFor("TabMarkers"); + var spaceMarkerColor = textArea.Document.HighlightingStrategy.GetColorFor("SpaceMarkers"); + + var currentLine = textArea.Document.GetLineSegment(lineNumber); + + var selectionBackgroundBrush = BrushRegistry.GetBrush(selectionColor.BackgroundColor); + + if (currentLine.Words == null) + return physicalXPos; + + var currentWordOffset = 0; // we cannot use currentWord.Offset because it is not set on space words + + TextWord nextCurrentWord = null; + var fontContainer = TextEditorProperties.FontContainer; + for (var wordIdx = 0; wordIdx < currentLine.Words.Count; wordIdx++) + { + var currentWord = currentLine.Words[wordIdx]; + if (currentWordOffset < startColumn) + { + // TODO: maybe we need to split at startColumn when we support fold markers + // inside words + currentWordOffset += currentWord.Length; + continue; + } + + repeatDrawCurrentWord: + //physicalXPos += 10; // leave room between drawn words - useful for debugging the drawing code + if (currentWordOffset >= endColumn || physicalXPos >= lineRectangle.Right) + break; + var currentWordEndOffset = currentWordOffset + currentWord.Length - 1; + var currentWordType = currentWord.Type; + + Color wordForeColor; + if (currentWordType == TextWordType.Space) + wordForeColor = spaceMarkerColor.Color; + else if (currentWordType == TextWordType.Tab) + wordForeColor = tabMarkerColor.Color; + else + wordForeColor = currentWord.Color; + + IList markers = Document.MarkerStrategy.GetMarkers(currentLine.Offset + currentWordOffset, currentWord.Length); + var wordBackBrush = GetMarkerBrush(markers, ref wordForeColor); + + // It is possible that we have to split the current word because a marker/the selection begins/ends inside it + if (currentWord.Length > 1) + { + var splitPos = int.MaxValue; + if (Highlight != null) + { + // split both before and after highlight + if (Highlight.OpenBrace.Y == lineNumber) + if (Highlight.OpenBrace.X >= currentWordOffset && Highlight.OpenBrace.X <= currentWordEndOffset) + splitPos = Math.Min(splitPos, Highlight.OpenBrace.X - currentWordOffset); + if (Highlight.CloseBrace.Y == lineNumber) + if (Highlight.CloseBrace.X >= currentWordOffset && Highlight.CloseBrace.X <= currentWordEndOffset) + splitPos = Math.Min(splitPos, Highlight.CloseBrace.X - currentWordOffset); + if (splitPos == 0) + splitPos = 1; // split after highlight + } + + if (endColumn < currentWordEndOffset) + splitPos = Math.Min(splitPos, endColumn - currentWordOffset); + if (selectionRange.StartColumn > currentWordOffset && selectionRange.StartColumn <= currentWordEndOffset) + splitPos = Math.Min(splitPos, selectionRange.StartColumn - currentWordOffset); + else if (selectionRange.EndColumn > currentWordOffset && selectionRange.EndColumn <= currentWordEndOffset) + splitPos = Math.Min(splitPos, selectionRange.EndColumn - currentWordOffset); + foreach (var marker in markers) + { + var markerColumn = marker.Offset - currentLine.Offset; + var markerEndColumn = marker.EndOffset - currentLine.Offset + 1; // make end offset exclusive + if (markerColumn > currentWordOffset && markerColumn <= currentWordEndOffset) + splitPos = Math.Min(splitPos, markerColumn - currentWordOffset); + else if (markerEndColumn > currentWordOffset && markerEndColumn <= currentWordEndOffset) + splitPos = Math.Min(splitPos, markerEndColumn - currentWordOffset); + } + + if (splitPos != int.MaxValue) + { + if (nextCurrentWord != null) + throw new ApplicationException("split part invalid: first part cannot be splitted further"); + nextCurrentWord = TextWord.Split(ref currentWord, splitPos); + goto repeatDrawCurrentWord; // get markers for first word part + } + } + + // get colors from selection status: + if (ColumnRange.WholeColumn.Equals(selectionRange) || selectionRange.StartColumn <= currentWordOffset + && selectionRange.EndColumn > currentWordEndOffset) + { + // word is completely selected + wordBackBrush = selectionBackgroundBrush; + if (selectionColor.HasForeground) + wordForeColor = selectionColor.Color; + } + else if (drawLineMarker) + { + wordBackBrush = backgroundBrush; + } + + if (wordBackBrush == null) + { + // use default background if no other background is set + if (currentWord.SyntaxColor != null && currentWord.SyntaxColor.HasBackground) + wordBackBrush = BrushRegistry.GetBrush(currentWord.SyntaxColor.BackgroundColor); + else + wordBackBrush = backgroundBrush; + } + + RectangleF wordRectangle; + + if (currentWord.Type == TextWordType.Space) + { + ++physicalColumn; + + wordRectangle = new RectangleF(physicalXPos, lineRectangle.Y, SpaceWidth, lineRectangle.Height); + g.FillRectangle(wordBackBrush, wordRectangle); + + if (TextEditorProperties.ShowSpaces) + DrawSpaceMarker(g, wordForeColor, physicalXPos, lineRectangle.Y); + physicalXPos += SpaceWidth; + } + else if (currentWord.Type == TextWordType.Tab) + { + physicalColumn += TextEditorProperties.TabIndent; + physicalColumn = physicalColumn/TextEditorProperties.TabIndent*TextEditorProperties.TabIndent; + // go to next tabstop + var physicalTabEnd = (physicalXPos + MinTabWidth - lineRectangle.X) + /WideSpaceWidth/TextEditorProperties.TabIndent + *WideSpaceWidth*TextEditorProperties.TabIndent + lineRectangle.X; + physicalTabEnd += WideSpaceWidth*TextEditorProperties.TabIndent; + + wordRectangle = new RectangleF(physicalXPos, lineRectangle.Y, physicalTabEnd - physicalXPos, lineRectangle.Height); + g.FillRectangle(wordBackBrush, wordRectangle); + + if (TextEditorProperties.ShowTabs) + DrawTabMarker(g, wordForeColor, physicalXPos, lineRectangle.Y); + physicalXPos = physicalTabEnd; + } + else + { + var wordWidth = DrawDocumentWord( + g, + currentWord.Word, + new Point(physicalXPos, lineRectangle.Y), + currentWord.GetFont(fontContainer), + wordForeColor, + wordBackBrush); + wordRectangle = new RectangleF(physicalXPos, lineRectangle.Y, wordWidth, lineRectangle.Height); + physicalXPos += wordWidth; + } + + foreach (var marker in markers) + if (marker.TextMarkerType != TextMarkerType.SolidBlock) + DrawMarker(marker, wordRectangle); + + // draw bracket highlight + if (Highlight != null) + if (Highlight.OpenBrace.Y == lineNumber && Highlight.OpenBrace.X == currentWordOffset || + Highlight.CloseBrace.Y == lineNumber && Highlight.CloseBrace.X == currentWordOffset) + DrawBracketHighlight(g, new Rectangle((int)wordRectangle.X, lineRectangle.Y, (int)wordRectangle.Width - 1, lineRectangle.Height - 1)); + + currentWordOffset += currentWord.Length; + if (nextCurrentWord != null) + { + currentWord = nextCurrentWord; + nextCurrentWord = null; + goto repeatDrawCurrentWord; + } + } + + if (physicalXPos < lineRectangle.Right && endColumn >= currentLine.Length) + { + // draw markers at line end + IList markers = Document.MarkerStrategy.GetMarkers(currentLine.Offset + currentLine.Length); + foreach (var marker in markers) + if (marker.TextMarkerType != TextMarkerType.SolidBlock) + DrawMarker(marker, new RectangleF(physicalXPos, lineRectangle.Y, WideSpaceWidth, lineRectangle.Height)); + } + + return physicalXPos; + } + + private int DrawDocumentWord(Graphics g, string word, Point position, Font font, Color foreColor, Brush backBrush) + { + if (string.IsNullOrEmpty(word)) + return 0; + + if (word.Length > MaximumWordLength) + { + var width = 0; + for (var i = 0; i < word.Length; i += MaximumWordLength) + { + var pos = position; + pos.X += width; + if (i + MaximumWordLength < word.Length) + width += DrawDocumentWord(g, word.Substring(i, MaximumWordLength), pos, font, foreColor, backBrush); + else + width += DrawDocumentWord(g, word.Substring(i, word.Length - i), pos, font, foreColor, backBrush); + } + + return width; + } + + var wordWidth = MeasureStringWidth(g, word, font); + + //num = ++num % 3; + g.FillRectangle( + backBrush, //num == 0 ? Brushes.LightBlue : num == 1 ? Brushes.LightGreen : Brushes.Yellow, + new RectangleF(position.X, position.Y, wordWidth + 1, FontHeight)); + + DrawString( + g, + word, + font, + foreColor, + position.X, + position.Y); + return wordWidth; + } + + private struct WordFontPair + { + private readonly string word; + private readonly Font font; + + public WordFontPair(string word, Font font) + { + this.word = word; + this.font = font; + } + + public override bool Equals(object obj) + { + var myWordFontPair = (WordFontPair)obj; + if (!word.Equals(myWordFontPair.word)) return false; + return font.Equals(myWordFontPair.font); + } + + public override int GetHashCode() + { + return word.GetHashCode() ^ font.GetHashCode(); + } + } + + private readonly Dictionary measureCache = new Dictionary(); + + // split words after 1000 characters. Fixes GDI+ crash on very longs words, for example + // a 100 KB Base64-file without any line breaks. + private const int MaximumWordLength = 1000; + private const int MaximumCacheSize = 2000; + + private int MeasureStringWidth(Graphics g, string word, Font font) + { + if (string.IsNullOrEmpty(word)) + return 0; + int width; + if (word.Length > MaximumWordLength) + { + width = 0; + for (var i = 0; i < word.Length; i += MaximumWordLength) + if (i + MaximumWordLength < word.Length) + width += MeasureStringWidth(g, word.Substring(i, MaximumWordLength), font); + else + width += MeasureStringWidth(g, word.Substring(i, word.Length - i), font); + return width; + } + + if (measureCache.TryGetValue(new WordFontPair(word, font), out width)) + return width; + if (measureCache.Count > MaximumCacheSize) + measureCache.Clear(); + + // This code here provides better results than MeasureString! + // Example line that is measured wrong: + // txt.GetPositionFromCharIndex(txt.SelectionStart) + // (Verdana 10, highlighting makes GetP... bold) -> note the space between 'x' and '(' + // this also fixes "jumping" characters when selecting in non-monospace fonts + // [...] + // Replaced GDI+ measurement with GDI measurement: faster and even more exact + width = TextRenderer.MeasureText(g, word, font, new Size(short.MaxValue, short.MaxValue), textFormatFlags).Width; + measureCache.Add(new WordFontPair(word, font), width); + return width; + } + + // Important: Some flags combinations work on WinXP, but not on Win2000. + // Make sure to test changes here on all operating systems. + private const TextFormatFlags textFormatFlags = + TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix | TextFormatFlags.PreserveGraphicsClipping; + + #endregion + + #region Conversion Functions + + private readonly Dictionary> fontBoundCharWidth = new Dictionary>(); + + public int GetWidth(char ch, Font font) + { + if (!fontBoundCharWidth.ContainsKey(font)) + fontBoundCharWidth.Add(font, new Dictionary()); + if (!fontBoundCharWidth[font].ContainsKey(ch)) + using (var g = textArea.CreateGraphics()) + { + return GetWidth(g, ch, font); + } + + return fontBoundCharWidth[font][ch]; + } + + public int GetWidth(Graphics g, char ch, Font font) + { + if (!fontBoundCharWidth.ContainsKey(font)) + fontBoundCharWidth.Add(font, new Dictionary()); + if (!fontBoundCharWidth[font].ContainsKey(ch)) + fontBoundCharWidth[font].Add(ch, MeasureStringWidth(g, ch.ToString(), font)); + return fontBoundCharWidth[font][ch]; + } + + public int GetVisualColumn(int logicalLine, int logicalColumn) + { + var column = 0; + using (var g = textArea.CreateGraphics()) + { + CountColumns(ref column, start: 0, logicalColumn, logicalLine, g); + } + + return column; + } + + public int GetVisualColumnFast(LineSegment line, int logicalColumn) + { + var lineOffset = line.Offset; + var tabIndent = Document.TextEditorProperties.TabIndent; + var guessedColumn = 0; + for (var i = 0; i < logicalColumn; ++i) + { + char ch; + if (i >= line.Length) + ch = ' '; + else + ch = Document.GetCharAt(lineOffset + i); + switch (ch) + { + case '\t': + guessedColumn += tabIndent; + guessedColumn = guessedColumn/tabIndent*tabIndent; + break; + default: + ++guessedColumn; + break; + } + } + + return guessedColumn; + } + + /// + /// returns line/column for a visual point position + /// + public TextLocation GetLogicalPosition(Point mousePosition) + { + return GetLogicalColumn(GetLogicalLine(mousePosition.Y), mousePosition.X, out var dummy); + } + + /// + /// returns line/column for a visual point position + /// + public TextLocation GetLogicalPosition(int visualPosX, int visualPosY) + { + return GetLogicalColumn(GetLogicalLine(visualPosY), visualPosX, out var dummy); + } + + /// + /// returns line/column for a visual point position + /// + public FoldMarker GetFoldMarkerFromPosition(int visualPosX, int visualPosY) + { + GetLogicalColumn(GetLogicalLine(visualPosY), visualPosX, out var foldMarker); + return foldMarker; + } + + /// + /// returns logical line number for a visual point + /// + public int GetLogicalLine(int visualPosY) + { + var clickedVisualLine = Math.Max(val1: 0, (visualPosY + textArea.VirtualTop.Y)/FontHeight); + return Document.GetFirstLogicalLine(clickedVisualLine); + } + + internal TextLocation GetLogicalColumn(int lineNumber, int visualPosX, out FoldMarker inFoldMarker) + { + visualPosX += textArea.VirtualTop.X; + + inFoldMarker = null; + if (lineNumber >= Document.TotalNumberOfLines) + return new TextLocation(visualPosX/WideSpaceWidth, lineNumber); + if (visualPosX <= 0) + return new TextLocation(column: 0, lineNumber); + + var start = 0; // column + var posX = 0; // visual position + + int result; + using (var g = textArea.CreateGraphics()) + { + // call GetLogicalColumnInternal to skip over text, + // then skip over fold markers + // and repeat as necessary. + // The loop terminates once the correct logical column is reached in + // GetLogicalColumnInternal or inside a fold marker. + while (true) + { + var line = Document.GetLineSegment(lineNumber); + var nextFolding = FindNextFoldedFoldingOnLineAfterColumn(lineNumber, start - 1); + var end = nextFolding?.StartColumn ?? int.MaxValue; + result = GetLogicalColumnInternal(g, line, start, end, ref posX, visualPosX); + + // break when GetLogicalColumnInternal found the result column + if (result < end) + break; + + // reached fold marker + lineNumber = nextFolding.EndLine; + start = nextFolding.EndColumn; + var newPosX = posX + 1 + MeasureStringWidth(g, nextFolding.FoldText, TextEditorProperties.FontContainer.RegularFont); + if (newPosX >= visualPosX) + { + inFoldMarker = nextFolding; + if (IsNearerToAThanB(visualPosX, posX, newPosX)) + return new TextLocation(nextFolding.StartColumn, nextFolding.StartLine); + return new TextLocation(nextFolding.EndColumn, nextFolding.EndLine); + } + + posX = newPosX; + } + } + + return new TextLocation(result, lineNumber); + } + + private int GetLogicalColumnInternal(Graphics g, LineSegment line, int start, int end, ref int drawingPos, int targetVisualPosX) + { + if (start == end) + return end; + Debug.Assert(start < end); + Debug.Assert(drawingPos < targetVisualPosX); + + var tabIndent = Document.TextEditorProperties.TabIndent; + + /*float spaceWidth = SpaceWidth; + float drawingPos = 0; + LineSegment currentLine = Document.GetLineSegment(logicalLine); + List words = currentLine.Words; + if (words == null) return 0; + int wordCount = words.Count; + int wordOffset = 0; + FontContainer fontContainer = TextEditorProperties.FontContainer; + */ + var fontContainer = TextEditorProperties.FontContainer; + + var words = line.Words; + if (words == null) return 0; + var wordOffset = 0; + for (var i = 0; i < words.Count; i++) + { + var word = words[i]; + if (wordOffset >= end) + return wordOffset; + if (wordOffset + word.Length >= start) + { + int newDrawingPos; + switch (word.Type) + { + case TextWordType.Space: + newDrawingPos = drawingPos + SpaceWidth; + if (newDrawingPos >= targetVisualPosX) + return IsNearerToAThanB(targetVisualPosX, drawingPos, newDrawingPos) ? wordOffset : wordOffset + 1; + break; + case TextWordType.Tab: + // go to next tab position + drawingPos = (drawingPos + MinTabWidth)/tabIndent/WideSpaceWidth*tabIndent*WideSpaceWidth; + newDrawingPos = drawingPos + tabIndent*WideSpaceWidth; + if (newDrawingPos >= targetVisualPosX) + return IsNearerToAThanB(targetVisualPosX, drawingPos, newDrawingPos) ? wordOffset : wordOffset + 1; + break; + case TextWordType.Word: + var wordStart = Math.Max(wordOffset, start); + var wordLength = Math.Min(wordOffset + word.Length, end) - wordStart; + var text = Document.GetText(line.Offset + wordStart, wordLength); + var font = word.GetFont(fontContainer) ?? fontContainer.RegularFont; + newDrawingPos = drawingPos + MeasureStringWidth(g, text, font); + if (newDrawingPos >= targetVisualPosX) + { + for (var j = 0; j < text.Length; j++) + { + newDrawingPos = drawingPos + MeasureStringWidth(g, text[j].ToString(), font); + if (newDrawingPos >= targetVisualPosX) + { + if (IsNearerToAThanB(targetVisualPosX, drawingPos, newDrawingPos)) + return wordStart + j; + return wordStart + j + 1; + } + + drawingPos = newDrawingPos; + } + + return wordStart + text.Length; + } + + break; + default: + throw new NotSupportedException(); + } + + drawingPos = newDrawingPos; + } + + wordOffset += word.Length; + } + + return wordOffset; + } + + private static bool IsNearerToAThanB(int num, int a, int b) + { + return Math.Abs(a - num) < Math.Abs(b - num); + } + + private FoldMarker FindNextFoldedFoldingOnLineAfterColumn(int lineNumber, int column) + { + var list = Document.FoldingManager.GetFoldedFoldingsWithStartAfterColumn(lineNumber, column); + if (list.Count != 0) + return list[index: 0]; + return null; + } + + private const int MinTabWidth = 4; + + private float CountColumns(ref int column, int start, int end, int logicalLine, Graphics g) + { + if (start > end) throw new ArgumentException("start > end"); + if (start == end) return 0; + float spaceWidth = SpaceWidth; + float drawingPos = 0; + var tabIndent = Document.TextEditorProperties.TabIndent; + var currentLine = Document.GetLineSegment(logicalLine); + var words = currentLine.Words; + if (words == null) return 0; + var wordCount = words.Count; + var wordOffset = 0; + var fontContainer = TextEditorProperties.FontContainer; + for (var i = 0; i < wordCount; i++) + { + var word = words[i]; + if (wordOffset >= end) + break; + if (wordOffset + word.Length >= start) + switch (word.Type) + { + case TextWordType.Space: + drawingPos += spaceWidth; + break; + case TextWordType.Tab: + // go to next tab position + drawingPos = (int)((drawingPos + MinTabWidth)/tabIndent/WideSpaceWidth)*tabIndent*WideSpaceWidth; + drawingPos += tabIndent*WideSpaceWidth; + break; + case TextWordType.Word: + var wordStart = Math.Max(wordOffset, start); + var wordLength = Math.Min(wordOffset + word.Length, end) - wordStart; + var text = Document.GetText(currentLine.Offset + wordStart, wordLength); + drawingPos += MeasureStringWidth(g, text, word.GetFont(fontContainer) ?? fontContainer.RegularFont); + break; + } + wordOffset += word.Length; + } + + for (var j = currentLine.Length; j < end; j++) + drawingPos += WideSpaceWidth; + // add one pixel in column calculation to account for floating point calculation errors + column += (int)((drawingPos + 1)/WideSpaceWidth); + + /* OLD Code (does not work for fonts like Verdana) + for (int j = start; j < end; ++j) { + char ch; + if (j >= line.Length) { + ch = ' '; + } else { + ch = Document.GetCharAt(line.Offset + j); + } + + switch (ch) { + case '\t': + int oldColumn = column; + column += tabIndent; + column = (column / tabIndent) * tabIndent; + drawingPos += (column - oldColumn) * spaceWidth; + break; + default: + ++column; + TextWord word = line.GetWord(j); + if (word == null || word.Font == null) { + drawingPos += GetWidth(ch, TextEditorProperties.Font); + } else { + drawingPos += GetWidth(ch, word.Font); + } + break; + } + } + //*/ + return drawingPos; + } + + public int GetDrawingXPos(int logicalLine, int logicalColumn) + { + var foldings = Document.FoldingManager.GetTopLevelFoldedFoldings(); + int i; + FoldMarker f = null; + // search the last folding that's interesting + for (i = foldings.Count - 1; i >= 0; --i) + { + f = foldings[i]; + if (f.StartLine < logicalLine || f.StartLine == logicalLine && f.StartColumn < logicalColumn) + break; + var f2 = foldings[i/2]; + if (f2.StartLine > logicalLine || f2.StartLine == logicalLine && f2.StartColumn >= logicalColumn) + i /= 2; + } + + var column = 0; +// var tabIndent = Document.TextEditorProperties.TabIndent; + float drawingPos; + var g = textArea.CreateGraphics(); + // if no folding is interesting + if (f == null || !(f.StartLine < logicalLine || f.StartLine == logicalLine && f.StartColumn < logicalColumn)) + { + drawingPos = CountColumns(ref column, start: 0, logicalColumn, logicalLine, g); + return (int)(drawingPos - textArea.VirtualTop.X); + } + + // if logicalLine/logicalColumn is in folding + if (f.EndLine > logicalLine || f.EndLine == logicalLine && f.EndColumn > logicalColumn) + { + logicalColumn = f.StartColumn; + logicalLine = f.StartLine; + --i; + } + + var lastFolding = i; + + // search backwards until a new visible line is reached + for (; i >= 0; --i) + { + f = foldings[i]; + if (f.EndLine < logicalLine) + break; + } + + var firstFolding = i + 1; + + if (lastFolding < firstFolding) + { + drawingPos = CountColumns(ref column, start: 0, logicalColumn, logicalLine, g); + return (int)(drawingPos - textArea.VirtualTop.X); + } + + var foldEnd = 0; + drawingPos = 0; + for (i = firstFolding; i <= lastFolding; ++i) + { + f = foldings[i]; + drawingPos += CountColumns(ref column, foldEnd, f.StartColumn, f.StartLine, g); + foldEnd = f.EndColumn; + column += f.FoldText.Length; + drawingPos += additionalFoldTextSize; + drawingPos += MeasureStringWidth(g, f.FoldText, TextEditorProperties.FontContainer.RegularFont); + } + + drawingPos += CountColumns(ref column, foldEnd, logicalColumn, logicalLine, g); + g.Dispose(); + return (int)(drawingPos - textArea.VirtualTop.X); + } + + #endregion + + #region DrawHelper functions + + private static void DrawBracketHighlight(Graphics g, Rectangle rect) + { + g.FillRectangle(BrushRegistry.GetBrush(Color.FromArgb(alpha: 50, red: 0, green: 0, blue: 255)), rect); + g.DrawRectangle(Pens.Blue, rect); + } + + private static void DrawString(Graphics g, string text, Font font, Color color, int x, int y) + { + TextRenderer.DrawText(g, text, font, new Point(x, y), color, textFormatFlags); + } + + private void DrawInvalidLineMarker(Graphics g, int x, int y) + { + var invalidLinesColor = textArea.Document.HighlightingStrategy.GetColorFor("InvalidLines"); + DrawString(g, "~", invalidLinesColor.GetFont(TextEditorProperties.FontContainer), invalidLinesColor.Color, x, y); + } + + private void DrawSpaceMarker(Graphics g, Color color, int x, int y) + { + var spaceMarkerColor = textArea.Document.HighlightingStrategy.GetColorFor("SpaceMarkers"); + DrawString(g, "\u00B7", spaceMarkerColor.GetFont(TextEditorProperties.FontContainer), color, x, y); + } + + private void DrawTabMarker(Graphics g, Color color, int x, int y) + { + var tabMarkerColor = textArea.Document.HighlightingStrategy.GetColorFor("TabMarkers"); + DrawString(g, "\u00BB", tabMarkerColor.GetFont(TextEditorProperties.FontContainer), color, x, y); + } + + private int DrawEOLMarker(Graphics g, Color color, Brush backBrush, int x, int y) + { + var eolMarkerColor = textArea.Document.HighlightingStrategy.GetColorFor("EOLMarkers"); + + var width = GetWidth(ch: '\u00B6', eolMarkerColor.GetFont(TextEditorProperties.FontContainer)); + g.FillRectangle( + backBrush, + new RectangleF(x, y, width, FontHeight)); + + DrawString(g, "\u00B6", eolMarkerColor.GetFont(TextEditorProperties.FontContainer), color, x, y); + return width; + } + + private void DrawVerticalRuler(Graphics g, Rectangle lineRectangle) + { + var xpos = WideSpaceWidth*TextEditorProperties.VerticalRulerRow - textArea.VirtualTop.X; + if (xpos <= 0) + return; + var vRulerColor = textArea.Document.HighlightingStrategy.GetColorFor("VRuler"); + + g.DrawLine( + BrushRegistry.GetPen(vRulerColor.Color), + drawingPosition.Left + xpos, + lineRectangle.Top, + drawingPosition.Left + xpos, + lineRectangle.Bottom); + } + + #endregion + } +} diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/ToolTipRequestEventArgs.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/ToolTipRequestEventArgs.cs new file mode 100644 index 0000000..610a7d7 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/ToolTipRequestEventArgs.cs @@ -0,0 +1,41 @@ +// +// +// +// +// $Revision$ +// + +using System.Drawing; + +namespace ICSharpCode.TextEditor +{ + public delegate void ToolTipRequestEventHandler(object sender, ToolTipRequestEventArgs e); + + public class ToolTipRequestEventArgs + { + internal string toolTipText; + + public ToolTipRequestEventArgs(Point mousePosition, TextLocation logicalPosition, bool inDocument) + { + MousePosition = mousePosition; + LogicalPosition = logicalPosition; + InDocument = inDocument; + } + + public Point MousePosition { get; } + + public TextLocation LogicalPosition { get; } + + public bool InDocument { get; } + + /// + /// Gets if some client handling the event has already shown a tool tip. + /// + public bool ToolTipShown => toolTipText != null; + + public void ShowToolTip(string text) + { + toolTipText = text; + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Undo/IUndoableOperation.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Undo/IUndoableOperation.cs new file mode 100644 index 0000000..ebd1a50 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Undo/IUndoableOperation.cs @@ -0,0 +1,26 @@ +// +// +// +// +// $Revision$ +// + +namespace ICSharpCode.TextEditor.Undo +{ + /// + /// This Interface describes a the basic Undo/Redo operation + /// all Undo Operations must implement this interface. + /// + public interface IUndoableOperation + { + /// + /// Undo the last operation + /// + void Undo(); + + /// + /// Redo the last operation + /// + void Redo(); + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Undo/UndoQueue.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Undo/UndoQueue.cs new file mode 100644 index 0000000..4106147 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Undo/UndoQueue.cs @@ -0,0 +1,49 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace ICSharpCode.TextEditor.Undo +{ + /// + /// This class stacks the last x operations from the undostack and makes + /// one undo/redo operation from it. + /// + internal sealed class UndoQueue : IUndoableOperation + { + private readonly List undolist = new List(); + + /// + /// + public UndoQueue(Stack stack, int numops) + { + if (stack == null) + throw new ArgumentNullException(nameof(stack)); + + Debug.Assert(numops > 0, "ICSharpCode.TextEditor.Undo.UndoQueue : numops should be > 0"); + if (numops > stack.Count) + numops = stack.Count; + + for (var i = 0; i < numops; ++i) + undolist.Add(stack.Pop()); + } + + public void Undo() + { + for (var i = 0; i < undolist.Count; ++i) + undolist[i].Undo(); + } + + public void Redo() + { + for (var i = undolist.Count - 1; i >= 0; --i) + undolist[i].Redo(); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Undo/UndoStack.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Undo/UndoStack.cs new file mode 100644 index 0000000..c7fbd5b --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Undo/UndoStack.cs @@ -0,0 +1,220 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.TextEditor.Undo +{ + /// + /// This class implements an undo stack + /// + public class UndoStack + { + private readonly Stack redostack = new Stack(); + private readonly Stack undostack = new Stack(); + + /// + /// Gets/Sets if changes to the document are protocolled by the undo stack. + /// Used internally to disable the undo stack temporarily while undoing an action. + /// + internal bool AcceptChanges = true; + + private int actionCountInUndoGroup; + + public TextEditorControlBase TextEditorControl = null; + + private int undoGroupDepth; + + /// + /// Gets if there are actions on the undo stack. + /// + public bool CanUndo => undostack.Count > 0; + + /// + /// Gets if there are actions on the redo stack. + /// + public bool CanRedo => redostack.Count > 0; + + /// + /// Gets the number of actions on the undo stack. + /// + public int UndoItemCount => undostack.Count; + + /// + /// Gets the number of actions on the redo stack. + /// + public int RedoItemCount => redostack.Count; + + /// + /// + public event EventHandler ActionUndone; + + /// + /// + public event EventHandler ActionRedone; + + public event OperationEventHandler OperationPushed; + + public void StartUndoGroup() + { + if (undoGroupDepth == 0) + actionCountInUndoGroup = 0; + undoGroupDepth++; + //Util.LoggingService.Debug("Open undo group (new depth=" + undoGroupDepth + ")"); + } + + public void EndUndoGroup() + { + if (undoGroupDepth == 0) + throw new InvalidOperationException("There are no open undo groups"); + undoGroupDepth--; + //Util.LoggingService.Debug("Close undo group (new depth=" + undoGroupDepth + ")"); + if (undoGroupDepth == 0 && actionCountInUndoGroup > 1) + { + var op = new UndoQueue(undostack, actionCountInUndoGroup); + undostack.Push(op); + OperationPushed?.Invoke(this, new OperationEventArgs(op)); + } + } + + public void AssertNoUndoGroupOpen() + { + if (undoGroupDepth != 0) + { + undoGroupDepth = 0; + throw new InvalidOperationException("No undo group should be open at this point"); + } + } + + /// + /// Call this method to undo the last operation on the stack + /// + public void Undo() + { + AssertNoUndoGroupOpen(); + if (undostack.Count > 0) + { + var uedit = undostack.Pop(); + redostack.Push(uedit); + uedit.Undo(); + OnActionUndone(); + } + } + + /// + /// Call this method to redo the last undone operation + /// + public void Redo() + { + AssertNoUndoGroupOpen(); + if (redostack.Count > 0) + { + var uedit = redostack.Pop(); + undostack.Push(uedit); + uedit.Redo(); + OnActionRedone(); + } + } + + /// + /// Call this method to push an UndoableOperation on the undostack, the redostack + /// will be cleared, if you use this method. + /// + public void Push(IUndoableOperation operation) + { + if (operation == null) + throw new ArgumentNullException(nameof(operation)); + + if (AcceptChanges) + { + StartUndoGroup(); + undostack.Push(operation); + actionCountInUndoGroup++; + if (TextEditorControl != null) + { + undostack.Push(new UndoableSetCaretPosition(this, TextEditorControl.ActiveTextAreaControl.Caret.Position)); + actionCountInUndoGroup++; + } + + EndUndoGroup(); + ClearRedoStack(); + } + } + + /// + /// Call this method, if you want to clear the redo stack + /// + public void ClearRedoStack() + { + redostack.Clear(); + } + + /// + /// Clears both the undo and redo stack. + /// + public void ClearAll() + { + AssertNoUndoGroupOpen(); + undostack.Clear(); + redostack.Clear(); + actionCountInUndoGroup = 0; + } + + /// + /// + protected void OnActionUndone() + { + ActionUndone?.Invoke(sender: null, e: null); + } + + /// + /// + protected void OnActionRedone() + { + ActionRedone?.Invoke(sender: null, e: null); + } + + private class UndoableSetCaretPosition : IUndoableOperation + { + private readonly TextLocation pos; + private readonly UndoStack stack; + private TextLocation redoPos; + + public UndoableSetCaretPosition(UndoStack stack, TextLocation pos) + { + this.stack = stack; + this.pos = pos; + } + + public void Undo() + { + redoPos = stack.TextEditorControl.ActiveTextAreaControl.Caret.Position; + stack.TextEditorControl.ActiveTextAreaControl.Caret.Position = pos; + stack.TextEditorControl.ActiveTextAreaControl.SelectionManager.ClearSelection(); + } + + public void Redo() + { + stack.TextEditorControl.ActiveTextAreaControl.Caret.Position = redoPos; + stack.TextEditorControl.ActiveTextAreaControl.SelectionManager.ClearSelection(); + } + } + } + + public class OperationEventArgs : EventArgs + { + public OperationEventArgs(IUndoableOperation op) + { + Operation = op; + } + + public IUndoableOperation Operation { get; } + } + + public delegate void OperationEventHandler(object sender, OperationEventArgs e); +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Undo/UndoableDelete.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Undo/UndoableDelete.cs new file mode 100644 index 0000000..0b068fa --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Undo/UndoableDelete.cs @@ -0,0 +1,71 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Diagnostics; +using ICSharpCode.TextEditor.Document; + +namespace ICSharpCode.TextEditor.Undo +{ + /// + /// This class is for the undo of Document insert operations + /// + public class UndoableDelete : IUndoableOperation + { + private readonly IDocument document; + +// int oldCaretPos; + private readonly int offset; + private readonly string text; + + /// + /// Creates a new instance of + /// + public UndoableDelete(IDocument document, int offset, string text) + { + if (document == null) + throw new ArgumentNullException(nameof(document)); + if (offset < 0 || offset > document.TextLength) + throw new ArgumentOutOfRangeException(nameof(offset)); + + Debug.Assert(text != null, "text can't be null"); +// oldCaretPos = document.Caret.Offset; + this.document = document; + this.offset = offset; + this.text = text; + } + + /// + /// Undo last operation + /// + public void Undo() + { + // we clear all selection direct, because the redraw + // is done per refresh at the end of the action +// textArea.SelectionManager.SelectionCollection.Clear(); + document.UndoStack.AcceptChanges = false; + document.Insert(offset, text); +// document.Caret.Offset = Math.Min(document.TextLength, Math.Max(0, oldCaretPos)); + document.UndoStack.AcceptChanges = true; + } + + /// + /// Redo last undone operation + /// + public void Redo() + { + // we clear all selection direct, because the redraw + // is done per refresh at the end of the action +// textArea.SelectionManager.SelectionCollection.Clear(); + + document.UndoStack.AcceptChanges = false; + document.Remove(offset, text.Length); +// document.Caret.Offset = Math.Min(document.TextLength, Math.Max(0, document.Caret.Offset)); + document.UndoStack.AcceptChanges = true; + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Undo/UndoableInsert.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Undo/UndoableInsert.cs new file mode 100644 index 0000000..d063089 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Undo/UndoableInsert.cs @@ -0,0 +1,72 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Diagnostics; +using ICSharpCode.TextEditor.Document; + +namespace ICSharpCode.TextEditor.Undo +{ + /// + /// This class is for the undo of Document insert operations + /// + public class UndoableInsert : IUndoableOperation + { + private readonly IDocument document; + +// int oldCaretPos; + private readonly int offset; + private readonly string text; + + /// + /// Creates a new instance of + /// + public UndoableInsert(IDocument document, int offset, string text) + { + if (document == null) + throw new ArgumentNullException(nameof(document)); + if (offset < 0 || offset > document.TextLength) + throw new ArgumentOutOfRangeException(nameof(offset)); + + Debug.Assert(text != null, "text can't be null"); +// oldCaretPos = document.Caret.Offset; + this.document = document; + this.offset = offset; + this.text = text; + } + + /// + /// Undo last operation + /// + public void Undo() + { + // we clear all selection direct, because the redraw + // is done per refresh at the end of the action +// document.SelectionCollection.Clear(); + + document.UndoStack.AcceptChanges = false; + document.Remove(offset, text.Length); +// document.Caret.Offset = Math.Min(document.TextLength, Math.Max(0, oldCaretPos)); + document.UndoStack.AcceptChanges = true; + } + + /// + /// Redo last undone operation + /// + public void Redo() + { + // we clear all selection direct, because the redraw + // is done per refresh at the end of the action +// document.SelectionCollection.Clear(); + + document.UndoStack.AcceptChanges = false; + document.Insert(offset, text); +// document.Caret.Offset = Math.Min(document.TextLength, Math.Max(0, document.Caret.Offset)); + document.UndoStack.AcceptChanges = true; + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Undo/UndoableReplace.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Undo/UndoableReplace.cs new file mode 100644 index 0000000..b000aa0 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Undo/UndoableReplace.cs @@ -0,0 +1,74 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Diagnostics; +using ICSharpCode.TextEditor.Document; + +namespace ICSharpCode.TextEditor.Undo +{ + /// + /// This class is for the undo of Document insert operations + /// + public class UndoableReplace : IUndoableOperation + { + private readonly IDocument document; + +// int oldCaretPos; + private readonly int offset; + private readonly string origText; + private readonly string text; + + /// + /// Creates a new instance of + /// + public UndoableReplace(IDocument document, int offset, string origText, string text) + { + if (document == null) + throw new ArgumentNullException(nameof(document)); + if (offset < 0 || offset > document.TextLength) + throw new ArgumentOutOfRangeException(nameof(offset)); + + Debug.Assert(text != null, "text can't be null"); +// oldCaretPos = document.Caret.Offset; + this.document = document; + this.offset = offset; + this.text = text; + this.origText = origText; + } + + /// + /// Undo last operation + /// + public void Undo() + { + // we clear all selection direct, because the redraw + // is done per refresh at the end of the action +// document.SelectionCollection.Clear(); + + document.UndoStack.AcceptChanges = false; + document.Replace(offset, text.Length, origText); +// document.Caret.Offset = Math.Min(document.TextLength, Math.Max(0, oldCaretPos)); + document.UndoStack.AcceptChanges = true; + } + + /// + /// Redo last undone operation + /// + public void Redo() + { + // we clear all selection direct, because the redraw + // is done per refresh at the end of the action +// document.SelectionCollection.Clear(); + + document.UndoStack.AcceptChanges = false; + document.Replace(offset, origText.Length, text); +// document.Caret.Offset = Math.Min(document.TextLength, Math.Max(0, document.Caret.Offset)); + document.UndoStack.AcceptChanges = true; + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Util/AugmentableRedBlackTree.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/AugmentableRedBlackTree.cs new file mode 100644 index 0000000..0f878cc --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/AugmentableRedBlackTree.cs @@ -0,0 +1,629 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace ICSharpCode.TextEditor.Util +{ + internal sealed class RedBlackTreeNode + { + internal bool color; + internal RedBlackTreeNode left, right, parent; + internal T val; + + internal RedBlackTreeNode(T val) + { + this.val = val; + } + + internal RedBlackTreeNode LeftMost + { + get + { + var node = this; + while (node.left != null) + node = node.left; + return node; + } + } + + internal RedBlackTreeNode RightMost + { + get + { + var node = this; + while (node.right != null) + node = node.right; + return node; + } + } + } + + internal interface IRedBlackTreeHost : IComparer + { + bool Equals(T a, T b); + + void UpdateAfterChildrenChange(RedBlackTreeNode node); + void UpdateAfterRotateLeft(RedBlackTreeNode node); + void UpdateAfterRotateRight(RedBlackTreeNode node); + } + + /// + /// Description of RedBlackTree. + /// + internal sealed class AugmentableRedBlackTree : ICollection where Host : IRedBlackTreeHost + { + private readonly Host host; + internal RedBlackTreeNode root; + + public AugmentableRedBlackTree(Host host) + { + if (host == null) throw new ArgumentNullException(nameof(host)); + this.host = host; + } + + public int Count { get; private set; } + + public void Clear() + { + root = null; + Count = 0; + } + + #region Debugging code + +#if DEBUG + /// + /// Check tree for consistency and being balanced. + /// + [Conditional("DATACONSISTENCYTEST")] + private void CheckProperties() + { + var blackCount = -1; + CheckNodeProperties(root, parentNode: null, RED, blackCount: 0, ref blackCount); + + var nodeCount = 0; + foreach (var _ in this) + nodeCount++; + Debug.Assert(Count == nodeCount); + } + + /* + 1. A node is either red or black. + 2. The root is black. + 3. All leaves are black. (The leaves are the NIL children.) + 4. Both children of every red node are black. (So every red node must have a black parent.) + 5. Every simple path from a node to a descendant leaf contains the same number of black nodes. (Not counting the leaf node.) + */ + private static void CheckNodeProperties(RedBlackTreeNode node, RedBlackTreeNode parentNode, bool parentColor, int blackCount, ref int expectedBlackCount) + { + if (node == null) return; + + Debug.Assert(node.parent == parentNode); + + if (parentColor == RED) + Debug.Assert(node.color == BLACK); + if (node.color == BLACK) + blackCount++; + if (node.left == null && node.right == null) + { + // node is a leaf node: + if (expectedBlackCount == -1) + expectedBlackCount = blackCount; + else + Debug.Assert(expectedBlackCount == blackCount); + } + + CheckNodeProperties(node.left, node, node.color, blackCount, ref expectedBlackCount); + CheckNodeProperties(node.right, node, node.color, blackCount, ref expectedBlackCount); + } + + public string GetTreeAsString() + { + var b = new StringBuilder(); + AppendTreeToString(root, b, indent: 0); + return b.ToString(); + } + + private static void AppendTreeToString(RedBlackTreeNode node, StringBuilder b, int indent) + { + if (node.color == RED) + b.Append("RED "); + else + b.Append("BLACK "); + b.AppendLine(node.val.ToString()); + indent += 2; + if (node.left != null) + { + b.Append(value: ' ', indent); + b.Append("L: "); + AppendTreeToString(node.left, b, indent); + } + + if (node.right != null) + { + b.Append(value: ' ', indent); + b.Append("R: "); + AppendTreeToString(node.right, b, indent); + } + } +#endif + + #endregion + + #region Add + + public void Add(T item) + { + AddInternal(new RedBlackTreeNode(item)); +#if DEBUG + CheckProperties(); +#endif + } + + private void AddInternal(RedBlackTreeNode newNode) + { + Debug.Assert(newNode.color == BLACK); + if (root == null) + { + Count = 1; + root = newNode; + return; + } + + // Insert into the tree + var parentNode = root; + while (true) + if (host.Compare(newNode.val, parentNode.val) <= 0) + { + if (parentNode.left == null) + { + InsertAsLeft(parentNode, newNode); + return; + } + + parentNode = parentNode.left; + } + else + { + if (parentNode.right == null) + { + InsertAsRight(parentNode, newNode); + return; + } + + parentNode = parentNode.right; + } + } + + internal void InsertAsLeft(RedBlackTreeNode parentNode, RedBlackTreeNode newNode) + { + Debug.Assert(parentNode.left == null); + parentNode.left = newNode; + newNode.parent = parentNode; + newNode.color = RED; + host.UpdateAfterChildrenChange(parentNode); + FixTreeOnInsert(newNode); + Count++; + } + + internal void InsertAsRight(RedBlackTreeNode parentNode, RedBlackTreeNode newNode) + { + Debug.Assert(parentNode.right == null); + parentNode.right = newNode; + newNode.parent = parentNode; + newNode.color = RED; + host.UpdateAfterChildrenChange(parentNode); + FixTreeOnInsert(newNode); + Count++; + } + + private void FixTreeOnInsert(RedBlackTreeNode node) + { + Debug.Assert(node != null); + Debug.Assert(node.color == RED); + Debug.Assert(node.left == null || node.left.color == BLACK); + Debug.Assert(node.right == null || node.right.color == BLACK); + + var parentNode = node.parent; + if (parentNode == null) + { + // we inserted in the root -> the node must be black + // since this is a root node, making the node black increments the number of black nodes + // on all paths by one, so it is still the same for all paths. + node.color = BLACK; + return; + } + + if (parentNode.color == BLACK) + return; + // parentNode is red, so there is a conflict here! + + // because the root is black, parentNode is not the root -> there is a grandparent node + var grandparentNode = parentNode.parent; + var uncleNode = Sibling(parentNode); + if (uncleNode != null && uncleNode.color == RED) + { + parentNode.color = BLACK; + uncleNode.color = BLACK; + grandparentNode.color = RED; + FixTreeOnInsert(grandparentNode); + return; + } + + // now we know: parent is red but uncle is black + // First rotation: + if (node == parentNode.right && parentNode == grandparentNode.left) + { + RotateLeft(parentNode); + node = node.left; + } + else if (node == parentNode.left && parentNode == grandparentNode.right) + { + RotateRight(parentNode); + node = node.right; + } + + // because node might have changed, reassign variables: + parentNode = node.parent; + grandparentNode = parentNode.parent; + + // Now recolor a bit: + parentNode.color = BLACK; + grandparentNode.color = RED; + // Second rotation: + if (node == parentNode.left && parentNode == grandparentNode.left) + { + RotateRight(grandparentNode); + } + else + { + // because of the first rotation, this is guaranteed: + Debug.Assert(node == parentNode.right && parentNode == grandparentNode.right); + RotateLeft(grandparentNode); + } + } + + private void ReplaceNode(RedBlackTreeNode replacedNode, RedBlackTreeNode newNode) + { + if (replacedNode.parent == null) + { + Debug.Assert(replacedNode == root); + root = newNode; + } + else + { + if (replacedNode.parent.left == replacedNode) + replacedNode.parent.left = newNode; + else + replacedNode.parent.right = newNode; + } + + if (newNode != null) + newNode.parent = replacedNode.parent; + replacedNode.parent = null; + } + + private void RotateLeft(RedBlackTreeNode p) + { + // let q be p's right child + var q = p.right; + Debug.Assert(q != null); + Debug.Assert(q.parent == p); + // set q to be the new root + ReplaceNode(p, q); + + // set p's right child to be q's left child + p.right = q.left; + if (p.right != null) p.right.parent = p; + // set q's left child to be p + q.left = p; + p.parent = q; + host.UpdateAfterRotateLeft(p); + } + + private void RotateRight(RedBlackTreeNode p) + { + // let q be p's left child + var q = p.left; + Debug.Assert(q != null); + Debug.Assert(q.parent == p); + // set q to be the new root + ReplaceNode(p, q); + + // set p's left child to be q's right child + p.left = q.right; + if (p.left != null) p.left.parent = p; + // set q's right child to be p + q.right = p; + p.parent = q; + host.UpdateAfterRotateRight(p); + } + + private static RedBlackTreeNode Sibling(RedBlackTreeNode node) + { + if (node == node.parent.left) + return node.parent.right; + return node.parent.left; + } + + #endregion + + #region Remove + + public void RemoveAt(RedBlackTreeIterator iterator) + { + var node = iterator.node; + if (node == null) + throw new ArgumentException("Invalid iterator"); + while (node.parent != null) + node = node.parent; + if (node != root) + throw new ArgumentException("Iterator does not belong to this tree"); + RemoveNode(iterator.node); +#if DEBUG + CheckProperties(); +#endif + } + + internal void RemoveNode(RedBlackTreeNode removedNode) + { + if (removedNode.left != null && removedNode.right != null) + { + // replace removedNode with it's in-order successor + + var leftMost = removedNode.right.LeftMost; + RemoveNode(leftMost); // remove leftMost from its current location + + // and overwrite the removedNode with it + ReplaceNode(removedNode, leftMost); + leftMost.left = removedNode.left; + if (leftMost.left != null) leftMost.left.parent = leftMost; + leftMost.right = removedNode.right; + if (leftMost.right != null) leftMost.right.parent = leftMost; + leftMost.color = removedNode.color; + + host.UpdateAfterChildrenChange(leftMost); + if (leftMost.parent != null) host.UpdateAfterChildrenChange(leftMost.parent); + return; + } + + Count--; + + // now either removedNode.left or removedNode.right is null + // get the remaining child + var parentNode = removedNode.parent; + var childNode = removedNode.left ?? removedNode.right; + ReplaceNode(removedNode, childNode); + if (parentNode != null) host.UpdateAfterChildrenChange(parentNode); + if (removedNode.color == BLACK) + { + if (childNode != null && childNode.color == RED) + childNode.color = BLACK; + else + FixTreeOnDelete(childNode, parentNode); + } + } + + private static RedBlackTreeNode Sibling(RedBlackTreeNode node, RedBlackTreeNode parentNode) + { + Debug.Assert(node == null || node.parent == parentNode); + if (node == parentNode.left) + return parentNode.right; + return parentNode.left; + } + + private const bool RED = true; + private const bool BLACK = false; + + private static bool GetColor(RedBlackTreeNode node) + { + return node != null && node.color; + } + + private void FixTreeOnDelete(RedBlackTreeNode node, RedBlackTreeNode parentNode) + { + Debug.Assert(node == null || node.parent == parentNode); + if (parentNode == null) + return; + + // warning: node may be null + var sibling = Sibling(node, parentNode); + if (sibling.color == RED) + { + parentNode.color = RED; + sibling.color = BLACK; + if (node == parentNode.left) + RotateLeft(parentNode); + else + RotateRight(parentNode); + + sibling = Sibling(node, parentNode); // update value of sibling after rotation + } + + if (parentNode.color == BLACK + && sibling.color == BLACK + && GetColor(sibling.left) == BLACK + && GetColor(sibling.right) == BLACK) + { + sibling.color = RED; + FixTreeOnDelete(parentNode, parentNode.parent); + return; + } + + if (parentNode.color == RED + && sibling.color == BLACK + && GetColor(sibling.left) == BLACK + && GetColor(sibling.right) == BLACK) + { + sibling.color = RED; + parentNode.color = BLACK; + return; + } + + if (node == parentNode.left && + sibling.color == BLACK && + GetColor(sibling.left) == RED && + GetColor(sibling.right) == BLACK) + { + sibling.color = RED; + sibling.left.color = BLACK; + RotateRight(sibling); + } + else if (node == parentNode.right && + sibling.color == BLACK && + GetColor(sibling.right) == RED && + GetColor(sibling.left) == BLACK) + { + sibling.color = RED; + sibling.right.color = BLACK; + RotateLeft(sibling); + } + + sibling = Sibling(node, parentNode); // update value of sibling after rotation + + sibling.color = parentNode.color; + parentNode.color = BLACK; + if (node == parentNode.left) + { + if (sibling.right != null) + { + Debug.Assert(sibling.right.color == RED); + sibling.right.color = BLACK; + } + + RotateLeft(parentNode); + } + else + { + if (sibling.left != null) + { + Debug.Assert(sibling.left.color == RED); + sibling.left.color = BLACK; + } + + RotateRight(parentNode); + } + } + + #endregion + + #region Find/LowerBound/UpperBound/GetEnumerator + + /// + /// Returns the iterator pointing to the specified item, or an iterator in End state if the item is not found. + /// + public RedBlackTreeIterator Find(T item) + { + var it = LowerBound(item); + while (it.IsValid && host.Compare(it.Current, item) == 0) + { + if (host.Equals(it.Current, item)) + return it; + it.MoveNext(); + } + + return default; + } + + /// + /// Returns the iterator pointing to the first item greater or equal to . + /// + public RedBlackTreeIterator LowerBound(T item) + { + var node = root; + RedBlackTreeNode resultNode = null; + while (node != null) + if (host.Compare(node.val, item) < 0) + { + node = node.right; + } + else + { + resultNode = node; + node = node.left; + } + + return new RedBlackTreeIterator(resultNode); + } + + /// + /// Returns the iterator pointing to the first item greater than . + /// + public RedBlackTreeIterator UpperBound(T item) + { + var it = LowerBound(item); + while (it.IsValid && host.Compare(it.Current, item) == 0) + it.MoveNext(); + return it; + } + + /// + /// Gets a tree iterator that starts on the first node. + /// + public RedBlackTreeIterator Begin() + { + if (root == null) return default; + return new RedBlackTreeIterator(root.LeftMost); + } + + /// + /// Gets a tree iterator that starts one node before the first node. + /// + public RedBlackTreeIterator GetEnumerator() + { + if (root == null) return default; + var dummyNode = new RedBlackTreeNode(val: default); + dummyNode.right = root; + return new RedBlackTreeIterator(dummyNode); + } + + #endregion + + #region ICollection members + + public bool Contains(T item) + { + return Find(item).IsValid; + } + + public bool Remove(T item) + { + var it = Find(item); + if (!it.IsValid) + return false; + + RemoveAt(it); + return true; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + bool ICollection.IsReadOnly => false; + + public void CopyTo(T[] array, int arrayIndex) + { + if (array == null) throw new ArgumentNullException(nameof(array)); + foreach (var val in this) + array[arrayIndex++] = val; + } + + #endregion + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Util/CheckedList.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/CheckedList.cs new file mode 100644 index 0000000..ead5ff4 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/CheckedList.cs @@ -0,0 +1,162 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; + +namespace ICSharpCode.TextEditor.Util +{ + /// + /// A IList{T} that checks that it is only accessed on the thread that created it, and that + /// it is not modified while an enumerator is running. + /// + internal sealed class CheckedList : IList + { + private readonly IList baseList; + private readonly int threadID; + private int enumeratorCount; + + public CheckedList() : this(new List()) + { + } + + public CheckedList(IList baseList) + { + if (baseList == null) + throw new ArgumentNullException(nameof(baseList)); + this.baseList = baseList; + threadID = Thread.CurrentThread.ManagedThreadId; + } + + public T this[int index] + { + get + { + CheckRead(); + return baseList[index]; + } + set + { + CheckWrite(); + baseList[index] = value; + } + } + + public int Count + { + get + { + CheckRead(); + return baseList.Count; + } + } + + public bool IsReadOnly + { + get + { + CheckRead(); + return baseList.IsReadOnly; + } + } + + public int IndexOf(T item) + { + CheckRead(); + return baseList.IndexOf(item); + } + + public void Insert(int index, T item) + { + CheckWrite(); + baseList.Insert(index, item); + } + + public void RemoveAt(int index) + { + CheckWrite(); + baseList.RemoveAt(index); + } + + public void Add(T item) + { + CheckWrite(); + baseList.Add(item); + } + + public void Clear() + { + CheckWrite(); + baseList.Clear(); + } + + public bool Contains(T item) + { + CheckRead(); + return baseList.Contains(item); + } + + public void CopyTo(T[] array, int arrayIndex) + { + CheckRead(); + baseList.CopyTo(array, arrayIndex); + } + + public bool Remove(T item) + { + CheckWrite(); + return baseList.Remove(item); + } + + public IEnumerator GetEnumerator() + { + CheckRead(); + return Enumerate(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + CheckRead(); + return Enumerate(); + } + + private void CheckRead() + { + if (Thread.CurrentThread.ManagedThreadId != threadID) + throw new InvalidOperationException("CheckList cannot be accessed from this thread!"); + } + + private void CheckWrite() + { + if (Thread.CurrentThread.ManagedThreadId != threadID) + throw new InvalidOperationException("CheckList cannot be accessed from this thread!"); + if (enumeratorCount != 0) + throw new InvalidOperationException("CheckList cannot be written to while enumerators are active!"); + } + + private IEnumerator Enumerate() + { + CheckRead(); + try + { + enumeratorCount++; + foreach (var val in baseList) + { + yield return val; + CheckRead(); + } + } + finally + { + enumeratorCount--; + CheckRead(); + } + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Util/FileReader.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/FileReader.cs new file mode 100644 index 0000000..3634a7e --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/FileReader.cs @@ -0,0 +1,166 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.IO; +using System.Text; + +namespace ICSharpCode.TextEditor.Util +{ + /// + /// Class that can open text files with auto-detection of the encoding. + /// + public static class FileReader + { + public static bool IsUnicode(Encoding encoding) + { + var codepage = encoding.CodePage; + // return true if codepage is any UTF codepage + return codepage == 65001 || codepage == 65000 || codepage == 1200 || codepage == 1201; + } + + public static string ReadFileContent(Stream fs, ref Encoding encoding) + { + using (var reader = OpenStream(fs, encoding)) + { + reader.Peek(); + encoding = reader.CurrentEncoding; + return reader.ReadToEnd(); + } + } + + public static string ReadFileContent(string fileName, Encoding encoding) + { + using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + return ReadFileContent(fs, ref encoding); + } + } + + public static StreamReader OpenStream(Stream fs, Encoding defaultEncoding) + { + if (fs == null) + throw new ArgumentNullException(nameof(fs)); + + if (fs.Length >= 2) + { + // the autodetection of StreamReader is not capable of detecting the difference + // between ISO-8859-1 and UTF-8 without BOM. + var firstByte = fs.ReadByte(); + var secondByte = fs.ReadByte(); + switch ((firstByte << 8) | secondByte) + { + case 0x0000: // either UTF-32 Big Endian or a binary file; use StreamReader + case 0xfffe: // Unicode BOM (UTF-16 LE or UTF-32 LE) + case 0xfeff: // UTF-16 BE BOM + case 0xefbb: // start of UTF-8 BOM + // StreamReader autodetection works + fs.Position = 0; + return new StreamReader(fs); + default: + return AutoDetect(fs, (byte)firstByte, (byte)secondByte, defaultEncoding); + } + } + + if (defaultEncoding != null) + return new StreamReader(fs, defaultEncoding); + + return new StreamReader(fs); + } + + private static StreamReader AutoDetect(Stream fs, byte firstByte, byte secondByte, Encoding defaultEncoding) + { + var max = (int)Math.Min(fs.Length, val2: 500000); // look at max. 500 KB + const int ASCII = 0; + const int Error = 1; + const int UTF8 = 2; + const int UTF8Sequence = 3; + var state = ASCII; + var sequenceLength = 0; + for (var i = 0; i < max; i++) + { + byte b; + if (i == 0) + b = firstByte; + else if (i == 1) + b = secondByte; + else + b = (byte)fs.ReadByte(); + if (b < 0x80) + { + // normal ASCII character + if (state == UTF8Sequence) + { + state = Error; + break; + } + } + else if (b < 0xc0) + { + // 10xxxxxx : continues UTF8 byte sequence + if (state == UTF8Sequence) + { + --sequenceLength; + if (sequenceLength < 0) + { + state = Error; + break; + } + + if (sequenceLength == 0) + state = UTF8; + } + else + { + state = Error; + break; + } + } + else if (b >= 0xc2 && b < 0xf5) + { + // beginning of byte sequence + if (state == UTF8 || state == ASCII) + { + state = UTF8Sequence; + if (b < 0xe0) + sequenceLength = 1; // one more byte following + else if (b < 0xf0) + sequenceLength = 2; // two more bytes following + else + sequenceLength = 3; // three more bytes following + } + else + { + state = Error; + break; + } + } + else + { + // 0xc0, 0xc1, 0xf5 to 0xff are invalid in UTF-8 (see RFC 3629) + state = Error; + break; + } + } + + fs.Position = 0; + switch (state) + { + case ASCII: + case Error: + // when the file seems to be ASCII or non-UTF8, + // we read it using the user-specified encoding so it is saved again + // using that encoding. + if (IsUnicode(defaultEncoding)) + defaultEncoding = Encoding.Default; // use system encoding instead + return new StreamReader(fs, defaultEncoding); + default: + return new StreamReader(fs); + } + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Util/LoggingService.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/LoggingService.cs new file mode 100644 index 0000000..5025ebc --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/LoggingService.cs @@ -0,0 +1,24 @@ +// +// +// +// +// $Revision$ +// + +using System; + +namespace ICSharpCode.TextEditor.Util +{ + /// + /// Central location for logging calls in the text editor. + /// + internal static class LoggingService + { + public static void Debug(string text) + { +#if DEBUG + Console.WriteLine(text); +#endif + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Util/LookupTable.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/LookupTable.cs new file mode 100644 index 0000000..4daf565 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/LookupTable.cs @@ -0,0 +1,154 @@ +// +// +// +// +// $Revision$ +// + +using ICSharpCode.TextEditor.Document; + +namespace ICSharpCode.TextEditor.Util +{ + /// + /// This class implements a keyword map. It implements a digital search trees (tries) to find + /// a word. + /// + public class LookupTable + { + private readonly bool casesensitive; + private readonly Node root = new Node(color: null, word: null); + + /// + /// Creates a new instance of + /// + public LookupTable(bool casesensitive) + { + this.casesensitive = casesensitive; + } + + /// + /// The number of elements in the table + /// + public int Count { get; private set; } + + /// + /// Get the object, which was inserted under the keyword (line, at offset, with length length), + /// returns null, if no such keyword was inserted. + /// + public object this[IDocument document, LineSegment line, int offset, int length] + { + get + { + if (length == 0) + return null; + var next = root; + + var wordOffset = line.Offset + offset; + if (casesensitive) + for (var i = 0; i < length; ++i) + { + var index = document.GetCharAt(wordOffset + i)%256; + next = next[index]; + + if (next == null) + return null; + + if (next.color != null && TextUtility.RegionMatches(document, wordOffset, length, next.word)) + return next.color; + } + else + for (var i = 0; i < length; ++i) + { + var index = char.ToUpper(document.GetCharAt(wordOffset + i))%256; + + next = next[index]; + + if (next == null) + return null; + + if (next.color != null && TextUtility.RegionMatches(document, casesensitive, wordOffset, length, next.word)) + return next.color; + } + + return null; + } + } + + /// + /// Inserts an object in the tree, under keyword + /// + public object this[string keyword] + { + set + { + var node = root; + var next = root; + if (!casesensitive) + keyword = keyword.ToUpper(); + ++Count; + + // insert word into the tree + for (var i = 0; i < keyword.Length; ++i) + { + var index = keyword[i]%256; // index of curchar +// var d = keyword[i] == '\\'; + + next = next[index]; // get node to this index + + if (next == null) + { + // no node created -> insert word here + node[index] = new Node(value, keyword); + break; + } + + if (next.word != null && next.word.Length != i) + { + // node there, take node content and insert them again + var tmpword = next.word; // this word will be inserted 1 level deeper (better, don't need too much + var tmpcolor = next.color; // string comparisons for finding.) + next.color = next.word = null; + this[tmpword] = tmpcolor; + } + + if (i == keyword.Length - 1) + { + // end of keyword reached, insert node there, if a node was here it was + next.word = keyword; // reinserted, if it has the same length (keyword EQUALS this word) it will be overwritten + next.color = value; + break; + } + + node = next; + } + } + } + + private class Node + { + private Node[] children; + public object color; + + public string word; + + public Node(object color, string word) + { + this.word = word; + this.color = color; + } + + // Lazily initialize children array. Saves 200 KB of memory for the C# highlighting + // because we don't have to store the array for leaf nodes. + public Node this[int index] + { + get => children?[index]; + set + { + if (children == null) + children = new Node[256]; + children[index] = value; + } + } + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Util/MouseWheelHandler.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/MouseWheelHandler.cs new file mode 100644 index 0000000..62d98f9 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/MouseWheelHandler.cs @@ -0,0 +1,36 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Windows.Forms; + +namespace ICSharpCode.TextEditor.Util +{ + /// + /// Accumulates mouse wheel deltas and reports the actual number of lines to scroll. + /// + internal class MouseWheelHandler + { + // CODE DUPLICATION: See ICSharpCode.SharpDevelop.Widgets.MouseWheelHandler + + private const int WHEEL_DELTA = 120; + + private int mouseWheelDelta; + + public int GetScrollAmount(MouseEventArgs e) + { + // accumulate the delta to support high-resolution mice + mouseWheelDelta += e.Delta; + + var linesPerClick = Math.Max(SystemInformation.MouseWheelScrollLines, val2: 1); + + var scrollDistance = mouseWheelDelta*linesPerClick/WHEEL_DELTA; + mouseWheelDelta %= Math.Max(val1: 1, WHEEL_DELTA/linesPerClick); + return scrollDistance; + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Util/RedBlackTreeIterator.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/RedBlackTreeIterator.cs new file mode 100644 index 0000000..414fcfb --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/RedBlackTreeIterator.cs @@ -0,0 +1,90 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace ICSharpCode.TextEditor.Util +{ + internal struct RedBlackTreeIterator : IEnumerator + { + internal RedBlackTreeNode node; + + internal RedBlackTreeIterator(RedBlackTreeNode node) + { + this.node = node; + } + + public bool IsValid => node != null; + + public T Current + { + get + { + if (node != null) + return node.val; + throw new InvalidOperationException(); + } + } + + object IEnumerator.Current => Current; + + void IDisposable.Dispose() + { + } + + void IEnumerator.Reset() + { + throw new NotSupportedException(); + } + + public bool MoveNext() + { + if (node == null) + return false; + if (node.right != null) + { + node = node.right.LeftMost; + } + else + { + RedBlackTreeNode oldNode; + do + { + oldNode = node; + node = node.parent; + // we are on the way up from the right part, don't output node again + } while (node != null && node.right == oldNode); + } + + return node != null; + } + + public bool MoveBack() + { + if (node == null) + return false; + if (node.left != null) + { + node = node.left.RightMost; + } + else + { + RedBlackTreeNode oldNode; + do + { + oldNode = node; + node = node.parent; + // we are on the way up from the left part, don't output node again + } while (node != null && node.left == oldNode); + } + + return node != null; + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Util/RtfWriter.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/RtfWriter.cs new file mode 100644 index 0000000..18fb509 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/RtfWriter.cs @@ -0,0 +1,191 @@ +// +// +// +// +// $Revision$ +// + +using System.Collections.Generic; +using System.Drawing; +using System.Text; +using ICSharpCode.TextEditor.Document; + +namespace ICSharpCode.TextEditor.Util +{ + public class RtfWriter + { + private static Dictionary colors; + private static int colorNum; + private static StringBuilder colorString; + + public static string GenerateRtf(TextArea textArea) + { + colors = new Dictionary(); + colorNum = 0; + colorString = new StringBuilder(); + + var rtf = new StringBuilder(); + + rtf.Append(@"{\rtf1\ansi\ansicpg1252\deff0\deflang1031"); + BuildFontTable(textArea.Document, rtf); + rtf.Append(value: '\n'); + + var fileContent = BuildFileContent(textArea); + BuildColorTable(rtf); + rtf.Append(value: '\n'); + rtf.Append(@"\viewkind4\uc1\pard"); + rtf.Append(fileContent); + rtf.Append("}"); + return rtf.ToString(); + } + + private static void BuildColorTable(StringBuilder rtf) + { + rtf.Append(@"{\colortbl ;"); + rtf.Append(colorString); + rtf.Append("}"); + } + + private static void BuildFontTable(IDocument doc, StringBuilder rtf) + { + rtf.Append(@"{\fonttbl"); + rtf.Append(@"{\f0\fmodern\fprq1\fcharset0 " + doc.TextEditorProperties.Font.Name + ";}"); + rtf.Append("}"); + } + + private static string BuildFileContent(TextArea textArea) + { + var rtf = new StringBuilder(); + var firstLine = true; + var curColor = SystemColors.WindowText; + var oldItalic = false; + var oldBold = false; + var escapeSequence = false; + + foreach (var selection in textArea.SelectionManager.SelectionCollection) + { + var selectionOffset = textArea.Document.PositionToOffset(selection.StartPosition); + var selectionEndOffset = textArea.Document.PositionToOffset(selection.EndPosition); + for (var i = selection.StartPosition.Y; i <= selection.EndPosition.Y; ++i) + { + var line = textArea.Document.GetLineSegment(i); + var offset = line.Offset; + if (line.Words == null) + continue; + + foreach (var word in line.Words) + switch (word.Type) + { + case TextWordType.Space: + if (selection.ContainsOffset(offset)) + rtf.Append(value: ' '); + ++offset; + break; + + case TextWordType.Tab: + if (selection.ContainsOffset(offset)) + rtf.Append(@"\tab"); + ++offset; + escapeSequence = true; + break; + + case TextWordType.Word: + var c = word.Color; + + if (offset + word.Word.Length > selectionOffset && offset < selectionEndOffset) + { + var colorstr = c.R + ", " + c.G + ", " + c.B; + + if (!colors.ContainsKey(colorstr)) + { + colors[colorstr] = ++colorNum; + colorString.Append(@"\red" + c.R + @"\green" + c.G + @"\blue" + c.B + ";"); + } + + if (c != curColor || firstLine) + { + rtf.Append(@"\cf" + colors[colorstr]); + curColor = c; + escapeSequence = true; + } + + if (oldItalic != word.Italic) + { + if (word.Italic) + rtf.Append(@"\i"); + else + rtf.Append(@"\i0"); + oldItalic = word.Italic; + escapeSequence = true; + } + + if (oldBold != word.Bold) + { + if (word.Bold) + rtf.Append(@"\b"); + else + rtf.Append(@"\b0"); + oldBold = word.Bold; + escapeSequence = true; + } + + if (firstLine) + { + rtf.Append(@"\f0\fs" + textArea.TextEditorProperties.Font.Size*2); + firstLine = false; + } + + if (escapeSequence) + { + rtf.Append(value: ' '); + escapeSequence = false; + } + + string printWord; + if (offset < selectionOffset) + printWord = word.Word.Substring(selectionOffset - offset); + else if (offset + word.Word.Length > selectionEndOffset) + printWord = word.Word.Substring(startIndex: 0, offset + word.Word.Length - selectionEndOffset); + else + printWord = word.Word; + + AppendText(rtf, printWord); + } + + offset += word.Length; + break; + } + if (offset < selectionEndOffset) + rtf.Append(@"\par"); + rtf.Append(value: '\n'); + } + } + + return rtf.ToString(); + } + + private static void AppendText(StringBuilder rtfOutput, string text) + { + //rtf.Append(printWord.Replace(@"\", @"\\").Replace("{", "\\{").Replace("}", "\\}")); + foreach (var c in text) + switch (c) + { + case '\\': + rtfOutput.Append(@"\\"); + break; + case '{': + rtfOutput.Append("\\{"); + break; + case '}': + rtfOutput.Append("\\}"); + break; + default: + if (c < 256) + rtfOutput.Append(c); + else + rtfOutput.Append("\\u" + unchecked((short)c) + "?"); + break; + } + } + } +} diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Util/TextUtility.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/TextUtility.cs new file mode 100644 index 0000000..2451947 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/TextUtility.cs @@ -0,0 +1,64 @@ +// +// +// +// +// $Revision$ +// + +using ICSharpCode.TextEditor.Document; + +namespace ICSharpCode.TextEditor.Util +{ + public class TextUtility + { + public static bool RegionMatches(IDocument document, int offset, int length, string word) + { + if (length != word.Length || document.TextLength < offset + length) + return false; + + for (var i = 0; i < length; ++i) + if (document.GetCharAt(offset + i) != word[i]) + return false; + return true; + } + + public static bool RegionMatches(IDocument document, bool casesensitive, int offset, int length, string word) + { + if (casesensitive) + return RegionMatches(document, offset, length, word); + + if (length != word.Length || document.TextLength < offset + length) + return false; + + for (var i = 0; i < length; ++i) + if (char.ToUpper(document.GetCharAt(offset + i)) != char.ToUpper(word[i])) + return false; + return true; + } + + public static bool RegionMatches(IDocument document, int offset, int length, char[] word) + { + if (length != word.Length || document.TextLength < offset + length) + return false; + + for (var i = 0; i < length; ++i) + if (document.GetCharAt(offset + i) != word[i]) + return false; + return true; + } + + public static bool RegionMatches(IDocument document, bool casesensitive, int offset, int length, char[] word) + { + if (casesensitive) + return RegionMatches(document, offset, length, word); + + if (length != word.Length || document.TextLength < offset + length) + return false; + + for (var i = 0; i < length; ++i) + if (char.ToUpper(document.GetCharAt(offset + i)) != char.ToUpper(word[i])) + return false; + return true; + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Util/TipPainter.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/TipPainter.cs new file mode 100644 index 0000000..b80de89 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/TipPainter.cs @@ -0,0 +1,207 @@ +// +// +// +// +// $Revision$ +// + +using System.Drawing; +using System.Drawing.Text; +using System.Windows.Forms; + +namespace ICSharpCode.TextEditor.Util +{ + internal static class TipPainter + { + private const float HorizontalBorder = 2; + private const float VerticalBorder = 1; + + //static StringFormat centerTipFormat = CreateTipStringFormat(); + + public static Size GetTipSize(Control control, Graphics graphics, Font font, string description) + { + return GetTipSize(control, graphics, new TipText(graphics, font, description)); + } + + private static Rectangle GetWorkingArea(Control control) + { + var ownerForm = control.FindForm(); + if (ownerForm.Owner != null) + ownerForm = ownerForm.Owner; + + return Screen.GetWorkingArea(ownerForm); + } + + public static Size GetTipSize(Control control, Graphics graphics, TipSection tipData) + { + var tipSize = Size.Empty; + + RectangleF workingArea = GetWorkingArea(control); + + PointF screenLocation = control.PointToScreen(Point.Empty); + + var maxLayoutSize = new SizeF( + workingArea.Right - screenLocation.X - HorizontalBorder*2, + workingArea.Bottom - screenLocation.Y - VerticalBorder*2); + + if (maxLayoutSize.Width > 0 && maxLayoutSize.Height > 0) + { + graphics.TextRenderingHint = + TextRenderingHint.AntiAliasGridFit; + + tipData.SetMaximumSize(maxLayoutSize); + var tipSizeF = tipData.GetRequiredSize(); + tipData.SetAllocatedSize(tipSizeF); + + tipSizeF += new SizeF( + HorizontalBorder*2, + VerticalBorder*2); + tipSize = Size.Ceiling(tipSizeF); + } + + if (control.ClientSize != tipSize) + control.ClientSize = tipSize; + + return tipSize; + } + + public static Size GetLeftHandSideTipSize(Control control, Graphics graphics, TipSection tipData, Point p) + { + var tipSize = Size.Empty; + + RectangleF workingArea = GetWorkingArea(control); + + PointF screenLocation = p; + + var maxLayoutSize = new SizeF( + screenLocation.X - HorizontalBorder*2, + workingArea.Bottom - screenLocation.Y - VerticalBorder*2); + + if (maxLayoutSize.Width > 0 && maxLayoutSize.Height > 0) + { + graphics.TextRenderingHint = + TextRenderingHint.AntiAliasGridFit; + + tipData.SetMaximumSize(maxLayoutSize); + var tipSizeF = tipData.GetRequiredSize(); + tipData.SetAllocatedSize(tipSizeF); + + tipSizeF += new SizeF( + HorizontalBorder*2, + VerticalBorder*2); + tipSize = Size.Ceiling(tipSizeF); + } + + return tipSize; + } + + public static Size DrawTip(Control control, Graphics graphics, Font font, string description) + { + return DrawTip(control, graphics, new TipText(graphics, font, description)); + } + + public static Size DrawTip(Control control, Graphics graphics, TipSection tipData) + { + var tipSize = Size.Empty; + var tipSizeF = SizeF.Empty; + + PointF screenLocation = control.PointToScreen(Point.Empty); + + RectangleF workingArea = GetWorkingArea(control); + + var maxLayoutSize = new SizeF( + workingArea.Right - screenLocation.X - HorizontalBorder*2, + workingArea.Bottom - screenLocation.Y - VerticalBorder*2); + + if (maxLayoutSize.Width > 0 && maxLayoutSize.Height > 0) + { + graphics.TextRenderingHint = + TextRenderingHint.AntiAliasGridFit; + + tipData.SetMaximumSize(maxLayoutSize); + tipSizeF = tipData.GetRequiredSize(); + tipData.SetAllocatedSize(tipSizeF); + + tipSizeF += new SizeF( + HorizontalBorder*2, + VerticalBorder*2); + tipSize = Size.Ceiling(tipSizeF); + } + + if (control.ClientSize != tipSize) + control.ClientSize = tipSize; + + if (tipSize != Size.Empty) + { + var borderRectangle = new Rectangle + (Point.Empty, tipSize - new Size(width: 1, height: 1)); + +// var displayRectangle = new RectangleF +// ( +// HorizontalBorder, VerticalBorder, +// tipSizeF.Width - HorizontalBorder*2, +// tipSizeF.Height - VerticalBorder*2); + + // DrawRectangle draws from Left to Left + Width. A bug? :-/ + graphics.DrawRectangle( + SystemPens.WindowFrame, + borderRectangle); + tipData.Draw(new PointF(HorizontalBorder, VerticalBorder)); + } + + return tipSize; + } + + public static Size DrawFixedWidthTip(Control control, Graphics graphics, TipSection tipData) + { + var tipSize = Size.Empty; + var tipSizeF = SizeF.Empty; + + PointF screenLocation = control.PointToScreen(new Point(control.Width, y: 0)); + + RectangleF workingArea = GetWorkingArea(control); + + var maxLayoutSize = new SizeF( + screenLocation.X - HorizontalBorder*2, + workingArea.Bottom - screenLocation.Y - VerticalBorder*2); + + if (maxLayoutSize.Width > 0 && maxLayoutSize.Height > 0) + { + graphics.TextRenderingHint = + TextRenderingHint.AntiAliasGridFit; + + tipData.SetMaximumSize(maxLayoutSize); + tipSizeF = tipData.GetRequiredSize(); + tipData.SetAllocatedSize(tipSizeF); + + tipSizeF += new SizeF( + HorizontalBorder*2, + VerticalBorder*2); + tipSize = Size.Ceiling(tipSizeF); + } + + if (control.Height != tipSize.Height) + control.Height = tipSize.Height; + + if (tipSize != Size.Empty) + { + var borderRectangle = new Rectangle + (Point.Empty, control.Size - new Size(width: 1, height: 1)); + +// var displayRectangle = new RectangleF +// ( +// HorizontalBorder, VerticalBorder, +// tipSizeF.Width - HorizontalBorder*2, +// tipSizeF.Height - VerticalBorder*2); + + // DrawRectangle draws from Left to Left + Width. A bug? :-/ + graphics.DrawRectangle( + SystemPens.WindowFrame, + borderRectangle); + tipData.Draw(new PointF(HorizontalBorder, VerticalBorder)); + } + + return tipSize; + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Util/TipPainterTools.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/TipPainterTools.cs new file mode 100644 index 0000000..659dc12 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/TipPainterTools.cs @@ -0,0 +1,331 @@ +// +// +// +// +// $Revision$ +// + +using System.Drawing; +using System.Windows.Forms; + +namespace ICSharpCode.TextEditor.Util +{ + internal static class TipPainterTools + { + private const int SpacerSize = 4; + + // btw. I know it's ugly. + public static Rectangle DrawingRectangle1; + public static Rectangle DrawingRectangle2; + + public static Size GetLeftHandSideDrawingSizeHelpTipFromCombinedDescription( + Control control, + Graphics graphics, + Font font, + string countMessage, + string description, + Point p) + { + string basicDescription = null; + string documentation = null; + + if (!string.IsNullOrEmpty(description)) + { + var splitDescription = description.Split(new[] {'\n'}, count: 2); + + if (splitDescription.Length > 0) + { + basicDescription = splitDescription[0]; + + if (splitDescription.Length > 1) + documentation = splitDescription[1].Trim(); + } + } + + return GetLeftHandSideDrawingSizeDrawHelpTip(control, graphics, font, countMessage, basicDescription, documentation, p); + } + + public static Size GetDrawingSizeHelpTipFromCombinedDescription( + Control control, + Graphics graphics, + Font font, + string countMessage, + string description) + { + string basicDescription = null; + string documentation = null; + + if (!string.IsNullOrEmpty(description)) + { + var splitDescription = description.Split(new[] {'\n'}, count: 2); + + if (splitDescription.Length > 0) + { + basicDescription = splitDescription[0]; + + if (splitDescription.Length > 1) + documentation = splitDescription[1].Trim(); + } + } + + return GetDrawingSizeDrawHelpTip(control, graphics, font, countMessage, basicDescription, documentation); + } + + public static Size DrawHelpTipFromCombinedDescription( + Control control, + Graphics graphics, + Font font, + string countMessage, + string description) + { + string basicDescription = null; + string documentation = null; + + if (!string.IsNullOrEmpty(description)) + { + var splitDescription = description.Split + (new[] {'\n'}, count: 2); + + if (splitDescription.Length > 0) + { + basicDescription = splitDescription[0]; + + if (splitDescription.Length > 1) + documentation = splitDescription[1].Trim(); + } + } + + return DrawHelpTip( + control, graphics, font, countMessage, + basicDescription, documentation); + } + + public static Size DrawFixedWidthHelpTipFromCombinedDescription( + Control control, + Graphics graphics, + Font font, + string countMessage, + string description) + { + string basicDescription = null; + string documentation = null; + + if (!string.IsNullOrEmpty(description)) + { + var splitDescription = description.Split + (new[] {'\n'}, count: 2); + + if (splitDescription.Length > 0) + { + basicDescription = splitDescription[0]; + + if (splitDescription.Length > 1) + documentation = splitDescription[1].Trim(); + } + } + + return DrawFixedWidthHelpTip( + control, graphics, font, countMessage, + basicDescription, documentation); + } + + public static Size GetDrawingSizeDrawHelpTip( + Control control, + Graphics graphics, Font font, + string countMessage, + string basicDescription, + string documentation) + { + if (!string.IsNullOrEmpty(countMessage) || + !string.IsNullOrEmpty(basicDescription) || + !string.IsNullOrEmpty(documentation)) + { + // Create all the TipSection objects. + var countMessageTip = new CountTipText(graphics, font, countMessage); + + var countSpacer = new TipSpacer(graphics, new SizeF(!string.IsNullOrEmpty(countMessage) ? 4 : 0, height: 0)); + + var descriptionTip = new TipText(graphics, font, basicDescription); + + var docSpacer = new TipSpacer(graphics, new SizeF(width: 0, !string.IsNullOrEmpty(documentation) ? 4 : 0)); + + var docTip = new TipText(graphics, font, documentation); + + // Now put them together. + var descSplitter = new TipSplitter( + graphics, horizontal: false, + descriptionTip, + docSpacer + ); + + var mainSplitter = new TipSplitter( + graphics, horizontal: true, + countMessageTip, + countSpacer, + descSplitter); + + var mainSplitter2 = new TipSplitter( + graphics, horizontal: false, + mainSplitter, + docTip); + + // Show it. + var size = TipPainter.GetTipSize(control, graphics, mainSplitter2); + DrawingRectangle1 = countMessageTip.DrawingRectangle1; + DrawingRectangle2 = countMessageTip.DrawingRectangle2; + return size; + } + + return Size.Empty; + } + + public static Size GetLeftHandSideDrawingSizeDrawHelpTip( + Control control, + Graphics graphics, Font font, + string countMessage, + string basicDescription, + string documentation, + Point p) + { + if (!string.IsNullOrEmpty(countMessage) || + !string.IsNullOrEmpty(basicDescription) || + !string.IsNullOrEmpty(documentation)) + { + // Create all the TipSection objects. + var countMessageTip = new CountTipText(graphics, font, countMessage); + + var countSpacer = new TipSpacer(graphics, new SizeF(!string.IsNullOrEmpty(countMessage) ? 4 : 0, height: 0)); + + var descriptionTip = new TipText(graphics, font, basicDescription); + + var docSpacer = new TipSpacer(graphics, new SizeF(width: 0, !string.IsNullOrEmpty(documentation) ? 4 : 0)); + + var docTip = new TipText(graphics, font, documentation); + + // Now put them together. + var descSplitter = new TipSplitter( + graphics, horizontal: false, + descriptionTip, + docSpacer + ); + + var mainSplitter = new TipSplitter( + graphics, horizontal: true, + countMessageTip, + countSpacer, + descSplitter); + + var mainSplitter2 = new TipSplitter( + graphics, horizontal: false, + mainSplitter, + docTip); + + // Show it. + var size = TipPainter.GetLeftHandSideTipSize(control, graphics, mainSplitter2, p); + return size; + } + + return Size.Empty; + } + + public static Size DrawHelpTip( + Control control, + Graphics graphics, Font font, + string countMessage, + string basicDescription, + string documentation) + { + if (!string.IsNullOrEmpty(countMessage) || + !string.IsNullOrEmpty(basicDescription) || + !string.IsNullOrEmpty(documentation)) + { + // Create all the TipSection objects. + var countMessageTip = new CountTipText(graphics, font, countMessage); + + var countSpacer = new TipSpacer(graphics, new SizeF(!string.IsNullOrEmpty(countMessage) ? 4 : 0, height: 0)); + + var descriptionTip = new TipText(graphics, font, basicDescription); + + var docSpacer = new TipSpacer(graphics, new SizeF(width: 0, !string.IsNullOrEmpty(documentation) ? 4 : 0)); + + var docTip = new TipText(graphics, font, documentation); + + // Now put them together. + var descSplitter = new TipSplitter( + graphics, horizontal: false, + descriptionTip, + docSpacer + ); + + var mainSplitter = new TipSplitter( + graphics, horizontal: true, + countMessageTip, + countSpacer, + descSplitter); + + var mainSplitter2 = new TipSplitter( + graphics, horizontal: false, + mainSplitter, + docTip); + + // Show it. + var size = TipPainter.DrawTip(control, graphics, mainSplitter2); + DrawingRectangle1 = countMessageTip.DrawingRectangle1; + DrawingRectangle2 = countMessageTip.DrawingRectangle2; + return size; + } + + return Size.Empty; + } + + public static Size DrawFixedWidthHelpTip( + Control control, + Graphics graphics, Font font, + string countMessage, + string basicDescription, + string documentation) + { + if (!string.IsNullOrEmpty(countMessage) || + !string.IsNullOrEmpty(basicDescription) || + !string.IsNullOrEmpty(documentation)) + { + // Create all the TipSection objects. + var countMessageTip = new CountTipText(graphics, font, countMessage); + + var countSpacer = new TipSpacer(graphics, new SizeF(!string.IsNullOrEmpty(countMessage) ? 4 : 0, height: 0)); + + var descriptionTip = new TipText(graphics, font, basicDescription); + + var docSpacer = new TipSpacer(graphics, new SizeF(width: 0, !string.IsNullOrEmpty(documentation) ? 4 : 0)); + + var docTip = new TipText(graphics, font, documentation); + + // Now put them together. + var descSplitter = new TipSplitter( + graphics, horizontal: false, + descriptionTip, + docSpacer + ); + + var mainSplitter = new TipSplitter( + graphics, horizontal: true, + countMessageTip, + countSpacer, + descSplitter); + + var mainSplitter2 = new TipSplitter( + graphics, horizontal: false, + mainSplitter, + docTip); + + // Show it. + var size = TipPainter.DrawFixedWidthTip(control, graphics, mainSplitter2); + DrawingRectangle1 = countMessageTip.DrawingRectangle1; + DrawingRectangle2 = countMessageTip.DrawingRectangle2; + return size; + } + + return Size.Empty; + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Util/TipSection.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/TipSection.cs new file mode 100644 index 0000000..a2e2cf6 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/TipSection.cs @@ -0,0 +1,70 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Diagnostics; +using System.Drawing; + +namespace ICSharpCode.TextEditor.Util +{ + internal abstract class TipSection + { + private SizeF tipRequiredSize; + + protected TipSection(Graphics graphics) + { + Graphics = graphics; + } + + protected Graphics Graphics { get; } + + protected SizeF AllocatedSize { get; private set; } + + protected SizeF MaximumSize { get; private set; } + + public abstract void Draw(PointF location); + + public SizeF GetRequiredSize() + { + return tipRequiredSize; + } + + public void SetAllocatedSize(SizeF allocatedSize) + { + Debug.Assert( + allocatedSize.Width >= tipRequiredSize.Width && + allocatedSize.Height >= tipRequiredSize.Height); + + AllocatedSize = allocatedSize; + OnAllocatedSizeChanged(); + } + + public void SetMaximumSize(SizeF maximumSize) + { + MaximumSize = maximumSize; + OnMaximumSizeChanged(); + } + + protected virtual void OnAllocatedSizeChanged() + { + } + + protected virtual void OnMaximumSizeChanged() + { + } + + protected void SetRequiredSize(SizeF requiredSize) + { + requiredSize.Width = Math.Max(val1: 0, requiredSize.Width); + requiredSize.Height = Math.Max(val1: 0, requiredSize.Height); + requiredSize.Width = Math.Min(MaximumSize.Width, requiredSize.Width); + requiredSize.Height = Math.Min(MaximumSize.Height, requiredSize.Height); + + tipRequiredSize = requiredSize; + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Util/TipSpacer.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/TipSpacer.cs new file mode 100644 index 0000000..069d223 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/TipSpacer.cs @@ -0,0 +1,37 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Drawing; + +namespace ICSharpCode.TextEditor.Util +{ + internal class TipSpacer : TipSection + { + private readonly SizeF spacerSize; + + public TipSpacer(Graphics graphics, SizeF size) : base(graphics) + { + spacerSize = size; + } + + public override void Draw(PointF location) + { + } + + protected override void OnMaximumSizeChanged() + { + base.OnMaximumSizeChanged(); + + SetRequiredSize( + new SizeF + ( + Math.Min(MaximumSize.Width, spacerSize.Width), + Math.Min(MaximumSize.Height, spacerSize.Height))); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Util/TipSplitter.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/TipSplitter.cs new file mode 100644 index 0000000..041f3d7 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/TipSplitter.cs @@ -0,0 +1,97 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Diagnostics; +using System.Drawing; + +namespace ICSharpCode.TextEditor.Util +{ + internal class TipSplitter : TipSection + { + private readonly bool isHorizontal; + private readonly float[] offsets; + private readonly TipSection[] tipSections; + + public TipSplitter(Graphics graphics, bool horizontal, params TipSection[] sections) : base(graphics) + { + Debug.Assert(sections != null); + + isHorizontal = horizontal; + offsets = new float[sections.Length]; + tipSections = (TipSection[])sections.Clone(); + } + + public override void Draw(PointF location) + { + if (isHorizontal) + for (var i = 0; i < tipSections.Length; i++) + tipSections[i].Draw + (new PointF(location.X + offsets[i], location.Y)); + else + for (var i = 0; i < tipSections.Length; i++) + tipSections[i].Draw + (new PointF(location.X, location.Y + offsets[i])); + } + + protected override void OnMaximumSizeChanged() + { + base.OnMaximumSizeChanged(); + + float currentDim = 0; + float otherDim = 0; + var availableArea = MaximumSize; + + for (var i = 0; i < tipSections.Length; i++) + { + var section = tipSections[i]; + + section.SetMaximumSize(availableArea); + + var requiredArea = section.GetRequiredSize(); + offsets[i] = currentDim; + + // It's best to start on pixel borders, so this will + // round up to the nearest pixel. Otherwise there are + // weird cutoff artifacts. + float pixelsUsed; + + if (isHorizontal) + { + pixelsUsed = (float)Math.Ceiling(requiredArea.Width); + currentDim += pixelsUsed; + + availableArea.Width = Math.Max + (val1: 0, availableArea.Width - pixelsUsed); + + otherDim = Math.Max(otherDim, requiredArea.Height); + } + else + { + pixelsUsed = (float)Math.Ceiling(requiredArea.Height); + currentDim += pixelsUsed; + + availableArea.Height = Math.Max + (val1: 0, availableArea.Height - pixelsUsed); + + otherDim = Math.Max(otherDim, requiredArea.Width); + } + } + + foreach (var section in tipSections) + if (isHorizontal) + section.SetAllocatedSize(new SizeF(section.GetRequiredSize().Width, otherDim)); + else + section.SetAllocatedSize(new SizeF(otherDim, section.GetRequiredSize().Height)); + + if (isHorizontal) + SetRequiredSize(new SizeF(currentDim, otherDim)); + else + SetRequiredSize(new SizeF(otherDim, currentDim)); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Util/TipText.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/TipText.cs new file mode 100644 index 0000000..8a280d0 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/TipText.cs @@ -0,0 +1,189 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Drawing; + +namespace ICSharpCode.TextEditor.Util +{ + internal class CountTipText : TipText + { + private readonly float triHeight = 10; + private readonly float triWidth = 10; + + public Rectangle DrawingRectangle1; + public Rectangle DrawingRectangle2; + + public CountTipText(Graphics graphics, Font font, string text) : base(graphics, font, text) + { + } + + private void DrawTriangle(float x, float y, bool flipped) + { + var brush = SystemBrushes.Info; + Graphics.FillRectangle(brush, new RectangleF(x, y, triHeight, triHeight)); + var triHeight2 = triHeight/2; + var triHeight4 = triHeight/4; + brush = SystemBrushes.InfoText; + if (flipped) + Graphics.FillPolygon( + brush, new[] + { + new PointF(x, y + triHeight2 - triHeight4), + new PointF(x + triWidth/2, y + triHeight2 + triHeight4), + new PointF(x + triWidth, y + triHeight2 - triHeight4) + }); + else + Graphics.FillPolygon( + brush, new[] + { + new PointF(x, y + triHeight2 + triHeight4), + new PointF(x + triWidth/2, y + triHeight2 - triHeight4), + new PointF(x + triWidth, y + triHeight2 + triHeight4) + }); + } + + public override void Draw(PointF location) + { + if (!string.IsNullOrEmpty(tipText)) + { + base.Draw(new PointF(location.X + triWidth + 4, location.Y)); + DrawingRectangle1 = new Rectangle( + (int)location.X + 2, + (int)location.Y + 2, + (int)triWidth, + (int)triHeight); + DrawingRectangle2 = new Rectangle( + (int)(location.X + AllocatedSize.Width - triWidth - 2), + (int)location.Y + 2, + (int)triWidth, + (int)triHeight); + DrawTriangle(location.X + 2, location.Y + 2, flipped: false); + DrawTriangle(location.X + AllocatedSize.Width - triWidth - 2, location.Y + 2, flipped: true); + } + } + + protected override void OnMaximumSizeChanged() + { + if (!string.IsNullOrEmpty(tipText)) + { + var tipSize = Graphics.MeasureString + ( + tipText, tipFont, MaximumSize, + GetInternalStringFormat()); + tipSize.Width += triWidth*2 + 8; + SetRequiredSize(tipSize); + } + else + { + SetRequiredSize(SizeF.Empty); + } + } + } + + internal class TipText : TipSection + { + protected StringAlignment horzAlign; + protected Color tipColor; + protected Font tipFont; + protected StringFormat tipFormat; + protected string tipText; + protected StringAlignment vertAlign; + + public TipText(Graphics graphics, Font font, string text) : + base(graphics) + { + tipFont = font; + tipText = text; + if (text != null && text.Length > short.MaxValue) + throw new ArgumentException("TipText: text too long (max. is " + short.MaxValue + " characters)", nameof(text)); + + Color = SystemColors.InfoText; + HorizontalAlignment = StringAlignment.Near; + VerticalAlignment = StringAlignment.Near; + } + + public Color Color + { + get => tipColor; + set => tipColor = value; + } + + public StringAlignment HorizontalAlignment + { + get => horzAlign; + set + { + horzAlign = value; + tipFormat = null; + } + } + + public StringAlignment VerticalAlignment + { + get => vertAlign; + set + { + vertAlign = value; + tipFormat = null; + } + } + + public override void Draw(PointF location) + { + if (!string.IsNullOrEmpty(tipText)) + { + var drawRectangle = new RectangleF(location, AllocatedSize); + + Graphics.DrawString( + tipText, tipFont, + BrushRegistry.GetBrush(Color), + drawRectangle, + GetInternalStringFormat()); + } + } + + protected StringFormat GetInternalStringFormat() + { + if (tipFormat == null) + tipFormat = CreateTipStringFormat(horzAlign, vertAlign); + + return tipFormat; + } + + protected override void OnMaximumSizeChanged() + { + base.OnMaximumSizeChanged(); + + if (!string.IsNullOrEmpty(tipText)) + { + var tipSize = Graphics.MeasureString + ( + tipText, tipFont, MaximumSize, + GetInternalStringFormat()); + + SetRequiredSize(tipSize); + } + else + { + SetRequiredSize(SizeF.Empty); + } + } + + private static StringFormat CreateTipStringFormat(StringAlignment horizontalAlignment, StringAlignment verticalAlignment) + { + var format = (StringFormat)StringFormat.GenericTypographic.Clone(); + format.FormatFlags = StringFormatFlags.FitBlackBox | StringFormatFlags.MeasureTrailingSpaces; + // note: Align Near, Line Center seemed to do something before + + format.Alignment = horizontalAlignment; + format.LineAlignment = verticalAlignment; + + return format; + } + } +} diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Util/WeakCollection.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/WeakCollection.cs new file mode 100644 index 0000000..1cdf570 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/WeakCollection.cs @@ -0,0 +1,148 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace ICSharpCode.TextEditor.Util +{ + /// + /// A collection that does not allows its elements to be garbage-collected (unless there are other + /// references to the elements). Elements will disappear from the collection when they are + /// garbage-collected. + /// The WeakCollection is not thread-safe, not even for read-only access! + /// No methods may be called on the WeakCollection while it is enumerated, not even a Contains or + /// creating a second enumerator. + /// The WeakCollection does not preserve any order among its contents; the ordering may be different each + /// time the collection is enumerated. + /// Since items may disappear at any time when they are garbage collected, this class + /// cannot provide a useful implementation for Count and thus cannot implement the ICollection interface. + /// + public class WeakCollection : IEnumerable where T : class + { + private readonly List innerList = new List(); + + private bool hasEnumerator; + + /// + /// Enumerates the collection. + /// Each MoveNext() call on the enumerator is O(1), thus the enumeration is O(n). + /// + public IEnumerator GetEnumerator() + { + if (hasEnumerator) + throw new InvalidOperationException("The WeakCollection is already being enumerated, it cannot be enumerated twice at the same time. Ensure you dispose the first enumerator before using another enumerator."); + try + { + hasEnumerator = true; + for (var i = 0; i < innerList.Count;) + { + var element = (T)innerList[i].Target; + if (element == null) + { + RemoveAt(i); + } + else + { + yield return element; + i++; + } + } + } + finally + { + hasEnumerator = false; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Adds an element to the collection. Runtime: O(n). + /// + public void Add(T item) + { + if (item == null) + throw new ArgumentNullException(nameof(item)); + CheckNoEnumerator(); + if (innerList.Count == innerList.Capacity || innerList.Count%32 == 31) + innerList.RemoveAll(delegate(WeakReference r) { return !r.IsAlive; }); + innerList.Add(new WeakReference(item)); + } + + /// + /// Removes all elements from the collection. Runtime: O(n). + /// + public void Clear() + { + innerList.Clear(); + CheckNoEnumerator(); + } + + /// + /// Checks if the collection contains an item. Runtime: O(n). + /// + public bool Contains(T item) + { + if (item == null) + throw new ArgumentNullException(nameof(item)); + CheckNoEnumerator(); + foreach (var element in this) + if (item.Equals(element)) + return true; + return false; + } + + /// + /// Removes an element from the collection. Returns true if the item is found and removed, + /// false when the item is not found. + /// Runtime: O(n). + /// + public bool Remove(T item) + { + if (item == null) + throw new ArgumentNullException(nameof(item)); + CheckNoEnumerator(); + for (var i = 0; i < innerList.Count;) + { + var element = (T)innerList[i].Target; + if (element == null) + { + RemoveAt(i); + } + else if (element == item) + { + RemoveAt(i); + return true; + } + else + { + i++; + } + } + + return false; + } + + private void RemoveAt(int i) + { + var lastIndex = innerList.Count - 1; + innerList[i] = innerList[lastIndex]; + innerList.RemoveAt(lastIndex); + } + + private void CheckNoEnumerator() + { + if (hasEnumerator) + throw new InvalidOperationException("The WeakCollection is already being enumerated, it cannot be modified at the same time. Ensure you dispose the first enumerator before modifying the WeakCollection."); + } + } +} \ No newline at end of file diff --git a/NetCore31/src/ICSharpCode.TextEditor/Src/Util/Win32Util.cs b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/Win32Util.cs new file mode 100644 index 0000000..583b7c8 --- /dev/null +++ b/NetCore31/src/ICSharpCode.TextEditor/Src/Util/Win32Util.cs @@ -0,0 +1,11 @@ +using System; +using System.Drawing; + +namespace ICSharpCode.TextEditor.Util +{ + public static class Win32Util + { + public static Point ToPoint(this IntPtr lparam) => + new Point(unchecked((int)lparam.ToInt64())); + } +} \ No newline at end of file diff --git a/README.md b/README.md index d05e580..20e7003 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,41 @@ -# ICSharpCode.TextEditor -ICSharpCode.TextEditor for WinForms from SharpDevelop 3.2 +# ICSharpCode.TextEditor for .NET Core 3.1 + +We're adding a new feature to [Visual ReCode](https://visualrecode.com) to +automatically migrate .NET 4.x projects to .NET Core 3.1. We expect this to +be helpful for people with projects that use WPF and Windows Forms now that +those are supported in Core, and also for projects like console applications, +Windows Services and so on which have Core equivalents. + +I reached out on Twitter and asked for open-source WinForms or WPF projects, +and somebody pointed me at [GitExtensions](https://github.com/gitextensions/gitextensions). +That's an insanely big and complicated project and I really wanted to start smaller, +but I noticed it referenced [ICSharpCode.TextEditor](https://github.com/gitextensions/ICSharpCode.TextEditor) +and that's much more like it. The library project has already been converted to the +new SDK project format, but targeting .NET 4.6.1, and there's a Windows Forms +sample that's still using the old-style project format. + +After a few fixes and improvements, Visual ReCode was able to cleanly migrate +both projects to .NET Core 3.1, using the `Microsoft.NET.Sdk.WindowsDesktop` SDK, +and the solution would build both in Visual Studio and from the CLI. + +Unfortunately it threw an exception when I tried to run it, which I tracked down +to the [Ime class](https://github.com/VisualReCode/ICSharpCode.TextEditor/blob/master/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/Ime.cs). +I don't know what IME is, but apparently it was causing crashes in WOW64 processes +and .NET 4.0 didn't like it, so there was some code to disable it in those environments: + +```csharp +// For unknown reasons, the IME support is causing crashes when used in a WOW64 process +// or when used in .NET 4.0. We'll disable IME support in those cases. +var PROCESSOR_ARCHITEW6432 = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITEW6432"); +if (PROCESSOR_ARCHITEW6432 == "IA64" || PROCESSOR_ARCHITEW6432 == "AMD64" || Environment.OSVersion.Platform == PlatformID.Unix || Environment.Version >= new Version(major: 4, minor: 0)) + disableIME = true; +else + hIMEWnd = ImmGetDefaultIMEWnd(hWnd); +``` + +None of the conditions were `true` when running in .NET Core 3.1, so I changed it to set +`disableIME = true` unconditionally. [See the change here](https://github.com/VisualReCode/ICSharpCode.TextEditor/blob/master/NetCore31/src/ICSharpCode.TextEditor/Src/Gui/Ime.cs#L42). + +With that one change, the sample project builds and runs under .NET Core 3.1. + +Next job is to get the Test project to migrate cleanly as well. diff --git a/packages/NUnit.2.6.1/lib/nunit.framework.xml b/packages/NUnit.2.6.1/lib/nunit.framework.xml deleted file mode 100644 index 7b0e798..0000000 --- a/packages/NUnit.2.6.1/lib/nunit.framework.xml +++ /dev/null @@ -1,10892 +0,0 @@ - - - - nunit.framework - - - - - Attribute used to apply a category to a test - - - - - The name of the category - - - - - Construct attribute for a given category based on - a name. The name may not contain the characters ',', - '+', '-' or '!'. However, this is not checked in the - constructor since it would cause an error to arise at - as the test was loaded without giving a clear indication - of where the problem is located. The error is handled - in NUnitFramework.cs by marking the test as not - runnable. - - The name of the category - - - - Protected constructor uses the Type name as the name - of the category. - - - - - The name of the category - - - - - Used to mark a field for use as a datapoint when executing a theory - within the same fixture that requires an argument of the field's Type. - - - - - Used to mark an array as containing a set of datapoints to be used - executing a theory within the same fixture that requires an argument - of the Type of the array elements. - - - - - Attribute used to provide descriptive text about a - test case or fixture. - - - - - Construct the attribute - - Text describing the test - - - - Gets the test description - - - - - Enumeration indicating how the expected message parameter is to be used - - - - Expect an exact match - - - Expect a message containing the parameter string - - - Match the regular expression provided as a parameter - - - Expect a message that starts with the parameter string - - - - ExpectedExceptionAttribute - - - - - - Constructor for a non-specific exception - - - - - Constructor for a given type of exception - - The type of the expected exception - - - - Constructor for a given exception name - - The full name of the expected exception - - - - Gets or sets the expected exception type - - - - - Gets or sets the full Type name of the expected exception - - - - - Gets or sets the expected message text - - - - - Gets or sets the user message displayed in case of failure - - - - - Gets or sets the type of match to be performed on the expected message - - - - - Gets the name of a method to be used as an exception handler - - - - - ExplicitAttribute marks a test or test fixture so that it will - only be run if explicitly executed from the gui or command line - or if it is included by use of a filter. The test will not be - run simply because an enclosing suite is run. - - - - - Default constructor - - - - - Constructor with a reason - - The reason test is marked explicit - - - - The reason test is marked explicit - - - - - Attribute used to mark a test that is to be ignored. - Ignored tests result in a warning message when the - tests are run. - - - - - Constructs the attribute without giving a reason - for ignoring the test. - - - - - Constructs the attribute giving a reason for ignoring the test - - The reason for ignoring the test - - - - The reason for ignoring a test - - - - - Abstract base for Attributes that are used to include tests - in the test run based on environmental settings. - - - - - Constructor with no included items specified, for use - with named property syntax. - - - - - Constructor taking one or more included items - - Comma-delimited list of included items - - - - Name of the item that is needed in order for - a test to run. Multiple itemss may be given, - separated by a comma. - - - - - Name of the item to be excluded. Multiple items - may be given, separated by a comma. - - - - - The reason for including or excluding the test - - - - - PlatformAttribute is used to mark a test fixture or an - individual method as applying to a particular platform only. - - - - - Constructor with no platforms specified, for use - with named property syntax. - - - - - Constructor taking one or more platforms - - Comma-deliminted list of platforms - - - - CultureAttribute is used to mark a test fixture or an - individual method as applying to a particular Culture only. - - - - - Constructor with no cultures specified, for use - with named property syntax. - - - - - Constructor taking one or more cultures - - Comma-deliminted list of cultures - - - - Marks a test to use a combinatorial join of any argument data - provided. NUnit will create a test case for every combination of - the arguments provided. This can result in a large number of test - cases and so should be used judiciously. This is the default join - type, so the attribute need not be used except as documentation. - - - - - PropertyAttribute is used to attach information to a test as a name/value pair.. - - - - - Construct a PropertyAttribute with a name and string value - - The name of the property - The property value - - - - Construct a PropertyAttribute with a name and int value - - The name of the property - The property value - - - - Construct a PropertyAttribute with a name and double value - - The name of the property - The property value - - - - Constructor for derived classes that set the - property dictionary directly. - - - - - Constructor for use by derived classes that use the - name of the type as the property name. Derived classes - must ensure that the Type of the property value is - a standard type supported by the BCL. Any custom - types will cause a serialization Exception when - in the client. - - - - - Gets the property dictionary for this attribute - - - - - Default constructor - - - - - Marks a test to use pairwise join of any argument data provided. - NUnit will attempt too excercise every pair of argument values at - least once, using as small a number of test cases as it can. With - only two arguments, this is the same as a combinatorial join. - - - - - Default constructor - - - - - Marks a test to use a sequential join of any argument data - provided. NUnit will use arguements for each parameter in - sequence, generating test cases up to the largest number - of argument values provided and using null for any arguments - for which it runs out of values. Normally, this should be - used with the same number of arguments for each parameter. - - - - - Default constructor - - - - - Summary description for MaxTimeAttribute. - - - - - Construct a MaxTimeAttribute, given a time in milliseconds. - - The maximum elapsed time in milliseconds - - - - RandomAttribute is used to supply a set of random values - to a single parameter of a parameterized test. - - - - - ValuesAttribute is used to provide literal arguments for - an individual parameter of a test. - - - - - Abstract base class for attributes that apply to parameters - and supply data for the parameter. - - - - - Gets the data to be provided to the specified parameter - - - - - The collection of data to be returned. Must - be set by any derived attribute classes. - We use an object[] so that the individual - elements may have their type changed in GetData - if necessary. - - - - - Construct with one argument - - - - - - Construct with two arguments - - - - - - - Construct with three arguments - - - - - - - - Construct with an array of arguments - - - - - - Get the collection of values to be used as arguments - - - - - Construct a set of doubles from 0.0 to 1.0, - specifying only the count. - - - - - - Construct a set of doubles from min to max - - - - - - - - Construct a set of ints from min to max - - - - - - - - Get the collection of values to be used as arguments - - - - - RangeAttribute is used to supply a range of values to an - individual parameter of a parameterized test. - - - - - Construct a range of ints using default step of 1 - - - - - - - Construct a range of ints specifying the step size - - - - - - - - Construct a range of longs - - - - - - - - Construct a range of doubles - - - - - - - - Construct a range of floats - - - - - - - - RepeatAttribute may be applied to test case in order - to run it multiple times. - - - - - Construct a RepeatAttribute - - The number of times to run the test - - - - RequiredAddinAttribute may be used to indicate the names of any addins - that must be present in order to run some or all of the tests in an - assembly. If the addin is not loaded, the entire assembly is marked - as NotRunnable. - - - - - Initializes a new instance of the class. - - The required addin. - - - - Gets the name of required addin. - - The required addin name. - - - - Summary description for SetCultureAttribute. - - - - - Construct given the name of a culture - - - - - - Summary description for SetUICultureAttribute. - - - - - Construct given the name of a culture - - - - - - SetUpAttribute is used in a TestFixture to identify a method - that is called immediately before each test is run. It is - also used in a SetUpFixture to identify the method that is - called once, before any of the subordinate tests are run. - - - - - Attribute used to mark a class that contains one-time SetUp - and/or TearDown methods that apply to all the tests in a - namespace or an assembly. - - - - - Attribute used to mark a static (shared in VB) property - that returns a list of tests. - - - - - Attribute used in a TestFixture to identify a method that is - called immediately after each test is run. It is also used - in a SetUpFixture to identify the method that is called once, - after all subordinate tests have run. In either case, the method - is guaranteed to be called, even if an exception is thrown. - - - - - Provide actions to execute before and after tests. - - - - - When implemented by an attribute, this interface implemented to provide actions to execute before and after tests. - - - - - Executed before each test is run - - Provides details about the test that is going to be run. - - - - Executed after each test is run - - Provides details about the test that has just been run. - - - - Provides the target for the action attribute - - The target for the action attribute - - - - Adding this attribute to a method within a - class makes the method callable from the NUnit test runner. There is a property - called Description which is optional which you can provide a more detailed test - description. This class cannot be inherited. - - - - [TestFixture] - public class Fixture - { - [Test] - public void MethodToTest() - {} - - [Test(Description = "more detailed description")] - publc void TestDescriptionMethod() - {} - } - - - - - - Descriptive text for this test - - - - - TestCaseAttribute is used to mark parameterized test cases - and provide them with their arguments. - - - - - The ITestCaseData interface is implemented by a class - that is able to return complete testcases for use by - a parameterized test method. - - NOTE: This interface is used in both the framework - and the core, even though that results in two different - types. However, sharing the source code guarantees that - the various implementations will be compatible and that - the core is able to reflect successfully over the - framework implementations of ITestCaseData. - - - - - Gets the argument list to be provided to the test - - - - - Gets the expected result - - - - - Indicates whether a result has been specified. - This is necessary because the result may be - null, so it's value cannot be checked. - - - - - Gets the expected exception Type - - - - - Gets the FullName of the expected exception - - - - - Gets the name to be used for the test - - - - - Gets the description of the test - - - - - Gets a value indicating whether this is ignored. - - true if ignored; otherwise, false. - - - - Gets a value indicating whether this is explicit. - - true if explicit; otherwise, false. - - - - Gets the ignore reason. - - The ignore reason. - - - - Construct a TestCaseAttribute with a list of arguments. - This constructor is not CLS-Compliant - - - - - - Construct a TestCaseAttribute with a single argument - - - - - - Construct a TestCaseAttribute with a two arguments - - - - - - - Construct a TestCaseAttribute with a three arguments - - - - - - - - Gets the list of arguments to a test case - - - - - Gets or sets the expected result. - - The result. - - - - Gets the expected result. - - The result. - - - - Gets a flag indicating whether an expected - result has been set. - - - - - Gets a list of categories associated with this test; - - - - - Gets or sets the category associated with this test. - May be a single category or a comma-separated list. - - - - - Gets or sets the expected exception. - - The expected exception. - - - - Gets or sets the name the expected exception. - - The expected name of the exception. - - - - Gets or sets the expected message of the expected exception - - The expected message of the exception. - - - - Gets or sets the type of match to be performed on the expected message - - - - - Gets or sets the description. - - The description. - - - - Gets or sets the name of the test. - - The name of the test. - - - - Gets or sets the ignored status of the test - - - - - Gets or sets the ignored status of the test - - - - - Gets or sets the explicit status of the test - - - - - Gets or sets the reason for not running the test - - - - - Gets or sets the reason for not running the test. - Set has the side effect of marking the test as ignored. - - The ignore reason. - - - - FactoryAttribute indicates the source to be used to - provide test cases for a test method. - - - - - Construct with the name of the factory - for use with languages - that don't support params arrays. - - An array of the names of the factories that will provide data - - - - Construct with a Type and name - for use with languages - that don't support params arrays. - - The Type that will provide data - The name of the method, property or field that will provide data - - - - The name of a the method, property or fiend to be used as a source - - - - - A Type to be used as a source - - - - - Gets or sets the category associated with this test. - May be a single category or a comma-separated list. - - - - - [TestFixture] - public class ExampleClass - {} - - - - - Default constructor - - - - - Construct with a object[] representing a set of arguments. - In .NET 2.0, the arguments may later be separated into - type arguments and constructor arguments. - - - - - - Descriptive text for this fixture - - - - - Gets and sets the category for this fixture. - May be a comma-separated list of categories. - - - - - Gets a list of categories for this fixture - - - - - The arguments originally provided to the attribute - - - - - Gets or sets a value indicating whether this should be ignored. - - true if ignore; otherwise, false. - - - - Gets or sets the ignore reason. May set Ignored as a side effect. - - The ignore reason. - - - - Get or set the type arguments. If not set - explicitly, any leading arguments that are - Types are taken as type arguments. - - - - - Attribute used to identify a method that is - called before any tests in a fixture are run. - - - - - Attribute used to identify a method that is called after - all the tests in a fixture have run. The method is - guaranteed to be called, even if an exception is thrown. - - - - - Adding this attribute to a method within a - class makes the method callable from the NUnit test runner. There is a property - called Description which is optional which you can provide a more detailed test - description. This class cannot be inherited. - - - - [TestFixture] - public class Fixture - { - [Test] - public void MethodToTest() - {} - - [Test(Description = "more detailed description")] - publc void TestDescriptionMethod() - {} - } - - - - - - Used on a method, marks the test with a timeout value in milliseconds. - The test will be run in a separate thread and is cancelled if the timeout - is exceeded. Used on a method or assembly, sets the default timeout - for all contained test methods. - - - - - Construct a TimeoutAttribute given a time in milliseconds - - The timeout value in milliseconds - - - - Marks a test that must run in the STA, causing it - to run in a separate thread if necessary. - - On methods, you may also use STAThreadAttribute - to serve the same purpose. - - - - - Construct a RequiresSTAAttribute - - - - - Marks a test that must run in the MTA, causing it - to run in a separate thread if necessary. - - On methods, you may also use MTAThreadAttribute - to serve the same purpose. - - - - - Construct a RequiresMTAAttribute - - - - - Marks a test that must run on a separate thread. - - - - - Construct a RequiresThreadAttribute - - - - - Construct a RequiresThreadAttribute, specifying the apartment - - - - - ValueSourceAttribute indicates the source to be used to - provide data for one parameter of a test method. - - - - - Construct with the name of the factory - for use with languages - that don't support params arrays. - - The name of the data source to be used - - - - Construct with a Type and name - for use with languages - that don't support params arrays. - - The Type that will provide data - The name of the method, property or field that will provide data - - - - The name of a the method, property or fiend to be used as a source - - - - - A Type to be used as a source - - - - - AttributeExistsConstraint tests for the presence of a - specified attribute on a Type. - - - - - The Constraint class is the base of all built-in constraints - within NUnit. It provides the operator overloads used to combine - constraints. - - - - - The IConstraintExpression interface is implemented by all - complete and resolvable constraints and expressions. - - - - - Return the top-level constraint for this expression - - - - - - Static UnsetObject used to detect derived constraints - failing to set the actual value. - - - - - The actual value being tested against a constraint - - - - - The display name of this Constraint for use by ToString() - - - - - Argument fields used by ToString(); - - - - - The builder holding this constraint - - - - - Construct a constraint with no arguments - - - - - Construct a constraint with one argument - - - - - Construct a constraint with two arguments - - - - - Sets the ConstraintBuilder holding this constraint - - - - - Write the failure message to the MessageWriter provided - as an argument. The default implementation simply passes - the constraint and the actual value to the writer, which - then displays the constraint description and the value. - - Constraints that need to provide additional details, - such as where the error occured can override this. - - The MessageWriter on which to display the message - - - - Test whether the constraint is satisfied by a given value - - The value to be tested - True for success, false for failure - - - - Test whether the constraint is satisfied by an - ActualValueDelegate that returns the value to be tested. - The default implementation simply evaluates the delegate - but derived classes may override it to provide for delayed - processing. - - An ActualValueDelegate - True for success, false for failure - - - - Test whether the constraint is satisfied by a given reference. - The default implementation simply dereferences the value but - derived classes may override it to provide for delayed processing. - - A reference to the value to be tested - True for success, false for failure - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - Write the actual value for a failing constraint test to a - MessageWriter. The default implementation simply writes - the raw value of actual, leaving it to the writer to - perform any formatting. - - The writer on which the actual value is displayed - - - - Default override of ToString returns the constraint DisplayName - followed by any arguments within angle brackets. - - - - - - Returns the string representation of this constraint - - - - - This operator creates a constraint that is satisfied only if both - argument constraints are satisfied. - - - - - This operator creates a constraint that is satisfied if either - of the argument constraints is satisfied. - - - - - This operator creates a constraint that is satisfied if the - argument constraint is not satisfied. - - - - - Returns a DelayedConstraint with the specified delay time. - - The delay in milliseconds. - - - - - Returns a DelayedConstraint with the specified delay time - and polling interval. - - The delay in milliseconds. - The interval at which to test the constraint. - - - - - The display name of this Constraint for use by ToString(). - The default value is the name of the constraint with - trailing "Constraint" removed. Derived classes may set - this to another name in their constructors. - - - - - Returns a ConstraintExpression by appending And - to the current constraint. - - - - - Returns a ConstraintExpression by appending And - to the current constraint. - - - - - Returns a ConstraintExpression by appending Or - to the current constraint. - - - - - Class used to detect any derived constraints - that fail to set the actual value in their - Matches override. - - - - - Constructs an AttributeExistsConstraint for a specific attribute Type - - - - - - Tests whether the object provides the expected attribute. - - A Type, MethodInfo, or other ICustomAttributeProvider - True if the expected attribute is present, otherwise false - - - - Writes the description of the constraint to the specified writer - - - - - AttributeConstraint tests that a specified attribute is present - on a Type or other provider and that the value of the attribute - satisfies some other constraint. - - - - - Abstract base class used for prefixes - - - - - The base constraint - - - - - Construct given a base constraint - - - - - - Constructs an AttributeConstraint for a specified attriute - Type and base constraint. - - - - - - - Determines whether the Type or other provider has the - expected attribute and if its value matches the - additional constraint specified. - - - - - Writes a description of the attribute to the specified writer. - - - - - Writes the actual value supplied to the specified writer. - - - - - Returns a string representation of the constraint. - - - - - BasicConstraint is the abstract base for constraints that - perform a simple comparison to a constant value. - - - - - Initializes a new instance of the class. - - The expected. - The description. - - - - Test whether the constraint is satisfied by a given value - - The value to be tested - True for success, false for failure - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - NullConstraint tests that the actual value is null - - - - - Initializes a new instance of the class. - - - - - TrueConstraint tests that the actual value is true - - - - - Initializes a new instance of the class. - - - - - FalseConstraint tests that the actual value is false - - - - - Initializes a new instance of the class. - - - - - NaNConstraint tests that the actual value is a double or float NaN - - - - - Test that the actual value is an NaN - - - - - - - Write the constraint description to a specified writer - - - - - - BinaryConstraint is the abstract base of all constraints - that combine two other constraints in some fashion. - - - - - The first constraint being combined - - - - - The second constraint being combined - - - - - Construct a BinaryConstraint from two other constraints - - The first constraint - The second constraint - - - - AndConstraint succeeds only if both members succeed. - - - - - Create an AndConstraint from two other constraints - - The first constraint - The second constraint - - - - Apply both member constraints to an actual value, succeeding - succeeding only if both of them succeed. - - The actual value - True if the constraints both succeeded - - - - Write a description for this contraint to a MessageWriter - - The MessageWriter to receive the description - - - - Write the actual value for a failing constraint test to a - MessageWriter. The default implementation simply writes - the raw value of actual, leaving it to the writer to - perform any formatting. - - The writer on which the actual value is displayed - - - - OrConstraint succeeds if either member succeeds - - - - - Create an OrConstraint from two other constraints - - The first constraint - The second constraint - - - - Apply the member constraints to an actual value, succeeding - succeeding as soon as one of them succeeds. - - The actual value - True if either constraint succeeded - - - - Write a description for this contraint to a MessageWriter - - The MessageWriter to receive the description - - - - CollectionConstraint is the abstract base class for - constraints that operate on collections. - - - - - Construct an empty CollectionConstraint - - - - - Construct a CollectionConstraint - - - - - - Determines whether the specified enumerable is empty. - - The enumerable. - - true if the specified enumerable is empty; otherwise, false. - - - - - Test whether the constraint is satisfied by a given value - - The value to be tested - True for success, false for failure - - - - Protected method to be implemented by derived classes - - - - - - - CollectionItemsEqualConstraint is the abstract base class for all - collection constraints that apply some notion of item equality - as a part of their operation. - - - - - Construct an empty CollectionConstraint - - - - - Construct a CollectionConstraint - - - - - - Flag the constraint to use the supplied IComparer object. - - The IComparer object to use. - Self. - - - - Flag the constraint to use the supplied IComparer object. - - The IComparer object to use. - Self. - - - - Flag the constraint to use the supplied Comparison object. - - The IComparer object to use. - Self. - - - - Flag the constraint to use the supplied IEqualityComparer object. - - The IComparer object to use. - Self. - - - - Flag the constraint to use the supplied IEqualityComparer object. - - The IComparer object to use. - Self. - - - - Compares two collection members for equality - - - - - Return a new CollectionTally for use in making tests - - The collection to be included in the tally - - - - Flag the constraint to ignore case and return self. - - - - - EmptyCollectionConstraint tests whether a collection is empty. - - - - - Check that the collection is empty - - - - - - - Write the constraint description to a MessageWriter - - - - - - UniqueItemsConstraint tests whether all the items in a - collection are unique. - - - - - Check that all items are unique. - - - - - - - Write a description of this constraint to a MessageWriter - - - - - - CollectionContainsConstraint is used to test whether a collection - contains an expected object as a member. - - - - - Construct a CollectionContainsConstraint - - - - - - Test whether the expected item is contained in the collection - - - - - - - Write a descripton of the constraint to a MessageWriter - - - - - - CollectionEquivalentCOnstraint is used to determine whether two - collections are equivalent. - - - - - Construct a CollectionEquivalentConstraint - - - - - - Test whether two collections are equivalent - - - - - - - Write a description of this constraint to a MessageWriter - - - - - - CollectionSubsetConstraint is used to determine whether - one collection is a subset of another - - - - - Construct a CollectionSubsetConstraint - - The collection that the actual value is expected to be a subset of - - - - Test whether the actual collection is a subset of - the expected collection provided. - - - - - - - Write a description of this constraint to a MessageWriter - - - - - - CollectionOrderedConstraint is used to test whether a collection is ordered. - - - - - Construct a CollectionOrderedConstraint - - - - - Modifies the constraint to use an IComparer and returns self. - - - - - Modifies the constraint to use an IComparer<T> and returns self. - - - - - Modifies the constraint to use a Comparison<T> and returns self. - - - - - Modifies the constraint to test ordering by the value of - a specified property and returns self. - - - - - Test whether the collection is ordered - - - - - - - Write a description of the constraint to a MessageWriter - - - - - - Returns the string representation of the constraint. - - - - - - If used performs a reverse comparison - - - - - CollectionTally counts (tallies) the number of - occurences of each object in one or more enumerations. - - - - - Construct a CollectionTally object from a comparer and a collection - - - - - Try to remove an object from the tally - - The object to remove - True if successful, false if the object was not found - - - - Try to remove a set of objects from the tally - - The objects to remove - True if successful, false if any object was not found - - - - The number of objects remaining in the tally - - - - - ComparisonAdapter class centralizes all comparisons of - values in NUnit, adapting to the use of any provided - IComparer, IComparer<T> or Comparison<T> - - - - - Returns a ComparisonAdapter that wraps an IComparer - - - - - Returns a ComparisonAdapter that wraps an IComparer<T> - - - - - Returns a ComparisonAdapter that wraps a Comparison<T> - - - - - Compares two objects - - - - - Gets the default ComparisonAdapter, which wraps an - NUnitComparer object. - - - - - Construct a ComparisonAdapter for an IComparer - - - - - Compares two objects - - - - - - - - Construct a default ComparisonAdapter - - - - - ComparisonAdapter<T> extends ComparisonAdapter and - allows use of an IComparer<T> or Comparison<T> - to actually perform the comparison. - - - - - Construct a ComparisonAdapter for an IComparer<T> - - - - - Compare a Type T to an object - - - - - Construct a ComparisonAdapter for a Comparison<T> - - - - - Compare a Type T to an object - - - - - Abstract base class for constraints that compare values to - determine if one is greater than, equal to or less than - the other. This class supplies the Using modifiers. - - - - - ComparisonAdapter to be used in making the comparison - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class. - - - - - Modifies the constraint to use an IComparer and returns self - - - - - Modifies the constraint to use an IComparer<T> and returns self - - - - - Modifies the constraint to use a Comparison<T> and returns self - - - - - Delegate used to delay evaluation of the actual value - to be used in evaluating a constraint - - - - - ConstraintBuilder maintains the stacks that are used in - processing a ConstraintExpression. An OperatorStack - is used to hold operators that are waiting for their - operands to be reognized. a ConstraintStack holds - input constraints as well as the results of each - operator applied. - - - - - Initializes a new instance of the class. - - - - - Appends the specified operator to the expression by first - reducing the operator stack and then pushing the new - operator on the stack. - - The operator to push. - - - - Appends the specified constraint to the expresson by pushing - it on the constraint stack. - - The constraint to push. - - - - Sets the top operator right context. - - The right context. - - - - Reduces the operator stack until the topmost item - precedence is greater than or equal to the target precedence. - - The target precedence. - - - - Resolves this instance, returning a Constraint. If the builder - is not currently in a resolvable state, an exception is thrown. - - The resolved constraint - - - - Gets a value indicating whether this instance is resolvable. - - - true if this instance is resolvable; otherwise, false. - - - - - OperatorStack is a type-safe stack for holding ConstraintOperators - - - - - Initializes a new instance of the class. - - The builder. - - - - Pushes the specified operator onto the stack. - - The op. - - - - Pops the topmost operator from the stack. - - - - - - Gets a value indicating whether this is empty. - - true if empty; otherwise, false. - - - - Gets the topmost operator without modifying the stack. - - The top. - - - - ConstraintStack is a type-safe stack for holding Constraints - - - - - Initializes a new instance of the class. - - The builder. - - - - Pushes the specified constraint. As a side effect, - the constraint's builder field is set to the - ConstraintBuilder owning this stack. - - The constraint. - - - - Pops this topmost constrait from the stack. - As a side effect, the constraint's builder - field is set to null. - - - - - - Gets a value indicating whether this is empty. - - true if empty; otherwise, false. - - - - Gets the topmost constraint without modifying the stack. - - The topmost constraint - - - - ConstraintExpression represents a compound constraint in the - process of being constructed from a series of syntactic elements. - - Individual elements are appended to the expression as they are - reognized. Once an actual Constraint is appended, the expression - returns a resolvable Constraint. - - - - - ConstraintExpressionBase is the abstract base class for the - ConstraintExpression class, which represents a - compound constraint in the process of being constructed - from a series of syntactic elements. - - NOTE: ConstraintExpressionBase is separate because the - ConstraintExpression class was generated in earlier - versions of NUnit. The two classes may be combined - in a future version. - - - - - The ConstraintBuilder holding the elements recognized so far - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the - class passing in a ConstraintBuilder, which may be pre-populated. - - The builder. - - - - Returns a string representation of the expression as it - currently stands. This should only be used for testing, - since it has the side-effect of resolving the expression. - - - - - - Appends an operator to the expression and returns the - resulting expression itself. - - - - - Appends a self-resolving operator to the expression and - returns a new ResolvableConstraintExpression. - - - - - Appends a constraint to the expression and returns that - constraint, which is associated with the current state - of the expression being built. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the - class passing in a ConstraintBuilder, which may be pre-populated. - - The builder. - - - - Returns a ConstraintExpression, which will apply - the following constraint to all members of a collection, - succeeding only if a specified number of them succeed. - - - - - Returns a new PropertyConstraintExpression, which will either - test for the existence of the named property on the object - being tested or apply any following constraint to that property. - - - - - Returns a new AttributeConstraint checking for the - presence of a particular attribute on an object. - - - - - Returns a new AttributeConstraint checking for the - presence of a particular attribute on an object. - - - - - Returns the constraint provided as an argument - used to allow custom - custom constraints to easily participate in the syntax. - - - - - Returns the constraint provided as an argument - used to allow custom - custom constraints to easily participate in the syntax. - - - - - Returns a constraint that tests two items for equality - - - - - Returns a constraint that tests that two references are the same object - - - - - Returns a constraint that tests whether the - actual value is greater than the suppled argument - - - - - Returns a constraint that tests whether the - actual value is greater than or equal to the suppled argument - - - - - Returns a constraint that tests whether the - actual value is greater than or equal to the suppled argument - - - - - Returns a constraint that tests whether the - actual value is less than the suppled argument - - - - - Returns a constraint that tests whether the - actual value is less than or equal to the suppled argument - - - - - Returns a constraint that tests whether the - actual value is less than or equal to the suppled argument - - - - - Returns a constraint that tests whether the actual - value is of the exact type supplied as an argument. - - - - - Returns a constraint that tests whether the actual - value is of the exact type supplied as an argument. - - - - - Returns a constraint that tests whether the actual value - is of the type supplied as an argument or a derived type. - - - - - Returns a constraint that tests whether the actual value - is of the type supplied as an argument or a derived type. - - - - - Returns a constraint that tests whether the actual value - is of the type supplied as an argument or a derived type. - - - - - Returns a constraint that tests whether the actual value - is of the type supplied as an argument or a derived type. - - - - - Returns a constraint that tests whether the actual value - is assignable from the type supplied as an argument. - - - - - Returns a constraint that tests whether the actual value - is assignable from the type supplied as an argument. - - - - - Returns a constraint that tests whether the actual value - is assignable from the type supplied as an argument. - - - - - Returns a constraint that tests whether the actual value - is assignable from the type supplied as an argument. - - - - - Returns a constraint that tests whether the actual value - is a collection containing the same elements as the - collection supplied as an argument. - - - - - Returns a constraint that tests whether the actual value - is a subset of the collection supplied as an argument. - - - - - Returns a new CollectionContainsConstraint checking for the - presence of a particular object in the collection. - - - - - Returns a new CollectionContainsConstraint checking for the - presence of a particular object in the collection. - - - - - Returns a new ContainsConstraint. This constraint - will, in turn, make use of the appropriate second-level - constraint, depending on the type of the actual argument. - This overload is only used if the item sought is a string, - since any other type implies that we are looking for a - collection member. - - - - - Returns a constraint that succeeds if the actual - value contains the substring supplied as an argument. - - - - - Returns a constraint that succeeds if the actual - value contains the substring supplied as an argument. - - - - - Returns a constraint that succeeds if the actual - value starts with the substring supplied as an argument. - - - - - Returns a constraint that succeeds if the actual - value starts with the substring supplied as an argument. - - - - - Returns a constraint that succeeds if the actual - value ends with the substring supplied as an argument. - - - - - Returns a constraint that succeeds if the actual - value ends with the substring supplied as an argument. - - - - - Returns a constraint that succeeds if the actual - value matches the Regex pattern supplied as an argument. - - - - - Returns a constraint that succeeds if the actual - value matches the Regex pattern supplied as an argument. - - - - - Returns a constraint that tests whether the path provided - is the same as an expected path after canonicalization. - - - - - Returns a constraint that tests whether the path provided - is the same path or under an expected path after canonicalization. - - - - - Returns a constraint that tests whether the path provided - is the same path or under an expected path after canonicalization. - - - - - Returns a constraint that tests whether the actual value falls - within a specified range. - - - - - Returns a ConstraintExpression that negates any - following constraint. - - - - - Returns a ConstraintExpression that negates any - following constraint. - - - - - Returns a ConstraintExpression, which will apply - the following constraint to all members of a collection, - succeeding if all of them succeed. - - - - - Returns a ConstraintExpression, which will apply - the following constraint to all members of a collection, - succeeding if at least one of them succeeds. - - - - - Returns a ConstraintExpression, which will apply - the following constraint to all members of a collection, - succeeding if all of them fail. - - - - - Returns a new ConstraintExpression, which will apply the following - constraint to the Length property of the object being tested. - - - - - Returns a new ConstraintExpression, which will apply the following - constraint to the Count property of the object being tested. - - - - - Returns a new ConstraintExpression, which will apply the following - constraint to the Message property of the object being tested. - - - - - Returns a new ConstraintExpression, which will apply the following - constraint to the InnerException property of the object being tested. - - - - - With is currently a NOP - reserved for future use. - - - - - Returns a constraint that tests for null - - - - - Returns a constraint that tests for True - - - - - Returns a constraint that tests for False - - - - - Returns a constraint that tests for a positive value - - - - - Returns a constraint that tests for a negative value - - - - - Returns a constraint that tests for NaN - - - - - Returns a constraint that tests for empty - - - - - Returns a constraint that tests whether a collection - contains all unique items. - - - - - Returns a constraint that tests whether an object graph is serializable in binary format. - - - - - Returns a constraint that tests whether an object graph is serializable in xml format. - - - - - Returns a constraint that tests whether a collection is ordered - - - - - Helper class with properties and methods that supply - a number of constraints used in Asserts. - - - - - Returns a ConstraintExpression, which will apply - the following constraint to all members of a collection, - succeeding only if a specified number of them succeed. - - - - - Returns a new PropertyConstraintExpression, which will either - test for the existence of the named property on the object - being tested or apply any following constraint to that property. - - - - - Returns a new AttributeConstraint checking for the - presence of a particular attribute on an object. - - - - - Returns a new AttributeConstraint checking for the - presence of a particular attribute on an object. - - - - - Returns a constraint that tests two items for equality - - - - - Returns a constraint that tests that two references are the same object - - - - - Returns a constraint that tests whether the - actual value is greater than the suppled argument - - - - - Returns a constraint that tests whether the - actual value is greater than or equal to the suppled argument - - - - - Returns a constraint that tests whether the - actual value is greater than or equal to the suppled argument - - - - - Returns a constraint that tests whether the - actual value is less than the suppled argument - - - - - Returns a constraint that tests whether the - actual value is less than or equal to the suppled argument - - - - - Returns a constraint that tests whether the - actual value is less than or equal to the suppled argument - - - - - Returns a constraint that tests whether the actual - value is of the exact type supplied as an argument. - - - - - Returns a constraint that tests whether the actual - value is of the exact type supplied as an argument. - - - - - Returns a constraint that tests whether the actual value - is of the type supplied as an argument or a derived type. - - - - - Returns a constraint that tests whether the actual value - is of the type supplied as an argument or a derived type. - - - - - Returns a constraint that tests whether the actual value - is of the type supplied as an argument or a derived type. - - - - - Returns a constraint that tests whether the actual value - is of the type supplied as an argument or a derived type. - - - - - Returns a constraint that tests whether the actual value - is assignable from the type supplied as an argument. - - - - - Returns a constraint that tests whether the actual value - is assignable from the type supplied as an argument. - - - - - Returns a constraint that tests whether the actual value - is assignable from the type supplied as an argument. - - - - - Returns a constraint that tests whether the actual value - is assignable from the type supplied as an argument. - - - - - Returns a constraint that tests whether the actual value - is a collection containing the same elements as the - collection supplied as an argument. - - - - - Returns a constraint that tests whether the actual value - is a subset of the collection supplied as an argument. - - - - - Returns a new CollectionContainsConstraint checking for the - presence of a particular object in the collection. - - - - - Returns a new CollectionContainsConstraint checking for the - presence of a particular object in the collection. - - - - - Returns a new ContainsConstraint. This constraint - will, in turn, make use of the appropriate second-level - constraint, depending on the type of the actual argument. - This overload is only used if the item sought is a string, - since any other type implies that we are looking for a - collection member. - - - - - Returns a constraint that succeeds if the actual - value contains the substring supplied as an argument. - - - - - Returns a constraint that succeeds if the actual - value contains the substring supplied as an argument. - - - - - Returns a constraint that fails if the actual - value contains the substring supplied as an argument. - - - - - Returns a constraint that succeeds if the actual - value starts with the substring supplied as an argument. - - - - - Returns a constraint that succeeds if the actual - value starts with the substring supplied as an argument. - - - - - Returns a constraint that fails if the actual - value starts with the substring supplied as an argument. - - - - - Returns a constraint that succeeds if the actual - value ends with the substring supplied as an argument. - - - - - Returns a constraint that succeeds if the actual - value ends with the substring supplied as an argument. - - - - - Returns a constraint that fails if the actual - value ends with the substring supplied as an argument. - - - - - Returns a constraint that succeeds if the actual - value matches the Regex pattern supplied as an argument. - - - - - Returns a constraint that succeeds if the actual - value matches the Regex pattern supplied as an argument. - - - - - Returns a constraint that fails if the actual - value matches the pattern supplied as an argument. - - - - - Returns a constraint that tests whether the path provided - is the same as an expected path after canonicalization. - - - - - Returns a constraint that tests whether the path provided - is the same path or under an expected path after canonicalization. - - - - - Returns a constraint that tests whether the path provided - is the same path or under an expected path after canonicalization. - - - - - Returns a constraint that tests whether the actual value falls - within a specified range. - - - - - Returns a ConstraintExpression that negates any - following constraint. - - - - - Returns a ConstraintExpression that negates any - following constraint. - - - - - Returns a ConstraintExpression, which will apply - the following constraint to all members of a collection, - succeeding if all of them succeed. - - - - - Returns a ConstraintExpression, which will apply - the following constraint to all members of a collection, - succeeding if at least one of them succeeds. - - - - - Returns a ConstraintExpression, which will apply - the following constraint to all members of a collection, - succeeding if all of them fail. - - - - - Returns a new ConstraintExpression, which will apply the following - constraint to the Length property of the object being tested. - - - - - Returns a new ConstraintExpression, which will apply the following - constraint to the Count property of the object being tested. - - - - - Returns a new ConstraintExpression, which will apply the following - constraint to the Message property of the object being tested. - - - - - Returns a new ConstraintExpression, which will apply the following - constraint to the InnerException property of the object being tested. - - - - - Returns a constraint that tests for null - - - - - Returns a constraint that tests for True - - - - - Returns a constraint that tests for False - - - - - Returns a constraint that tests for a positive value - - - - - Returns a constraint that tests for a negative value - - - - - Returns a constraint that tests for NaN - - - - - Returns a constraint that tests for empty - - - - - Returns a constraint that tests whether a collection - contains all unique items. - - - - - Returns a constraint that tests whether an object graph is serializable in binary format. - - - - - Returns a constraint that tests whether an object graph is serializable in xml format. - - - - - Returns a constraint that tests whether a collection is ordered - - - - - The ConstraintOperator class is used internally by a - ConstraintBuilder to represent an operator that - modifies or combines constraints. - - Constraint operators use left and right precedence - values to determine whether the top operator on the - stack should be reduced before pushing a new operator. - - - - - The precedence value used when the operator - is about to be pushed to the stack. - - - - - The precedence value used when the operator - is on the top of the stack. - - - - - Reduce produces a constraint from the operator and - any arguments. It takes the arguments from the constraint - stack and pushes the resulting constraint on it. - - - - - - The syntax element preceding this operator - - - - - The syntax element folowing this operator - - - - - The precedence value used when the operator - is about to be pushed to the stack. - - - - - The precedence value used when the operator - is on the top of the stack. - - - - - PrefixOperator takes a single constraint and modifies - it's action in some way. - - - - - Reduce produces a constraint from the operator and - any arguments. It takes the arguments from the constraint - stack and pushes the resulting constraint on it. - - - - - - Returns the constraint created by applying this - prefix to another constraint. - - - - - - - Negates the test of the constraint it wraps. - - - - - Constructs a new NotOperator - - - - - Returns a NotConstraint applied to its argument. - - - - - Abstract base for operators that indicate how to - apply a constraint to items in a collection. - - - - - Constructs a CollectionOperator - - - - - Represents a constraint that succeeds if all the - members of a collection match a base constraint. - - - - - Returns a constraint that will apply the argument - to the members of a collection, succeeding if - they all succeed. - - - - - Represents a constraint that succeeds if any of the - members of a collection match a base constraint. - - - - - Returns a constraint that will apply the argument - to the members of a collection, succeeding if - any of them succeed. - - - - - Represents a constraint that succeeds if none of the - members of a collection match a base constraint. - - - - - Returns a constraint that will apply the argument - to the members of a collection, succeeding if - none of them succeed. - - - - - Represents a constraint that succeeds if the specified - count of members of a collection match a base constraint. - - - - - Construct an ExactCountOperator for a specified count - - The expected count - - - - Returns a constraint that will apply the argument - to the members of a collection, succeeding if - none of them succeed. - - - - - Represents a constraint that simply wraps the - constraint provided as an argument, without any - further functionality, but which modifes the - order of evaluation because of its precedence. - - - - - Constructor for the WithOperator - - - - - Returns a constraint that wraps its argument - - - - - Abstract base class for operators that are able to reduce to a - constraint whether or not another syntactic element follows. - - - - - Operator used to test for the presence of a named Property - on an object and optionally apply further tests to the - value of that property. - - - - - Constructs a PropOperator for a particular named property - - - - - Reduce produces a constraint from the operator and - any arguments. It takes the arguments from the constraint - stack and pushes the resulting constraint on it. - - - - - - Gets the name of the property to which the operator applies - - - - - Operator that tests for the presence of a particular attribute - on a type and optionally applies further tests to the attribute. - - - - - Construct an AttributeOperator for a particular Type - - The Type of attribute tested - - - - Reduce produces a constraint from the operator and - any arguments. It takes the arguments from the constraint - stack and pushes the resulting constraint on it. - - - - - Operator that tests that an exception is thrown and - optionally applies further tests to the exception. - - - - - Construct a ThrowsOperator - - - - - Reduce produces a constraint from the operator and - any arguments. It takes the arguments from the constraint - stack and pushes the resulting constraint on it. - - - - - Abstract base class for all binary operators - - - - - Reduce produces a constraint from the operator and - any arguments. It takes the arguments from the constraint - stack and pushes the resulting constraint on it. - - - - - - Abstract method that produces a constraint by applying - the operator to its left and right constraint arguments. - - - - - Gets the left precedence of the operator - - - - - Gets the right precedence of the operator - - - - - Operator that requires both it's arguments to succeed - - - - - Construct an AndOperator - - - - - Apply the operator to produce an AndConstraint - - - - - Operator that requires at least one of it's arguments to succeed - - - - - Construct an OrOperator - - - - - Apply the operator to produce an OrConstraint - - - - - ContainsConstraint tests a whether a string contains a substring - or a collection contains an object. It postpones the decision of - which test to use until the type of the actual argument is known. - This allows testing whether a string is contained in a collection - or as a substring of another string using the same syntax. - - - - - Initializes a new instance of the class. - - The expected. - - - - Test whether the constraint is satisfied by a given value - - The value to be tested - True for success, false for failure - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - Flag the constraint to use the supplied IComparer object. - - The IComparer object to use. - Self. - - - - Flag the constraint to use the supplied IComparer object. - - The IComparer object to use. - Self. - - - - Flag the constraint to use the supplied Comparison object. - - The IComparer object to use. - Self. - - - - Flag the constraint to use the supplied IEqualityComparer object. - - The IComparer object to use. - Self. - - - - Flag the constraint to use the supplied IEqualityComparer object. - - The IComparer object to use. - Self. - - - - Flag the constraint to ignore case and return self. - - - - - Applies a delay to the match so that a match can be evaluated in the future. - - - - - Creates a new DelayedConstraint - - The inner constraint two decorate - The time interval after which the match is performed - If the value of is less than 0 - - - - Creates a new DelayedConstraint - - The inner constraint two decorate - The time interval after which the match is performed - The time interval used for polling - If the value of is less than 0 - - - - Test whether the constraint is satisfied by a given value - - The value to be tested - True for if the base constraint fails, false if it succeeds - - - - Test whether the constraint is satisfied by a delegate - - The delegate whose value is to be tested - True for if the base constraint fails, false if it succeeds - - - - Test whether the constraint is satisfied by a given reference. - Overridden to wait for the specified delay period before - calling the base constraint with the dereferenced value. - - A reference to the value to be tested - True for success, false for failure - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - Write the actual value for a failing constraint test to a MessageWriter. - - The writer on which the actual value is displayed - - - - Returns the string representation of the constraint. - - - - - EmptyDirectoryConstraint is used to test that a directory is empty - - - - - Test whether the constraint is satisfied by a given value - - The value to be tested - True for success, false for failure - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - Write the actual value for a failing constraint test to a - MessageWriter. The default implementation simply writes - the raw value of actual, leaving it to the writer to - perform any formatting. - - The writer on which the actual value is displayed - - - - EmptyConstraint tests a whether a string or collection is empty, - postponing the decision about which test is applied until the - type of the actual argument is known. - - - - - Test whether the constraint is satisfied by a given value - - The value to be tested - True for success, false for failure - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - EqualConstraint is able to compare an actual value with the - expected value provided in its constructor. Two objects are - considered equal if both are null, or if both have the same - value. NUnit has special semantics for some object types. - - - - - If true, strings in error messages will be clipped - - - - - NUnitEqualityComparer used to test equality. - - - - - Initializes a new instance of the class. - - The expected value. - - - - Flag the constraint to use a tolerance when determining equality. - - Tolerance value to be used - Self. - - - - Flag the constraint to use the supplied IComparer object. - - The IComparer object to use. - Self. - - - - Flag the constraint to use the supplied IComparer object. - - The IComparer object to use. - Self. - - - - Flag the constraint to use the supplied IComparer object. - - The IComparer object to use. - Self. - - - - Flag the constraint to use the supplied Comparison object. - - The IComparer object to use. - Self. - - - - Flag the constraint to use the supplied IEqualityComparer object. - - The IComparer object to use. - Self. - - - - Flag the constraint to use the supplied IEqualityComparer object. - - The IComparer object to use. - Self. - - - - Test whether the constraint is satisfied by a given value - - The value to be tested - True for success, false for failure - - - - Write a failure message. Overridden to provide custom - failure messages for EqualConstraint. - - The MessageWriter to write to - - - - Write description of this constraint - - The MessageWriter to write to - - - - Display the failure information for two collections that did not match. - - The MessageWriter on which to display - The expected collection. - The actual collection - The depth of this failure in a set of nested collections - - - - Displays a single line showing the types and sizes of the expected - and actual enumerations, collections or arrays. If both are identical, - the value is only shown once. - - The MessageWriter on which to display - The expected collection or array - The actual collection or array - The indentation level for the message line - - - - Displays a single line showing the point in the expected and actual - arrays at which the comparison failed. If the arrays have different - structures or dimensions, both values are shown. - - The MessageWriter on which to display - The expected array - The actual array - Index of the failure point in the underlying collections - The indentation level for the message line - - - - Display the failure information for two IEnumerables that did not match. - - The MessageWriter on which to display - The expected enumeration. - The actual enumeration - The depth of this failure in a set of nested collections - - - - Flag the constraint to ignore case and return self. - - - - - Flag the constraint to suppress string clipping - and return self. - - - - - Flag the constraint to compare arrays as collections - and return self. - - - - - Switches the .Within() modifier to interpret its tolerance as - a distance in representable values (see remarks). - - Self. - - Ulp stands for "unit in the last place" and describes the minimum - amount a given value can change. For any integers, an ulp is 1 whole - digit. For floating point values, the accuracy of which is better - for smaller numbers and worse for larger numbers, an ulp depends - on the size of the number. Using ulps for comparison of floating - point results instead of fixed tolerances is safer because it will - automatically compensate for the added inaccuracy of larger numbers. - - - - - Switches the .Within() modifier to interpret its tolerance as - a percentage that the actual values is allowed to deviate from - the expected value. - - Self - - - - Causes the tolerance to be interpreted as a TimeSpan in days. - - Self - - - - Causes the tolerance to be interpreted as a TimeSpan in hours. - - Self - - - - Causes the tolerance to be interpreted as a TimeSpan in minutes. - - Self - - - - Causes the tolerance to be interpreted as a TimeSpan in seconds. - - Self - - - - Causes the tolerance to be interpreted as a TimeSpan in milliseconds. - - Self - - - - Causes the tolerance to be interpreted as a TimeSpan in clock ticks. - - Self - - - - EqualityAdapter class handles all equality comparisons - that use an IEqualityComparer, IEqualityComparer<T> - or a ComparisonAdapter. - - - - - Compares two objects, returning true if they are equal - - - - - Returns true if the two objects can be compared by this adapter. - The base adapter cannot handle IEnumerables except for strings. - - - - - Returns an EqualityAdapter that wraps an IComparer. - - - - - Returns an EqualityAdapter that wraps an IEqualityComparer. - - - - - Returns an EqualityAdapter that wraps an IEqualityComparer<T>. - - - - - Returns an EqualityAdapter that wraps an IComparer<T>. - - - - - Returns an EqualityAdapter that wraps a Comparison<T>. - - - - - EqualityAdapter that wraps an IComparer. - - - - - Returns true if the two objects can be compared by this adapter. - Generic adapter requires objects of the specified type. - - - - - EqualityAdapter that wraps an IComparer. - - - - Helper routines for working with floating point numbers - - - The floating point comparison code is based on this excellent article: - http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm - - - "ULP" means Unit in the Last Place and in the context of this library refers to - the distance between two adjacent floating point numbers. IEEE floating point - numbers can only represent a finite subset of natural numbers, with greater - accuracy for smaller numbers and lower accuracy for very large numbers. - - - If a comparison is allowed "2 ulps" of deviation, that means the values are - allowed to deviate by up to 2 adjacent floating point values, which might be - as low as 0.0000001 for small numbers or as high as 10.0 for large numbers. - - - - - Compares two floating point values for equality - First floating point value to be compared - Second floating point value t be compared - - Maximum number of representable floating point values that are allowed to - be between the left and the right floating point values - - True if both numbers are equal or close to being equal - - - Floating point values can only represent a finite subset of natural numbers. - For example, the values 2.00000000 and 2.00000024 can be stored in a float, - but nothing inbetween them. - - - This comparison will count how many possible floating point values are between - the left and the right number. If the number of possible values between both - numbers is less than or equal to maxUlps, then the numbers are considered as - being equal. - - - Implementation partially follows the code outlined here: - http://www.anttirt.net/2007/08/19/proper-floating-point-comparisons/ - - - - - Compares two double precision floating point values for equality - First double precision floating point value to be compared - Second double precision floating point value t be compared - - Maximum number of representable double precision floating point values that are - allowed to be between the left and the right double precision floating point values - - True if both numbers are equal or close to being equal - - - Double precision floating point values can only represent a limited series of - natural numbers. For example, the values 2.0000000000000000 and 2.0000000000000004 - can be stored in a double, but nothing inbetween them. - - - This comparison will count how many possible double precision floating point - values are between the left and the right number. If the number of possible - values between both numbers is less than or equal to maxUlps, then the numbers - are considered as being equal. - - - Implementation partially follows the code outlined here: - http://www.anttirt.net/2007/08/19/proper-floating-point-comparisons/ - - - - - - Reinterprets the memory contents of a floating point value as an integer value - - - Floating point value whose memory contents to reinterpret - - - The memory contents of the floating point value interpreted as an integer - - - - - Reinterprets the memory contents of a double precision floating point - value as an integer value - - - Double precision floating point value whose memory contents to reinterpret - - - The memory contents of the double precision floating point value - interpreted as an integer - - - - - Reinterprets the memory contents of an integer as a floating point value - - Integer value whose memory contents to reinterpret - - The memory contents of the integer value interpreted as a floating point value - - - - - Reinterprets the memory contents of an integer value as a double precision - floating point value - - Integer whose memory contents to reinterpret - - The memory contents of the integer interpreted as a double precision - floating point value - - - - Union of a floating point variable and an integer - - - The union's value as a floating point variable - - - The union's value as an integer - - - The union's value as an unsigned integer - - - Union of a double precision floating point variable and a long - - - The union's value as a double precision floating point variable - - - The union's value as a long - - - The union's value as an unsigned long - - - - Tests whether a value is greater than the value supplied to its constructor - - - - - The value against which a comparison is to be made - - - - - Initializes a new instance of the class. - - The expected value. - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - Test whether the constraint is satisfied by a given value - - The value to be tested - True for success, false for failure - - - - Tests whether a value is greater than or equal to the value supplied to its constructor - - - - - The value against which a comparison is to be made - - - - - Initializes a new instance of the class. - - The expected value. - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - Test whether the constraint is satisfied by a given value - - The value to be tested - True for success, false for failure - - - - Tests whether a value is less than the value supplied to its constructor - - - - - The value against which a comparison is to be made - - - - - Initializes a new instance of the class. - - The expected value. - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - Test whether the constraint is satisfied by a given value - - The value to be tested - True for success, false for failure - - - - Tests whether a value is less than or equal to the value supplied to its constructor - - - - - The value against which a comparison is to be made - - - - - Initializes a new instance of the class. - - The expected value. - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - Test whether the constraint is satisfied by a given value - - The value to be tested - True for success, false for failure - - - - MessageWriter is the abstract base for classes that write - constraint descriptions and messages in some form. The - class has separate methods for writing various components - of a message, allowing implementations to tailor the - presentation as needed. - - - - - Construct a MessageWriter given a culture - - - - - Method to write single line message with optional args, usually - written to precede the general failure message. - - The message to be written - Any arguments used in formatting the message - - - - Method to write single line message with optional args, usually - written to precede the general failure message, at a givel - indentation level. - - The indentation level of the message - The message to be written - Any arguments used in formatting the message - - - - Display Expected and Actual lines for a constraint. This - is called by MessageWriter's default implementation of - WriteMessageTo and provides the generic two-line display. - - The constraint that failed - - - - Display Expected and Actual lines for given values. This - method may be called by constraints that need more control over - the display of actual and expected values than is provided - by the default implementation. - - The expected value - The actual value causing the failure - - - - Display Expected and Actual lines for given values, including - a tolerance value on the Expected line. - - The expected value - The actual value causing the failure - The tolerance within which the test was made - - - - Display the expected and actual string values on separate lines. - If the mismatch parameter is >=0, an additional line is displayed - line containing a caret that points to the mismatch point. - - The expected string value - The actual string value - The point at which the strings don't match or -1 - If true, case is ignored in locating the point where the strings differ - If true, the strings should be clipped to fit the line - - - - Writes the text for a connector. - - The connector. - - - - Writes the text for a predicate. - - The predicate. - - - - Writes the text for an expected value. - - The expected value. - - - - Writes the text for a modifier - - The modifier. - - - - Writes the text for an actual value. - - The actual value. - - - - Writes the text for a generalized value. - - The value. - - - - Writes the text for a collection value, - starting at a particular point, to a max length - - The collection containing elements to write. - The starting point of the elements to write - The maximum number of elements to write - - - - Abstract method to get the max line length - - - - - Static methods used in creating messages - - - - - Static string used when strings are clipped - - - - - Returns the representation of a type as used in NUnitLite. - This is the same as Type.ToString() except for arrays, - which are displayed with their declared sizes. - - - - - - - Converts any control characters in a string - to their escaped representation. - - The string to be converted - The converted string - - - - Return the a string representation for a set of indices into an array - - Array of indices for which a string is needed - - - - Get an array of indices representing the point in a enumerable, - collection or array corresponding to a single int index into the - collection. - - The collection to which the indices apply - Index in the collection - Array of indices - - - - Clip a string to a given length, starting at a particular offset, returning the clipped - string with ellipses representing the removed parts - - The string to be clipped - The maximum permitted length of the result string - The point at which to start clipping - The clipped string - - - - Clip the expected and actual strings in a coordinated fashion, - so that they may be displayed together. - - - - - - - - - Shows the position two strings start to differ. Comparison - starts at the start index. - - The expected string - The actual string - The index in the strings at which comparison should start - Boolean indicating whether case should be ignored - -1 if no mismatch found, or the index where mismatch found - - - - The Numerics class contains common operations on numeric values. - - - - - Checks the type of the object, returning true if - the object is a numeric type. - - The object to check - true if the object is a numeric type - - - - Checks the type of the object, returning true if - the object is a floating point numeric type. - - The object to check - true if the object is a floating point numeric type - - - - Checks the type of the object, returning true if - the object is a fixed point numeric type. - - The object to check - true if the object is a fixed point numeric type - - - - Test two numeric values for equality, performing the usual numeric - conversions and using a provided or default tolerance. If the tolerance - provided is Empty, this method may set it to a default tolerance. - - The expected value - The actual value - A reference to the tolerance in effect - True if the values are equal - - - - Compare two numeric values, performing the usual numeric conversions. - - The expected value - The actual value - The relationship of the values to each other - - - - NUnitComparer encapsulates NUnit's default behavior - in comparing two objects. - - - - - Compares two objects - - - - - - - - Returns the default NUnitComparer. - - - - - Generic version of NUnitComparer - - - - - - Compare two objects of the same type - - - - - NUnitEqualityComparer encapsulates NUnit's handling of - equality tests between objects. - - - - - - - - - - Compares two objects for equality within a tolerance - - The first object to compare - The second object to compare - The tolerance to use in the comparison - - - - - If true, all string comparisons will ignore case - - - - - If true, arrays will be treated as collections, allowing - those of different dimensions to be compared - - - - - Comparison objects used in comparisons for some constraints. - - - - - Compares two objects for equality within a tolerance. - - - - - Helper method to compare two arrays - - - - - Method to compare two DirectoryInfo objects - - first directory to compare - second directory to compare - true if equivalent, false if not - - - - Returns the default NUnitEqualityComparer - - - - - Gets and sets a flag indicating whether case should - be ignored in determining equality. - - - - - Gets and sets a flag indicating that arrays should be - compared as collections, without regard to their shape. - - - - - Gets and sets an external comparer to be used to - test for equality. It is applied to members of - collections, in place of NUnit's own logic. - - - - - Gets the list of failure points for the last Match performed. - - - - - FailurePoint class represents one point of failure - in an equality test. - - - - - The location of the failure - - - - - The expected value - - - - - The actual value - - - - - Indicates whether the expected value is valid - - - - - Indicates whether the actual value is valid - - - - - PathConstraint serves as the abstract base of constraints - that operate on paths and provides several helper methods. - - - - - The expected path used in the constraint - - - - - The actual path being tested - - - - - Flag indicating whether a caseInsensitive comparison should be made - - - - - Construct a PathConstraint for a give expected path - - The expected path - - - - Test whether the constraint is satisfied by a given value - - The value to be tested - True for success, false for failure - - - - Returns true if the expected path and actual path match - - - - - Returns the string representation of this constraint - - - - - Canonicalize the provided path - - - The path in standardized form - - - - Test whether two paths are the same - - The first path - The second path - Indicates whether case should be ignored - - - - - Test whether one path is under another path - - The first path - supposed to be the parent path - The second path - supposed to be the child path - Indicates whether case should be ignored - - - - - Test whether one path is the same as or under another path - - The first path - supposed to be the parent path - The second path - supposed to be the child path - - - - - Modifies the current instance to be case-insensitve - and returns it. - - - - - Modifies the current instance to be case-sensitve - and returns it. - - - - - Summary description for SamePathConstraint. - - - - - Initializes a new instance of the class. - - The expected path - - - - Test whether the constraint is satisfied by a given value - - The expected path - The actual path - True for success, false for failure - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - SubPathConstraint tests that the actual path is under the expected path - - - - - Initializes a new instance of the class. - - The expected path - - - - Test whether the constraint is satisfied by a given value - - The expected path - The actual path - True for success, false for failure - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - SamePathOrUnderConstraint tests that one path is under another - - - - - Initializes a new instance of the class. - - The expected path - - - - Test whether the constraint is satisfied by a given value - - The expected path - The actual path - True for success, false for failure - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - Predicate constraint wraps a Predicate in a constraint, - returning success if the predicate is true. - - - - - Construct a PredicateConstraint from a predicate - - - - - Determines whether the predicate succeeds when applied - to the actual value. - - - - - Writes the description to a MessageWriter - - - - - NotConstraint negates the effect of some other constraint - - - - - Initializes a new instance of the class. - - The base constraint to be negated. - - - - Test whether the constraint is satisfied by a given value - - The value to be tested - True for if the base constraint fails, false if it succeeds - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - Write the actual value for a failing constraint test to a MessageWriter. - - The writer on which the actual value is displayed - - - - AllItemsConstraint applies another constraint to each - item in a collection, succeeding if they all succeed. - - - - - Construct an AllItemsConstraint on top of an existing constraint - - - - - - Apply the item constraint to each item in the collection, - failing if any item fails. - - - - - - - Write a description of this constraint to a MessageWriter - - - - - - SomeItemsConstraint applies another constraint to each - item in a collection, succeeding if any of them succeeds. - - - - - Construct a SomeItemsConstraint on top of an existing constraint - - - - - - Apply the item constraint to each item in the collection, - succeeding if any item succeeds. - - - - - - - Write a description of this constraint to a MessageWriter - - - - - - NoItemConstraint applies another constraint to each - item in a collection, failing if any of them succeeds. - - - - - Construct a NoItemConstraint on top of an existing constraint - - - - - - Apply the item constraint to each item in the collection, - failing if any item fails. - - - - - - - Write a description of this constraint to a MessageWriter - - - - - - ExactCoutConstraint applies another constraint to each - item in a collection, succeeding only if a specified - number of items succeed. - - - - - Construct an ExactCountConstraint on top of an existing constraint - - - - - - - Apply the item constraint to each item in the collection, - succeeding only if the expected number of items pass. - - - - - - - Write a description of this constraint to a MessageWriter - - - - - - PropertyExistsConstraint tests that a named property - exists on the object provided through Match. - - Originally, PropertyConstraint provided this feature - in addition to making optional tests on the vaue - of the property. The two constraints are now separate. - - - - - Initializes a new instance of the class. - - The name of the property. - - - - Test whether the property exists for a given object - - The object to be tested - True for success, false for failure - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - Write the actual value for a failing constraint test to a - MessageWriter. - - The writer on which the actual value is displayed - - - - Returns the string representation of the constraint. - - - - - - PropertyConstraint extracts a named property and uses - its value as the actual value for a chained constraint. - - - - - Initializes a new instance of the class. - - The name. - The constraint to apply to the property. - - - - Test whether the constraint is satisfied by a given value - - The value to be tested - True for success, false for failure - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - Write the actual value for a failing constraint test to a - MessageWriter. The default implementation simply writes - the raw value of actual, leaving it to the writer to - perform any formatting. - - The writer on which the actual value is displayed - - - - Returns the string representation of the constraint. - - - - - - RangeConstraint tests whethe two values are within a - specified range. - - - - - Initializes a new instance of the class. - - From. - To. - - - - Test whether the constraint is satisfied by a given value - - The value to be tested - True for success, false for failure - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - ResolvableConstraintExpression is used to represent a compound - constraint being constructed at a point where the last operator - may either terminate the expression or may have additional - qualifying constraints added to it. - - It is used, for example, for a Property element or for - an Exception element, either of which may be optionally - followed by constraints that apply to the property or - exception. - - - - - Create a new instance of ResolvableConstraintExpression - - - - - Create a new instance of ResolvableConstraintExpression, - passing in a pre-populated ConstraintBuilder. - - - - - Resolve the current expression to a Constraint - - - - - This operator creates a constraint that is satisfied only if both - argument constraints are satisfied. - - - - - This operator creates a constraint that is satisfied only if both - argument constraints are satisfied. - - - - - This operator creates a constraint that is satisfied only if both - argument constraints are satisfied. - - - - - This operator creates a constraint that is satisfied if either - of the argument constraints is satisfied. - - - - - This operator creates a constraint that is satisfied if either - of the argument constraints is satisfied. - - - - - This operator creates a constraint that is satisfied if either - of the argument constraints is satisfied. - - - - - This operator creates a constraint that is satisfied if the - argument constraint is not satisfied. - - - - - Appends an And Operator to the expression - - - - - Appends an Or operator to the expression. - - - - - ReusableConstraint wraps a resolved constraint so that it - may be saved and reused as needed. - - - - - Construct a ReusableConstraint - - The constraint or expression to be reused - - - - Conversion operator from a normal constraint to a ReusableConstraint. - - The original constraint to be wrapped as a ReusableConstraint - - - - - Returns the string representation of the constraint. - - A string representing the constraint - - - - Resolves the ReusableConstraint by returning the constraint - that it originally wrapped. - - A resolved constraint - - - - SameAsConstraint tests whether an object is identical to - the object passed to its constructor - - - - - Initializes a new instance of the class. - - The expected object. - - - - Test whether the constraint is satisfied by a given value - - The value to be tested - True for success, false for failure - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - BinarySerializableConstraint tests whether - an object is serializable in binary format. - - - - - Test whether the constraint is satisfied by a given value - - The value to be tested - True for success, false for failure - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - Write the actual value for a failing constraint test to a - MessageWriter. The default implementation simply writes - the raw value of actual, leaving it to the writer to - perform any formatting. - - The writer on which the actual value is displayed - - - - Returns the string representation - - - - - BinarySerializableConstraint tests whether - an object is serializable in binary format. - - - - - Test whether the constraint is satisfied by a given value - - The value to be tested - True for success, false for failure - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - Write the actual value for a failing constraint test to a - MessageWriter. The default implementation simply writes - the raw value of actual, leaving it to the writer to - perform any formatting. - - The writer on which the actual value is displayed - - - - Returns the string representation of this constraint - - - - - StringConstraint is the abstract base for constraints - that operate on strings. It supports the IgnoreCase - modifier for string operations. - - - - - The expected value - - - - - Indicates whether tests should be case-insensitive - - - - - Constructs a StringConstraint given an expected value - - The expected value - - - - Modify the constraint to ignore case in matching. - - - - - EmptyStringConstraint tests whether a string is empty. - - - - - Test whether the constraint is satisfied by a given value - - The value to be tested - True for success, false for failure - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - NullEmptyStringConstraint tests whether a string is either null or empty. - - - - - Constructs a new NullOrEmptyStringConstraint - - - - - Test whether the constraint is satisfied by a given value - - The value to be tested - True for success, false for failure - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - SubstringConstraint can test whether a string contains - the expected substring. - - - - - Initializes a new instance of the class. - - The expected. - - - - Test whether the constraint is satisfied by a given value - - The value to be tested - True for success, false for failure - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - StartsWithConstraint can test whether a string starts - with an expected substring. - - - - - Initializes a new instance of the class. - - The expected string - - - - Test whether the constraint is matched by the actual value. - This is a template method, which calls the IsMatch method - of the derived class. - - - - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - EndsWithConstraint can test whether a string ends - with an expected substring. - - - - - Initializes a new instance of the class. - - The expected string - - - - Test whether the constraint is matched by the actual value. - This is a template method, which calls the IsMatch method - of the derived class. - - - - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - RegexConstraint can test whether a string matches - the pattern provided. - - - - - Initializes a new instance of the class. - - The pattern. - - - - Test whether the constraint is satisfied by a given value - - The value to be tested - True for success, false for failure - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - ThrowsConstraint is used to test the exception thrown by - a delegate by applying a constraint to it. - - - - - Initializes a new instance of the class, - using a constraint to be applied to the exception. - - A constraint to apply to the caught exception. - - - - Executes the code of the delegate and captures any exception. - If a non-null base constraint was provided, it applies that - constraint to the exception. - - A delegate representing the code to be tested - True if an exception is thrown and the constraint succeeds, otherwise false - - - - Converts an ActualValueDelegate to a TestDelegate - before calling the primary overload. - - - - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - Write the actual value for a failing constraint test to a - MessageWriter. The default implementation simply writes - the raw value of actual, leaving it to the writer to - perform any formatting. - - The writer on which the actual value is displayed - - - - Returns the string representation of this constraint - - - - - Get the actual exception thrown - used by Assert.Throws. - - - - - ThrowsNothingConstraint tests that a delegate does not - throw an exception. - - - - - Test whether the constraint is satisfied by a given value - - The value to be tested - True if no exception is thrown, otherwise false - - - - Converts an ActualValueDelegate to a TestDelegate - before calling the primary overload. - - - - - - - Write the constraint description to a MessageWriter - - The writer on which the description is displayed - - - - Write the actual value for a failing constraint test to a - MessageWriter. The default implementation simply writes - the raw value of actual, leaving it to the writer to - perform any formatting. - - The writer on which the actual value is displayed - - - - Modes in which the tolerance value for a comparison can - be interpreted. - - - - - The tolerance was created with a value, without specifying - how the value would be used. This is used to prevent setting - the mode more than once and is generally changed to Linear - upon execution of the test. - - - - - The tolerance is used as a numeric range within which - two compared values are considered to be equal. - - - - - Interprets the tolerance as the percentage by which - the two compared values my deviate from each other. - - - - - Compares two values based in their distance in - representable numbers. - - - - - The Tolerance class generalizes the notion of a tolerance - within which an equality test succeeds. Normally, it is - used with numeric types, but it can be used with any - type that supports taking a difference between two - objects and comparing that difference to a value. - - - - - Constructs a linear tolerance of a specdified amount - - - - - Constructs a tolerance given an amount and ToleranceMode - - - - - Tests that the current Tolerance is linear with a - numeric value, throwing an exception if it is not. - - - - - Returns an empty Tolerance object, equivalent to - specifying no tolerance. In most cases, it results - in an exact match but for floats and doubles a - default tolerance may be used. - - - - - Returns a zero Tolerance object, equivalent to - specifying an exact match. - - - - - Gets the ToleranceMode for the current Tolerance - - - - - Gets the value of the current Tolerance instance. - - - - - Returns a new tolerance, using the current amount as a percentage. - - - - - Returns a new tolerance, using the current amount in Ulps. - - - - - Returns a new tolerance with a TimeSpan as the amount, using - the current amount as a number of days. - - - - - Returns a new tolerance with a TimeSpan as the amount, using - the current amount as a number of hours. - - - - - Returns a new tolerance with a TimeSpan as the amount, using - the current amount as a number of minutes. - - - - - Returns a new tolerance with a TimeSpan as the amount, using - the current amount as a number of seconds. - - - - - Returns a new tolerance with a TimeSpan as the amount, using - the current amount as a number of milliseconds. - - - - - Returns a new tolerance with a TimeSpan as the amount, using - the current amount as a number of clock ticks. - - - - - Returns true if the current tolerance is empty. - - - - - TypeConstraint is the abstract base for constraints - that take a Type as their expected value. - - - - - The expected Type used by the constraint - - - - - Construct a TypeConstraint for a given Type - - - - - - Write the actual value for a failing constraint test to a - MessageWriter. TypeConstraints override this method to write - the name of the type. - - The writer on which the actual value is displayed - - - - ExactTypeConstraint is used to test that an object - is of the exact type provided in the constructor - - - - - Construct an ExactTypeConstraint for a given Type - - The expected Type. - - - - Test that an object is of the exact type specified - - The actual value. - True if the tested object is of the exact type provided, otherwise false. - - - - Write the description of this constraint to a MessageWriter - - The MessageWriter to use - - - - ExceptionTypeConstraint is a special version of ExactTypeConstraint - used to provided detailed info about the exception thrown in - an error message. - - - - - Constructs an ExceptionTypeConstraint - - - - - Write the actual value for a failing constraint test to a - MessageWriter. Overriden to write additional information - in the case of an Exception. - - The MessageWriter to use - - - - InstanceOfTypeConstraint is used to test that an object - is of the same type provided or derived from it. - - - - - Construct an InstanceOfTypeConstraint for the type provided - - The expected Type - - - - Test whether an object is of the specified type or a derived type - - The object to be tested - True if the object is of the provided type or derives from it, otherwise false. - - - - Write a description of this constraint to a MessageWriter - - The MessageWriter to use - - - - AssignableFromConstraint is used to test that an object - can be assigned from a given Type. - - - - - Construct an AssignableFromConstraint for the type provided - - - - - - Test whether an object can be assigned from the specified type - - The object to be tested - True if the object can be assigned a value of the expected Type, otherwise false. - - - - Write a description of this constraint to a MessageWriter - - The MessageWriter to use - - - - AssignableToConstraint is used to test that an object - can be assigned to a given Type. - - - - - Construct an AssignableToConstraint for the type provided - - - - - - Test whether an object can be assigned to the specified type - - The object to be tested - True if the object can be assigned a value of the expected Type, otherwise false. - - - - Write a description of this constraint to a MessageWriter - - The MessageWriter to use - - - - Thrown when an assertion failed. - - - - - The error message that explains - the reason for the exception - - - The error message that explains - the reason for the exception - The exception that caused the - current exception - - - - Serialization Constructor - - - - - Thrown when an assertion failed. - - - - - - - The error message that explains - the reason for the exception - The exception that caused the - current exception - - - - Serialization Constructor - - - - - Thrown when a test executes inconclusively. - - - - - The error message that explains - the reason for the exception - - - The error message that explains - the reason for the exception - The exception that caused the - current exception - - - - Serialization Constructor - - - - - Thrown when an assertion failed. - - - - - - - The error message that explains - the reason for the exception - The exception that caused the - current exception - - - - Serialization Constructor - - - - - - - - - - - Compares two objects of a given Type for equality within a tolerance - - The first object to compare - The second object to compare - The tolerance to use in the comparison - - - - - The different targets a test action attribute can be applied to - - - - - Default target, which is determined by where the action attribute is attached - - - - - Target a individual test case - - - - - Target a suite of test cases - - - - - Delegate used by tests that execute code and - capture any thrown exception. - - - - - The Assert class contains a collection of static methods that - implement the most common assertions used in NUnit. - - - - - We don't actually want any instances of this object, but some people - like to inherit from it to add other static methods. Hence, the - protected constructor disallows any instances of this object. - - - - - The Equals method throws an AssertionException. This is done - to make sure there is no mistake by calling this function. - - - - - - - override the default ReferenceEquals to throw an AssertionException. This - implementation makes sure there is no mistake in calling this function - as part of Assert. - - - - - - - Helper for Assert.AreEqual(double expected, double actual, ...) - allowing code generation to work consistently. - - The expected value - The actual value - The maximum acceptable difference between the - the expected and the actual - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Throws a with the message and arguments - that are passed in. This allows a test to be cut short, with a result - of success returned to NUnit. - - The message to initialize the with. - Arguments to be used in formatting the message - - - - Throws a with the message and arguments - that are passed in. This allows a test to be cut short, with a result - of success returned to NUnit. - - The message to initialize the with. - - - - Throws a with the message and arguments - that are passed in. This allows a test to be cut short, with a result - of success returned to NUnit. - - - - - Throws an with the message and arguments - that are passed in. This is used by the other Assert functions. - - The message to initialize the with. - Arguments to be used in formatting the message - - - - Throws an with the message that is - passed in. This is used by the other Assert functions. - - The message to initialize the with. - - - - Throws an . - This is used by the other Assert functions. - - - - - Throws an with the message and arguments - that are passed in. This causes the test to be reported as ignored. - - The message to initialize the with. - Arguments to be used in formatting the message - - - - Throws an with the message that is - passed in. This causes the test to be reported as ignored. - - The message to initialize the with. - - - - Throws an . - This causes the test to be reported as ignored. - - - - - Throws an with the message and arguments - that are passed in. This causes the test to be reported as inconclusive. - - The message to initialize the with. - Arguments to be used in formatting the message - - - - Throws an with the message that is - passed in. This causes the test to be reported as inconclusive. - - The message to initialize the with. - - - - Throws an . - This causes the test to be reported as Inconclusive. - - - - - Apply a constraint to an actual value, succeeding if the constraint - is satisfied and throwing an assertion exception on failure. - - A Constraint to be applied - The actual value to test - - - - Apply a constraint to an actual value, succeeding if the constraint - is satisfied and throwing an assertion exception on failure. - - A Constraint to be applied - The actual value to test - The message that will be displayed on failure - - - - Apply a constraint to an actual value, succeeding if the constraint - is satisfied and throwing an assertion exception on failure. - - A Constraint expression to be applied - The actual value to test - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Apply a constraint to an actual value, succeeding if the constraint - is satisfied and throwing an assertion exception on failure. - - A Constraint expression to be applied - An ActualValueDelegate returning the value to be tested - - - - Apply a constraint to an actual value, succeeding if the constraint - is satisfied and throwing an assertion exception on failure. - - A Constraint expression to be applied - An ActualValueDelegate returning the value to be tested - The message that will be displayed on failure - - - - Apply a constraint to an actual value, succeeding if the constraint - is satisfied and throwing an assertion exception on failure. - - An ActualValueDelegate returning the value to be tested - A Constraint expression to be applied - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Apply a constraint to a referenced value, succeeding if the constraint - is satisfied and throwing an assertion exception on failure. - - A Constraint to be applied - The actual value to test - - - - Apply a constraint to a referenced value, succeeding if the constraint - is satisfied and throwing an assertion exception on failure. - - A Constraint to be applied - The actual value to test - The message that will be displayed on failure - - - - Apply a constraint to a referenced value, succeeding if the constraint - is satisfied and throwing an assertion exception on failure. - - A Constraint to be applied - The actual value to test - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Asserts that a condition is true. If the condition is false the method throws - an . - - The evaluated condition - The message to display if the condition is false - Arguments to be used in formatting the message - - - - Asserts that a condition is true. If the condition is false the method throws - an . - - The evaluated condition - The message to display if the condition is false - - - - Asserts that a condition is true. If the condition is false the method throws - an . - - The evaluated condition - - - - Asserts that the code represented by a delegate throws an exception - that satisfies the constraint provided. - - A TestDelegate to be executed - A ThrowsConstraint used in the test - - - - Apply a constraint to an actual value, succeeding if the constraint - is satisfied and throwing an assertion exception on failure. - Used as a synonym for That in rare cases where a private setter - causes a Visual Basic compilation error. - - A Constraint to be applied - The actual value to test - - - - Apply a constraint to an actual value, succeeding if the constraint - is satisfied and throwing an assertion exception on failure. - Used as a synonym for That in rare cases where a private setter - causes a Visual Basic compilation error. - - A Constraint to be applied - The actual value to test - The message that will be displayed on failure - - - - Apply a constraint to an actual value, succeeding if the constraint - is satisfied and throwing an assertion exception on failure. - Used as a synonym for That in rare cases where a private setter - causes a Visual Basic compilation error. - - - This method is provided for use by VB developers needing to test - the value of properties with private setters. - - A Constraint expression to be applied - The actual value to test - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Verifies that a delegate throws a particular exception when called. - - A constraint to be satisfied by the exception - A TestSnippet delegate - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Verifies that a delegate throws a particular exception when called. - - A constraint to be satisfied by the exception - A TestSnippet delegate - The message that will be displayed on failure - - - - Verifies that a delegate throws a particular exception when called. - - A constraint to be satisfied by the exception - A TestSnippet delegate - - - - Verifies that a delegate throws a particular exception when called. - - The exception Type expected - A TestSnippet delegate - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Verifies that a delegate throws a particular exception when called. - - The exception Type expected - A TestSnippet delegate - The message that will be displayed on failure - - - - Verifies that a delegate throws a particular exception when called. - - The exception Type expected - A TestSnippet delegate - - - - Verifies that a delegate throws a particular exception when called. - - Type of the expected exception - A TestSnippet delegate - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Verifies that a delegate throws a particular exception when called. - - Type of the expected exception - A TestSnippet delegate - The message that will be displayed on failure - - - - Verifies that a delegate throws a particular exception when called. - - Type of the expected exception - A TestSnippet delegate - - - - Verifies that a delegate throws an exception when called - and returns it. - - A TestDelegate - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Verifies that a delegate throws an exception when called - and returns it. - - A TestDelegate - The message that will be displayed on failure - - - - Verifies that a delegate throws an exception when called - and returns it. - - A TestDelegate - - - - Verifies that a delegate throws an exception of a certain Type - or one derived from it when called and returns it. - - The expected Exception Type - A TestDelegate - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Verifies that a delegate throws an exception of a certain Type - or one derived from it when called and returns it. - - The expected Exception Type - A TestDelegate - The message that will be displayed on failure - - - - Verifies that a delegate throws an exception of a certain Type - or one derived from it when called and returns it. - - The expected Exception Type - A TestDelegate - - - - Verifies that a delegate throws an exception of a certain Type - or one derived from it when called and returns it. - - The expected Exception Type - A TestDelegate - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Verifies that a delegate throws an exception of a certain Type - or one derived from it when called and returns it. - - The expected Exception Type - A TestDelegate - The message that will be displayed on failure - - - - Verifies that a delegate throws an exception of a certain Type - or one derived from it when called and returns it. - - The expected Exception Type - A TestDelegate - - - - Verifies that a delegate does not throw an exception - - A TestSnippet delegate - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Verifies that a delegate does not throw an exception. - - A TestSnippet delegate - The message that will be displayed on failure - - - - Verifies that a delegate does not throw an exception. - - A TestSnippet delegate - - - - Asserts that a condition is true. If the condition is false the method throws - an . - - The evaluated condition - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Asserts that a condition is true. If the condition is false the method throws - an . - - The evaluated condition - The message to display in case of failure - - - - Asserts that a condition is true. If the condition is false the method throws - an . - - The evaluated condition - - - - Asserts that a condition is true. If the condition is false the method throws - an . - - The evaluated condition - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Asserts that a condition is true. If the condition is false the method throws - an . - - The evaluated condition - The message to display in case of failure - - - - Asserts that a condition is true. If the condition is false the method throws - an . - - The evaluated condition - - - - Asserts that a condition is false. If the condition is true the method throws - an . - - The evaluated condition - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Asserts that a condition is false. If the condition is true the method throws - an . - - The evaluated condition - The message to display in case of failure - - - - Asserts that a condition is false. If the condition is true the method throws - an . - - The evaluated condition - - - - Asserts that a condition is false. If the condition is true the method throws - an . - - The evaluated condition - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Asserts that a condition is false. If the condition is true the method throws - an . - - The evaluated condition - The message to display in case of failure - - - - Asserts that a condition is false. If the condition is true the method throws - an . - - The evaluated condition - - - - Verifies that the object that is passed in is not equal to null - If the object is null then an - is thrown. - - The object that is to be tested - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the object that is passed in is not equal to null - If the object is null then an - is thrown. - - The object that is to be tested - The message to display in case of failure - - - - Verifies that the object that is passed in is not equal to null - If the object is null then an - is thrown. - - The object that is to be tested - - - - Verifies that the object that is passed in is not equal to null - If the object is null then an - is thrown. - - The object that is to be tested - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the object that is passed in is not equal to null - If the object is null then an - is thrown. - - The object that is to be tested - The message to display in case of failure - - - - Verifies that the object that is passed in is not equal to null - If the object is null then an - is thrown. - - The object that is to be tested - - - - Verifies that the object that is passed in is equal to null - If the object is not null then an - is thrown. - - The object that is to be tested - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the object that is passed in is equal to null - If the object is not null then an - is thrown. - - The object that is to be tested - The message to display in case of failure - - - - Verifies that the object that is passed in is equal to null - If the object is not null then an - is thrown. - - The object that is to be tested - - - - Verifies that the object that is passed in is equal to null - If the object is not null then an - is thrown. - - The object that is to be tested - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the object that is passed in is equal to null - If the object is not null then an - is thrown. - - The object that is to be tested - The message to display in case of failure - - - - Verifies that the object that is passed in is equal to null - If the object is not null then an - is thrown. - - The object that is to be tested - - - - Verifies that the double that is passed in is an NaN value. - If the object is not NaN then an - is thrown. - - The value that is to be tested - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the double that is passed in is an NaN value. - If the object is not NaN then an - is thrown. - - The value that is to be tested - The message to display in case of failure - - - - Verifies that the double that is passed in is an NaN value. - If the object is not NaN then an - is thrown. - - The value that is to be tested - - - - Verifies that the double that is passed in is an NaN value. - If the object is not NaN then an - is thrown. - - The value that is to be tested - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the double that is passed in is an NaN value. - If the object is not NaN then an - is thrown. - - The value that is to be tested - The message to display in case of failure - - - - Verifies that the double that is passed in is an NaN value. - If the object is not NaN then an - is thrown. - - The value that is to be tested - - - - Assert that a string is empty - that is equal to string.Empty - - The string to be tested - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Assert that a string is empty - that is equal to string.Empty - - The string to be tested - The message to display in case of failure - - - - Assert that a string is empty - that is equal to string.Empty - - The string to be tested - - - - Assert that an array, list or other collection is empty - - An array, list or other collection implementing ICollection - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Assert that an array, list or other collection is empty - - An array, list or other collection implementing ICollection - The message to display in case of failure - - - - Assert that an array, list or other collection is empty - - An array, list or other collection implementing ICollection - - - - Assert that a string is not empty - that is not equal to string.Empty - - The string to be tested - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Assert that a string is not empty - that is not equal to string.Empty - - The string to be tested - The message to display in case of failure - - - - Assert that a string is not empty - that is not equal to string.Empty - - The string to be tested - - - - Assert that an array, list or other collection is not empty - - An array, list or other collection implementing ICollection - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Assert that an array, list or other collection is not empty - - An array, list or other collection implementing ICollection - The message to display in case of failure - - - - Assert that an array, list or other collection is not empty - - An array, list or other collection implementing ICollection - - - - Assert that a string is either null or equal to string.Empty - - The string to be tested - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Assert that a string is either null or equal to string.Empty - - The string to be tested - The message to display in case of failure - - - - Assert that a string is either null or equal to string.Empty - - The string to be tested - - - - Assert that a string is not null or empty - - The string to be tested - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Assert that a string is not null or empty - - The string to be tested - The message to display in case of failure - - - - Assert that a string is not null or empty - - The string to be tested - - - - Asserts that an object may be assigned a value of a given Type. - - The expected Type. - The object under examination - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Asserts that an object may be assigned a value of a given Type. - - The expected Type. - The object under examination - The message to display in case of failure - - - - Asserts that an object may be assigned a value of a given Type. - - The expected Type. - The object under examination - - - - Asserts that an object may be assigned a value of a given Type. - - The expected Type. - The object under examination - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Asserts that an object may be assigned a value of a given Type. - - The expected Type. - The object under examination - The message to display in case of failure - - - - Asserts that an object may be assigned a value of a given Type. - - The expected Type. - The object under examination - - - - Asserts that an object may not be assigned a value of a given Type. - - The expected Type. - The object under examination - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Asserts that an object may not be assigned a value of a given Type. - - The expected Type. - The object under examination - The message to display in case of failure - - - - Asserts that an object may not be assigned a value of a given Type. - - The expected Type. - The object under examination - - - - Asserts that an object may not be assigned a value of a given Type. - - The expected Type. - The object under examination - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Asserts that an object may not be assigned a value of a given Type. - - The expected Type. - The object under examination - The message to display in case of failure - - - - Asserts that an object may not be assigned a value of a given Type. - - The expected Type. - The object under examination - - - - Asserts that an object is an instance of a given type. - - The expected Type - The object being examined - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Asserts that an object is an instance of a given type. - - The expected Type - The object being examined - The message to display in case of failure - - - - Asserts that an object is an instance of a given type. - - The expected Type - The object being examined - - - - Asserts that an object is an instance of a given type. - - The expected Type - The object being examined - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Asserts that an object is an instance of a given type. - - The expected Type - The object being examined - The message to display in case of failure - - - - Asserts that an object is an instance of a given type. - - The expected Type - The object being examined - - - - Asserts that an object is an instance of a given type. - - The expected Type - The object being examined - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Asserts that an object is an instance of a given type. - - The expected Type - The object being examined - The message to display in case of failure - - - - Asserts that an object is an instance of a given type. - - The expected Type - The object being examined - - - - Asserts that an object is not an instance of a given type. - - The expected Type - The object being examined - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Asserts that an object is not an instance of a given type. - - The expected Type - The object being examined - The message to display in case of failure - - - - Asserts that an object is not an instance of a given type. - - The expected Type - The object being examined - - - - Asserts that an object is not an instance of a given type. - - The expected Type - The object being examined - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Asserts that an object is not an instance of a given type. - - The expected Type - The object being examined - The message to display in case of failure - - - - Asserts that an object is not an instance of a given type. - - The expected Type - The object being examined - - - - Asserts that an object is not an instance of a given type. - - The expected Type - The object being examined - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Asserts that an object is not an instance of a given type. - - The expected Type - The object being examined - The message to display in case of failure - - - - Asserts that an object is not an instance of a given type. - - The expected Type - The object being examined - - - - Verifies that two values are equal. If they are not, then an - is thrown. - - The expected value - The actual value - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that two values are equal. If they are not, then an - is thrown. - - The expected value - The actual value - The message to display in case of failure - - - - Verifies that two values are equal. If they are not, then an - is thrown. - - The expected value - The actual value - - - - Verifies that two values are equal. If they are not, then an - is thrown. - - The expected value - The actual value - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that two values are equal. If they are not, then an - is thrown. - - The expected value - The actual value - The message to display in case of failure - - - - Verifies that two values are equal. If they are not, then an - is thrown. - - The expected value - The actual value - - - - Verifies that two values are equal. If they are not, then an - is thrown. - - The expected value - The actual value - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that two values are equal. If they are not, then an - is thrown. - - The expected value - The actual value - The message to display in case of failure - - - - Verifies that two values are equal. If they are not, then an - is thrown. - - The expected value - The actual value - - - - Verifies that two values are equal. If they are not, then an - is thrown. - - The expected value - The actual value - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that two values are equal. If they are not, then an - is thrown. - - The expected value - The actual value - The message to display in case of failure - - - - Verifies that two values are equal. If they are not, then an - is thrown. - - The expected value - The actual value - - - - Verifies that two values are equal. If they are not, then an - is thrown. - - The expected value - The actual value - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that two values are equal. If they are not, then an - is thrown. - - The expected value - The actual value - The message to display in case of failure - - - - Verifies that two values are equal. If they are not, then an - is thrown. - - The expected value - The actual value - - - - Verifies that two doubles are equal considering a delta. If the - expected value is infinity then the delta value is ignored. If - they are not equal then an is - thrown. - - The expected value - The actual value - The maximum acceptable difference between the - the expected and the actual - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that two doubles are equal considering a delta. If the - expected value is infinity then the delta value is ignored. If - they are not equal then an is - thrown. - - The expected value - The actual value - The maximum acceptable difference between the - the expected and the actual - The message to display in case of failure - - - - Verifies that two doubles are equal considering a delta. If the - expected value is infinity then the delta value is ignored. If - they are not equal then an is - thrown. - - The expected value - The actual value - The maximum acceptable difference between the - the expected and the actual - - - - Verifies that two doubles are equal considering a delta. If the - expected value is infinity then the delta value is ignored. If - they are not equal then an is - thrown. - - The expected value - The actual value - The maximum acceptable difference between the - the expected and the actual - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that two doubles are equal considering a delta. If the - expected value is infinity then the delta value is ignored. If - they are not equal then an is - thrown. - - The expected value - The actual value - The maximum acceptable difference between the - the expected and the actual - The message to display in case of failure - - - - Verifies that two doubles are equal considering a delta. If the - expected value is infinity then the delta value is ignored. If - they are not equal then an is - thrown. - - The expected value - The actual value - The maximum acceptable difference between the - the expected and the actual - - - - Verifies that two objects are equal. Two objects are considered - equal if both are null, or if both have the same value. NUnit - has special semantics for some object types. - If they are not equal an is thrown. - - The value that is expected - The actual value - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that two objects are equal. Two objects are considered - equal if both are null, or if both have the same value. NUnit - has special semantics for some object types. - If they are not equal an is thrown. - - The value that is expected - The actual value - The message to display in case of failure - - - - Verifies that two objects are equal. Two objects are considered - equal if both are null, or if both have the same value. NUnit - has special semantics for some object types. - If they are not equal an is thrown. - - The value that is expected - The actual value - - - - Verifies that two values are not equal. If they are equal, then an - is thrown. - - The expected value - The actual value - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that two values are not equal. If they are equal, then an - is thrown. - - The expected value - The actual value - The message to display in case of failure - - - - Verifies that two values are not equal. If they are equal, then an - is thrown. - - The expected value - The actual value - - - - Verifies that two values are not equal. If they are equal, then an - is thrown. - - The expected value - The actual value - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that two values are not equal. If they are equal, then an - is thrown. - - The expected value - The actual value - The message to display in case of failure - - - - Verifies that two values are not equal. If they are equal, then an - is thrown. - - The expected value - The actual value - - - - Verifies that two values are not equal. If they are equal, then an - is thrown. - - The expected value - The actual value - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that two values are not equal. If they are equal, then an - is thrown. - - The expected value - The actual value - The message to display in case of failure - - - - Verifies that two values are not equal. If they are equal, then an - is thrown. - - The expected value - The actual value - - - - Verifies that two values are not equal. If they are equal, then an - is thrown. - - The expected value - The actual value - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that two values are not equal. If they are equal, then an - is thrown. - - The expected value - The actual value - The message to display in case of failure - - - - Verifies that two values are not equal. If they are equal, then an - is thrown. - - The expected value - The actual value - - - - Verifies that two values are not equal. If they are equal, then an - is thrown. - - The expected value - The actual value - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that two values are not equal. If they are equal, then an - is thrown. - - The expected value - The actual value - The message to display in case of failure - - - - Verifies that two values are not equal. If they are equal, then an - is thrown. - - The expected value - The actual value - - - - Verifies that two values are not equal. If they are equal, then an - is thrown. - - The expected value - The actual value - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that two values are not equal. If they are equal, then an - is thrown. - - The expected value - The actual value - The message to display in case of failure - - - - Verifies that two values are not equal. If they are equal, then an - is thrown. - - The expected value - The actual value - - - - Verifies that two values are not equal. If they are equal, then an - is thrown. - - The expected value - The actual value - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that two values are not equal. If they are equal, then an - is thrown. - - The expected value - The actual value - The message to display in case of failure - - - - Verifies that two values are not equal. If they are equal, then an - is thrown. - - The expected value - The actual value - - - - Verifies that two objects are not equal. Two objects are considered - equal if both are null, or if both have the same value. NUnit - has special semantics for some object types. - If they are equal an is thrown. - - The value that is expected - The actual value - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that two objects are not equal. Two objects are considered - equal if both are null, or if both have the same value. NUnit - has special semantics for some object types. - If they are equal an is thrown. - - The value that is expected - The actual value - The message to display in case of failure - - - - Verifies that two objects are not equal. Two objects are considered - equal if both are null, or if both have the same value. NUnit - has special semantics for some object types. - If they are equal an is thrown. - - The value that is expected - The actual value - - - - Asserts that two objects refer to the same object. If they - are not the same an is thrown. - - The expected object - The actual object - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Asserts that two objects refer to the same object. If they - are not the same an is thrown. - - The expected object - The actual object - The message to display in case of failure - - - - Asserts that two objects refer to the same object. If they - are not the same an is thrown. - - The expected object - The actual object - - - - Asserts that two objects do not refer to the same object. If they - are the same an is thrown. - - The expected object - The actual object - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Asserts that two objects do not refer to the same object. If they - are the same an is thrown. - - The expected object - The actual object - The message to display in case of failure - - - - Asserts that two objects do not refer to the same object. If they - are the same an is thrown. - - The expected object - The actual object - - - - Verifies that the first value is greater than the second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is greater than the second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - - - - Verifies that the first value is greater than the second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - - - - Verifies that the first value is greater than the second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is greater than the second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - - - - Verifies that the first value is greater than the second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - - - - Verifies that the first value is greater than the second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is greater than the second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - - - - Verifies that the first value is greater than the second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - - - - Verifies that the first value is greater than the second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is greater than the second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - - - - Verifies that the first value is greater than the second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - - - - Verifies that the first value is greater than the second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is greater than the second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - - - - Verifies that the first value is greater than the second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - - - - Verifies that the first value is greater than the second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is greater than the second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - - - - Verifies that the first value is greater than the second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - - - - Verifies that the first value is greater than the second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is greater than the second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - - - - Verifies that the first value is greater than the second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - - - - Verifies that the first value is greater than the second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is greater than the second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - - - - Verifies that the first value is greater than the second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - - - - Verifies that the first value is less than the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is less than the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - - - - Verifies that the first value is less than the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - - - - Verifies that the first value is less than the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is less than the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - - - - Verifies that the first value is less than the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - - - - Verifies that the first value is less than the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is less than the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - - - - Verifies that the first value is less than the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - - - - Verifies that the first value is less than the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is less than the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - - - - Verifies that the first value is less than the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - - - - Verifies that the first value is less than the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is less than the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - - - - Verifies that the first value is less than the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - - - - Verifies that the first value is less than the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is less than the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - - - - Verifies that the first value is less than the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - - - - Verifies that the first value is less than the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is less than the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - - - - Verifies that the first value is less than the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - - - - Verifies that the first value is less than the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is less than the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - - - - Verifies that the first value is less than the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - - - - Verifies that the first value is greater than or equal tothe second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is greater than or equal tothe second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - - - - Verifies that the first value is greater than or equal tothe second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - - - - Verifies that the first value is greater than or equal tothe second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is greater than or equal tothe second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - - - - Verifies that the first value is greater than or equal tothe second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - - - - Verifies that the first value is greater than or equal tothe second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is greater than or equal tothe second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - - - - Verifies that the first value is greater than or equal tothe second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - - - - Verifies that the first value is greater than or equal tothe second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is greater than or equal tothe second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - - - - Verifies that the first value is greater than or equal tothe second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - - - - Verifies that the first value is greater than or equal tothe second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is greater than or equal tothe second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - - - - Verifies that the first value is greater than or equal tothe second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - - - - Verifies that the first value is greater than or equal tothe second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is greater than or equal tothe second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - - - - Verifies that the first value is greater than or equal tothe second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - - - - Verifies that the first value is greater than or equal tothe second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is greater than or equal tothe second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - - - - Verifies that the first value is greater than or equal tothe second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - - - - Verifies that the first value is greater than or equal tothe second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is greater than or equal tothe second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - The message to display in case of failure - - - - Verifies that the first value is greater than or equal tothe second - value. If it is not, then an - is thrown. - - The first value, expected to be greater - The second value, expected to be less - - - - Verifies that the first value is less than or equal to the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is less than or equal to the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - - - - Verifies that the first value is less than or equal to the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - - - - Verifies that the first value is less than or equal to the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is less than or equal to the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - - - - Verifies that the first value is less than or equal to the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - - - - Verifies that the first value is less than or equal to the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is less than or equal to the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - - - - Verifies that the first value is less than or equal to the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - - - - Verifies that the first value is less than or equal to the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is less than or equal to the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - - - - Verifies that the first value is less than or equal to the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - - - - Verifies that the first value is less than or equal to the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is less than or equal to the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - - - - Verifies that the first value is less than or equal to the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - - - - Verifies that the first value is less than or equal to the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is less than or equal to the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - - - - Verifies that the first value is less than or equal to the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - - - - Verifies that the first value is less than or equal to the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is less than or equal to the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - - - - Verifies that the first value is less than or equal to the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - - - - Verifies that the first value is less than or equal to the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Verifies that the first value is less than or equal to the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - The message to display in case of failure - - - - Verifies that the first value is less than or equal to the second - value. If it is not, then an - is thrown. - - The first value, expected to be less - The second value, expected to be greater - - - - Asserts that an object is contained in a list. - - The expected object - The list to be examined - The message to display in case of failure - Array of objects to be used in formatting the message - - - - Asserts that an object is contained in a list. - - The expected object - The list to be examined - The message to display in case of failure - - - - Asserts that an object is contained in a list. - - The expected object - The list to be examined - - - - Gets the number of assertions executed so far and - resets the counter to zero. - - - - - AssertionHelper is an optional base class for user tests, - allowing the use of shorter names for constraints and - asserts and avoiding conflict with the definition of - , from which it inherits much of its - behavior, in certain mock object frameworks. - - - - - Apply a constraint to an actual value, succeeding if the constraint - is satisfied and throwing an assertion exception on failure. Works - identically to Assert.That - - A Constraint to be applied - The actual value to test - - - - Apply a constraint to an actual value, succeeding if the constraint - is satisfied and throwing an assertion exception on failure. Works - identically to Assert.That. - - A Constraint to be applied - The actual value to test - The message that will be displayed on failure - - - - Apply a constraint to an actual value, succeeding if the constraint - is satisfied and throwing an assertion exception on failure. Works - identically to Assert.That - - A Constraint to be applied - The actual value to test - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Apply a constraint to an actual value, succeeding if the constraint - is satisfied and throwing an assertion exception on failure. - - A Constraint expression to be applied - An ActualValueDelegate returning the value to be tested - - - - Apply a constraint to an actual value, succeeding if the constraint - is satisfied and throwing an assertion exception on failure. - - A Constraint expression to be applied - An ActualValueDelegate returning the value to be tested - The message that will be displayed on failure - - - - Apply a constraint to an actual value, succeeding if the constraint - is satisfied and throwing an assertion exception on failure. - - An ActualValueDelegate returning the value to be tested - A Constraint expression to be applied - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Apply a constraint to a referenced value, succeeding if the constraint - is satisfied and throwing an assertion exception on failure. - - A Constraint to be applied - The actual value to test - - - - Apply a constraint to a referenced value, succeeding if the constraint - is satisfied and throwing an assertion exception on failure. - - A Constraint to be applied - The actual value to test - The message that will be displayed on failure - - - - Apply a constraint to a referenced value, succeeding if the constraint - is satisfied and throwing an assertion exception on failure. - - A Constraint to be applied - The actual value to test - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Asserts that a condition is true. If the condition is false the method throws - an . Works Identically to Assert.That. - - The evaluated condition - The message to display if the condition is false - Arguments to be used in formatting the message - - - - Asserts that a condition is true. If the condition is false the method throws - an . Works Identically to Assert.That. - - The evaluated condition - The message to display if the condition is false - - - - Asserts that a condition is true. If the condition is false the method throws - an . Works Identically Assert.That. - - The evaluated condition - - - - Asserts that the code represented by a delegate throws an exception - that satisfies the constraint provided. - - A TestDelegate to be executed - A ThrowsConstraint used in the test - - - - Returns a ListMapper based on a collection. - - The original collection - - - - - Provides static methods to express the assumptions - that must be met for a test to give a meaningful - result. If an assumption is not met, the test - should produce an inconclusive result. - - - - - The Equals method throws an AssertionException. This is done - to make sure there is no mistake by calling this function. - - - - - - - override the default ReferenceEquals to throw an AssertionException. This - implementation makes sure there is no mistake in calling this function - as part of Assert. - - - - - - - Apply a constraint to an actual value, succeeding if the constraint - is satisfied and throwing an InconclusiveException on failure. - - A Constraint expression to be applied - The actual value to test - - - - Apply a constraint to an actual value, succeeding if the constraint - is satisfied and throwing an InconclusiveException on failure. - - A Constraint expression to be applied - The actual value to test - The message that will be displayed on failure - - - - Apply a constraint to an actual value, succeeding if the constraint - is satisfied and throwing an InconclusiveException on failure. - - A Constraint expression to be applied - The actual value to test - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Apply a constraint to an actual value, succeeding if the constraint - is satisfied and throwing an InconclusiveException on failure. - - A Constraint expression to be applied - An ActualValueDelegate returning the value to be tested - - - - Apply a constraint to an actual value, succeeding if the constraint - is satisfied and throwing an InconclusiveException on failure. - - A Constraint expression to be applied - An ActualValueDelegate returning the value to be tested - The message that will be displayed on failure - - - - Apply a constraint to an actual value, succeeding if the constraint - is satisfied and throwing an InconclusiveException on failure. - - An ActualValueDelegate returning the value to be tested - A Constraint expression to be applied - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Apply a constraint to a referenced value, succeeding if the constraint - is satisfied and throwing an InconclusiveException on failure. - - A Constraint expression to be applied - The actual value to test - - - - Apply a constraint to a referenced value, succeeding if the constraint - is satisfied and throwing an InconclusiveException on failure. - - A Constraint expression to be applied - The actual value to test - The message that will be displayed on failure - - - - Apply a constraint to a referenced value, succeeding if the constraint - is satisfied and throwing an InconclusiveException on failure. - - A Constraint expression to be applied - The actual value to test - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Asserts that a condition is true. If the condition is false the method throws - an . - - The evaluated condition - The message to display if the condition is false - Arguments to be used in formatting the message - - - - Asserts that a condition is true. If the condition is false the method throws - an . - - The evaluated condition - The message to display if the condition is false - - - - Asserts that a condition is true. If the condition is false the - method throws an . - - The evaluated condition - - - - Asserts that the code represented by a delegate throws an exception - that satisfies the constraint provided. - - A TestDelegate to be executed - A ThrowsConstraint used in the test - - - - A set of Assert methods operationg on one or more collections - - - - - The Equals method throws an AssertionException. This is done - to make sure there is no mistake by calling this function. - - - - - - - override the default ReferenceEquals to throw an AssertionException. This - implementation makes sure there is no mistake in calling this function - as part of Assert. - - - - - - - Asserts that all items contained in collection are of the type specified by expectedType. - - IEnumerable containing objects to be considered - System.Type that all objects in collection must be instances of - - - - Asserts that all items contained in collection are of the type specified by expectedType. - - IEnumerable containing objects to be considered - System.Type that all objects in collection must be instances of - The message that will be displayed on failure - - - - Asserts that all items contained in collection are of the type specified by expectedType. - - IEnumerable containing objects to be considered - System.Type that all objects in collection must be instances of - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Asserts that all items contained in collection are not equal to null. - - IEnumerable containing objects to be considered - - - - Asserts that all items contained in collection are not equal to null. - - IEnumerable containing objects to be considered - The message that will be displayed on failure - - - - Asserts that all items contained in collection are not equal to null. - - IEnumerable of objects to be considered - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Ensures that every object contained in collection exists within the collection - once and only once. - - IEnumerable of objects to be considered - - - - Ensures that every object contained in collection exists within the collection - once and only once. - - IEnumerable of objects to be considered - The message that will be displayed on failure - - - - Ensures that every object contained in collection exists within the collection - once and only once. - - IEnumerable of objects to be considered - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Asserts that expected and actual are exactly equal. The collections must have the same count, - and contain the exact same objects in the same order. - - The first IEnumerable of objects to be considered - The second IEnumerable of objects to be considered - - - - Asserts that expected and actual are exactly equal. The collections must have the same count, - and contain the exact same objects in the same order. - If comparer is not null then it will be used to compare the objects. - - The first IEnumerable of objects to be considered - The second IEnumerable of objects to be considered - The IComparer to use in comparing objects from each IEnumerable - - - - Asserts that expected and actual are exactly equal. The collections must have the same count, - and contain the exact same objects in the same order. - - The first IEnumerable of objects to be considered - The second IEnumerable of objects to be considered - The message that will be displayed on failure - - - - Asserts that expected and actual are exactly equal. The collections must have the same count, - and contain the exact same objects in the same order. - If comparer is not null then it will be used to compare the objects. - - The first IEnumerable of objects to be considered - The second IEnumerable of objects to be considered - The IComparer to use in comparing objects from each IEnumerable - The message that will be displayed on failure - - - - Asserts that expected and actual are exactly equal. The collections must have the same count, - and contain the exact same objects in the same order. - - The first IEnumerable of objects to be considered - The second IEnumerable of objects to be considered - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Asserts that expected and actual are exactly equal. The collections must have the same count, - and contain the exact same objects in the same order. - If comparer is not null then it will be used to compare the objects. - - The first IEnumerable of objects to be considered - The second IEnumerable of objects to be considered - The IComparer to use in comparing objects from each IEnumerable - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Asserts that expected and actual are equivalent, containing the same objects but the match may be in any order. - - The first IEnumerable of objects to be considered - The second IEnumerable of objects to be considered - - - - Asserts that expected and actual are equivalent, containing the same objects but the match may be in any order. - - The first IEnumerable of objects to be considered - The second IEnumerable of objects to be considered - The message that will be displayed on failure - - - - Asserts that expected and actual are equivalent, containing the same objects but the match may be in any order. - - The first IEnumerable of objects to be considered - The second IEnumerable of objects to be considered - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Asserts that expected and actual are not exactly equal. - - The first IEnumerable of objects to be considered - The second IEnumerable of objects to be considered - - - - Asserts that expected and actual are not exactly equal. - If comparer is not null then it will be used to compare the objects. - - The first IEnumerable of objects to be considered - The second IEnumerable of objects to be considered - The IComparer to use in comparing objects from each IEnumerable - - - - Asserts that expected and actual are not exactly equal. - - The first IEnumerable of objects to be considered - The second IEnumerable of objects to be considered - The message that will be displayed on failure - - - - Asserts that expected and actual are not exactly equal. - If comparer is not null then it will be used to compare the objects. - - The first IEnumerable of objects to be considered - The second IEnumerable of objects to be considered - The IComparer to use in comparing objects from each IEnumerable - The message that will be displayed on failure - - - - Asserts that expected and actual are not exactly equal. - - The first IEnumerable of objects to be considered - The second IEnumerable of objects to be considered - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Asserts that expected and actual are not exactly equal. - If comparer is not null then it will be used to compare the objects. - - The first IEnumerable of objects to be considered - The second IEnumerable of objects to be considered - The IComparer to use in comparing objects from each IEnumerable - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Asserts that expected and actual are not equivalent. - - The first IEnumerable of objects to be considered - The second IEnumerable of objects to be considered - - - - Asserts that expected and actual are not equivalent. - - The first IEnumerable of objects to be considered - The second IEnumerable of objects to be considered - The message that will be displayed on failure - - - - Asserts that expected and actual are not equivalent. - - The first IEnumerable of objects to be considered - The second IEnumerable of objects to be considered - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Asserts that collection contains actual as an item. - - IEnumerable of objects to be considered - Object to be found within collection - - - - Asserts that collection contains actual as an item. - - IEnumerable of objects to be considered - Object to be found within collection - The message that will be displayed on failure - - - - Asserts that collection contains actual as an item. - - IEnumerable of objects to be considered - Object to be found within collection - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Asserts that collection does not contain actual as an item. - - IEnumerable of objects to be considered - Object that cannot exist within collection - - - - Asserts that collection does not contain actual as an item. - - IEnumerable of objects to be considered - Object that cannot exist within collection - The message that will be displayed on failure - - - - Asserts that collection does not contain actual as an item. - - IEnumerable of objects to be considered - Object that cannot exist within collection - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Asserts that superset is not a subject of subset. - - The IEnumerable superset to be considered - The IEnumerable subset to be considered - - - - Asserts that superset is not a subject of subset. - - The IEnumerable superset to be considered - The IEnumerable subset to be considered - The message that will be displayed on failure - - - - Asserts that superset is not a subject of subset. - - The IEnumerable superset to be considered - The IEnumerable subset to be considered - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Asserts that superset is a subset of subset. - - The IEnumerable superset to be considered - The IEnumerable subset to be considered - - - - Asserts that superset is a subset of subset. - - The IEnumerable superset to be considered - The IEnumerable subset to be considered - The message that will be displayed on failure - - - - Asserts that superset is a subset of subset. - - The IEnumerable superset to be considered - The IEnumerable subset to be considered - The message that will be displayed on failure - Arguments to be used in formatting the message - - - - Assert that an array, list or other collection is empty - - An array, list or other collection implementing IEnumerable - The message to be displayed on failure - Arguments to be used in formatting the message - - - - Assert that an array, list or other collection is empty - - An array, list or other collection implementing IEnumerable - The message to be displayed on failure - - - - Assert that an array,list or other collection is empty - - An array, list or other collection implementing IEnumerable - - - - Assert that an array, list or other collection is empty - - An array, list or other collection implementing IEnumerable - The message to be displayed on failure - Arguments to be used in formatting the message - - - - Assert that an array, list or other collection is empty - - An array, list or other collection implementing IEnumerable - The message to be displayed on failure - - - - Assert that an array,list or other collection is empty - - An array, list or other collection implementing IEnumerable - - - - Assert that an array, list or other collection is ordered - - An array, list or other collection implementing IEnumerable - The message to be displayed on failure - Arguments to be used in formatting the message - - - - Assert that an array, list or other collection is ordered - - An array, list or other collection implementing IEnumerable - The message to be displayed on failure - - - - Assert that an array, list or other collection is ordered - - An array, list or other collection implementing IEnumerable - - - - Assert that an array, list or other collection is ordered - - An array, list or other collection implementing IEnumerable - A custom comparer to perform the comparisons - The message to be displayed on failure - Arguments to be used in formatting the message - - - - Assert that an array, list or other collection is ordered - - An array, list or other collection implementing IEnumerable - A custom comparer to perform the comparisons - The message to be displayed on failure - - - - Assert that an array, list or other collection is ordered - - An array, list or other collection implementing IEnumerable - A custom comparer to perform the comparisons - - - - Static helper class used in the constraint-based syntax - - - - - Creates a new SubstringConstraint - - The value of the substring - A SubstringConstraint - - - - Creates a new CollectionContainsConstraint. - - The item that should be found. - A new CollectionContainsConstraint - - - - Summary description for DirectoryAssert - - - - - The Equals method throws an AssertionException. This is done - to make sure there is no mistake by calling this function. - - - - - - - override the default ReferenceEquals to throw an AssertionException. This - implementation makes sure there is no mistake in calling this function - as part of Assert. - - - - - - - We don't actually want any instances of this object, but some people - like to inherit from it to add other static methods. Hence, the - protected constructor disallows any instances of this object. - - - - - Verifies that two directories are equal. Two directories are considered - equal if both are null, or if both have the same value byte for byte. - If they are not equal an is thrown. - - A directory containing the value that is expected - A directory containing the actual value - The message to display if directories are not equal - Arguments to be used in formatting the message - - - - Verifies that two directories are equal. Two directories are considered - equal if both are null, or if both have the same value byte for byte. - If they are not equal an is thrown. - - A directory containing the value that is expected - A directory containing the actual value - The message to display if directories are not equal - - - - Verifies that two directories are equal. Two directories are considered - equal if both are null, or if both have the same value byte for byte. - If they are not equal an is thrown. - - A directory containing the value that is expected - A directory containing the actual value - - - - Verifies that two directories are equal. Two directories are considered - equal if both are null, or if both have the same value byte for byte. - If they are not equal an is thrown. - - A directory path string containing the value that is expected - A directory path string containing the actual value - The message to display if directories are not equal - Arguments to be used in formatting the message - - - - Verifies that two directories are equal. Two directories are considered - equal if both are null, or if both have the same value byte for byte. - If they are not equal an is thrown. - - A directory path string containing the value that is expected - A directory path string containing the actual value - The message to display if directories are not equal - - - - Verifies that two directories are equal. Two directories are considered - equal if both are null, or if both have the same value byte for byte. - If they are not equal an is thrown. - - A directory path string containing the value that is expected - A directory path string containing the actual value - - - - Asserts that two directories are not equal. If they are equal - an is thrown. - - A directory containing the value that is expected - A directory containing the actual value - The message to display if directories are not equal - Arguments to be used in formatting the message - - - - Asserts that two directories are not equal. If they are equal - an is thrown. - - A directory containing the value that is expected - A directory containing the actual value - The message to display if directories are not equal - - - - Asserts that two directories are not equal. If they are equal - an is thrown. - - A directory containing the value that is expected - A directory containing the actual value - - - - Asserts that two directories are not equal. If they are equal - an is thrown. - - A directory path string containing the value that is expected - A directory path string containing the actual value - The message to display if directories are equal - Arguments to be used in formatting the message - - - - Asserts that two directories are not equal. If they are equal - an is thrown. - - A directory path string containing the value that is expected - A directory path string containing the actual value - The message to display if directories are equal - - - - Asserts that two directories are not equal. If they are equal - an is thrown. - - A directory path string containing the value that is expected - A directory path string containing the actual value - - - - Asserts that the directory is empty. If it is not empty - an is thrown. - - A directory to search - The message to display if directories are not equal - Arguments to be used in formatting the message - - - - Asserts that the directory is empty. If it is not empty - an is thrown. - - A directory to search - The message to display if directories are not equal - - - - Asserts that the directory is empty. If it is not empty - an is thrown. - - A directory to search - - - - Asserts that the directory is empty. If it is not empty - an is thrown. - - A directory to search - The message to display if directories are not equal - Arguments to be used in formatting the message - - - - Asserts that the directory is empty. If it is not empty - an is thrown. - - A directory to search - The message to display if directories are not equal - - - - Asserts that the directory is empty. If it is not empty - an is thrown. - - A directory to search - - - - Asserts that the directory is not empty. If it is empty - an is thrown. - - A directory to search - The message to display if directories are not equal - Arguments to be used in formatting the message - - - - Asserts that the directory is not empty. If it is empty - an is thrown. - - A directory to search - The message to display if directories are not equal - - - - Asserts that the directory is not empty. If it is empty - an is thrown. - - A directory to search - - - - Asserts that the directory is not empty. If it is empty - an is thrown. - - A directory to search - The message to display if directories are not equal - Arguments to be used in formatting the message - - - - Asserts that the directory is not empty. If it is empty - an is thrown. - - A directory to search - The message to display if directories are not equal - - - - Asserts that the directory is not empty. If it is empty - an is thrown. - - A directory to search - - - - Asserts that path contains actual as a subdirectory or - an is thrown. - - A directory to search - sub-directory asserted to exist under directory - The message to display if directory is not within the path - Arguments to be used in formatting the message - - - - Asserts that path contains actual as a subdirectory or - an is thrown. - - A directory to search - sub-directory asserted to exist under directory - The message to display if directory is not within the path - - - - Asserts that path contains actual as a subdirectory or - an is thrown. - - A directory to search - sub-directory asserted to exist under directory - - - - Asserts that path contains actual as a subdirectory or - an is thrown. - - A directory to search - sub-directory asserted to exist under directory - The message to display if directory is not within the path - Arguments to be used in formatting the message - - - - Asserts that path contains actual as a subdirectory or - an is thrown. - - A directory to search - sub-directory asserted to exist under directory - The message to display if directory is not within the path - - - - Asserts that path contains actual as a subdirectory or - an is thrown. - - A directory to search - sub-directory asserted to exist under directory - - - - Asserts that path does not contain actual as a subdirectory or - an is thrown. - - A directory to search - sub-directory asserted to exist under directory - The message to display if directory is not within the path - Arguments to be used in formatting the message - - - - Asserts that path does not contain actual as a subdirectory or - an is thrown. - - A directory to search - sub-directory asserted to exist under directory - The message to display if directory is not within the path - - - - Asserts that path does not contain actual as a subdirectory or - an is thrown. - - A directory to search - sub-directory asserted to exist under directory - - - - Asserts that path does not contain actual as a subdirectory or - an is thrown. - - A directory to search - sub-directory asserted to exist under directory - The message to display if directory is not within the path - Arguments to be used in formatting the message - - - - Asserts that path does not contain actual as a subdirectory or - an is thrown. - - A directory to search - sub-directory asserted to exist under directory - The message to display if directory is not within the path - - - - Asserts that path does not contain actual as a subdirectory or - an is thrown. - - A directory to search - sub-directory asserted to exist under directory - - - - Summary description for FileAssert. - - - - - The Equals method throws an AssertionException. This is done - to make sure there is no mistake by calling this function. - - - - - - - override the default ReferenceEquals to throw an AssertionException. This - implementation makes sure there is no mistake in calling this function - as part of Assert. - - - - - - - We don't actually want any instances of this object, but some people - like to inherit from it to add other static methods. Hence, the - protected constructor disallows any instances of this object. - - - - - Verifies that two Streams are equal. Two Streams are considered - equal if both are null, or if both have the same value byte for byte. - If they are not equal an is thrown. - - The expected Stream - The actual Stream - The message to display if Streams are not equal - Arguments to be used in formatting the message - - - - Verifies that two Streams are equal. Two Streams are considered - equal if both are null, or if both have the same value byte for byte. - If they are not equal an is thrown. - - The expected Stream - The actual Stream - The message to display if objects are not equal - - - - Verifies that two Streams are equal. Two Streams are considered - equal if both are null, or if both have the same value byte for byte. - If they are not equal an is thrown. - - The expected Stream - The actual Stream - - - - Verifies that two files are equal. Two files are considered - equal if both are null, or if both have the same value byte for byte. - If they are not equal an is thrown. - - A file containing the value that is expected - A file containing the actual value - The message to display if Streams are not equal - Arguments to be used in formatting the message - - - - Verifies that two files are equal. Two files are considered - equal if both are null, or if both have the same value byte for byte. - If they are not equal an is thrown. - - A file containing the value that is expected - A file containing the actual value - The message to display if objects are not equal - - - - Verifies that two files are equal. Two files are considered - equal if both are null, or if both have the same value byte for byte. - If they are not equal an is thrown. - - A file containing the value that is expected - A file containing the actual value - - - - Verifies that two files are equal. Two files are considered - equal if both are null, or if both have the same value byte for byte. - If they are not equal an is thrown. - - The path to a file containing the value that is expected - The path to a file containing the actual value - The message to display if Streams are not equal - Arguments to be used in formatting the message - - - - Verifies that two files are equal. Two files are considered - equal if both are null, or if both have the same value byte for byte. - If they are not equal an is thrown. - - The path to a file containing the value that is expected - The path to a file containing the actual value - The message to display if objects are not equal - - - - Verifies that two files are equal. Two files are considered - equal if both are null, or if both have the same value byte for byte. - If they are not equal an is thrown. - - The path to a file containing the value that is expected - The path to a file containing the actual value - - - - Asserts that two Streams are not equal. If they are equal - an is thrown. - - The expected Stream - The actual Stream - The message to be displayed when the two Stream are the same. - Arguments to be used in formatting the message - - - - Asserts that two Streams are not equal. If they are equal - an is thrown. - - The expected Stream - The actual Stream - The message to be displayed when the Streams are the same. - - - - Asserts that two Streams are not equal. If they are equal - an is thrown. - - The expected Stream - The actual Stream - - - - Asserts that two files are not equal. If they are equal - an is thrown. - - A file containing the value that is expected - A file containing the actual value - The message to display if Streams are not equal - Arguments to be used in formatting the message - - - - Asserts that two files are not equal. If they are equal - an is thrown. - - A file containing the value that is expected - A file containing the actual value - The message to display if objects are not equal - - - - Asserts that two files are not equal. If they are equal - an is thrown. - - A file containing the value that is expected - A file containing the actual value - - - - Asserts that two files are not equal. If they are equal - an is thrown. - - The path to a file containing the value that is expected - The path to a file containing the actual value - The message to display if Streams are not equal - Arguments to be used in formatting the message - - - - Asserts that two files are not equal. If they are equal - an is thrown. - - The path to a file containing the value that is expected - The path to a file containing the actual value - The message to display if objects are not equal - - - - Asserts that two files are not equal. If they are equal - an is thrown. - - The path to a file containing the value that is expected - The path to a file containing the actual value - - - - GlobalSettings is a place for setting default values used - by the framework in performing asserts. - - - - - Default tolerance for floating point equality - - - - - Helper class with properties and methods that supply - a number of constraints used in Asserts. - - - - - Returns a ConstraintExpression, which will apply - the following constraint to all members of a collection, - succeeding only if a specified number of them succeed. - - - - - Returns a new PropertyConstraintExpression, which will either - test for the existence of the named property on the object - being tested or apply any following constraint to that property. - - - - - Returns a new AttributeConstraint checking for the - presence of a particular attribute on an object. - - - - - Returns a new AttributeConstraint checking for the - presence of a particular attribute on an object. - - - - - Returns a new CollectionContainsConstraint checking for the - presence of a particular object in the collection. - - - - - Returns a ConstraintExpression that negates any - following constraint. - - - - - Returns a ConstraintExpression, which will apply - the following constraint to all members of a collection, - succeeding if all of them succeed. - - - - - Returns a ConstraintExpression, which will apply - the following constraint to all members of a collection, - succeeding if at least one of them succeeds. - - - - - Returns a ConstraintExpression, which will apply - the following constraint to all members of a collection, - succeeding if all of them fail. - - - - - Returns a new ConstraintExpression, which will apply the following - constraint to the Length property of the object being tested. - - - - - Returns a new ConstraintExpression, which will apply the following - constraint to the Count property of the object being tested. - - - - - Returns a new ConstraintExpression, which will apply the following - constraint to the Message property of the object being tested. - - - - - Returns a new ConstraintExpression, which will apply the following - constraint to the InnerException property of the object being tested. - - - - - Interface implemented by a user fixture in order to - validate any expected exceptions. It is only called - for test methods marked with the ExpectedException - attribute. - - - - - Method to handle an expected exception - - The exception to be handled - - - - Helper class with properties and methods that supply - a number of constraints used in Asserts. - - - - - Returns a constraint that tests two items for equality - - - - - Returns a constraint that tests that two references are the same object - - - - - Returns a constraint that tests whether the - actual value is greater than the suppled argument - - - - - Returns a constraint that tests whether the - actual value is greater than or equal to the suppled argument - - - - - Returns a constraint that tests whether the - actual value is greater than or equal to the suppled argument - - - - - Returns a constraint that tests whether the - actual value is less than the suppled argument - - - - - Returns a constraint that tests whether the - actual value is less than or equal to the suppled argument - - - - - Returns a constraint that tests whether the - actual value is less than or equal to the suppled argument - - - - - Returns a constraint that tests whether the actual - value is of the exact type supplied as an argument. - - - - - Returns a constraint that tests whether the actual - value is of the exact type supplied as an argument. - - - - - Returns a constraint that tests whether the actual value - is of the type supplied as an argument or a derived type. - - - - - Returns a constraint that tests whether the actual value - is of the type supplied as an argument or a derived type. - - - - - Returns a constraint that tests whether the actual value - is of the type supplied as an argument or a derived type. - - - - - Returns a constraint that tests whether the actual value - is of the type supplied as an argument or a derived type. - - - - - Returns a constraint that tests whether the actual value - is assignable from the type supplied as an argument. - - - - - Returns a constraint that tests whether the actual value - is assignable from the type supplied as an argument. - - - - - Returns a constraint that tests whether the actual value - is assignable from the type supplied as an argument. - - - - - Returns a constraint that tests whether the actual value - is assignable from the type supplied as an argument. - - - - - Returns a constraint that tests whether the actual value - is a collection containing the same elements as the - collection supplied as an argument. - - - - - Returns a constraint that tests whether the actual value - is a subset of the collection supplied as an argument. - - - - - Returns a constraint that succeeds if the actual - value contains the substring supplied as an argument. - - - - - Returns a constraint that succeeds if the actual - value starts with the substring supplied as an argument. - - - - - Returns a constraint that succeeds if the actual - value ends with the substring supplied as an argument. - - - - - Returns a constraint that succeeds if the actual - value matches the Regex pattern supplied as an argument. - - - - - Returns a constraint that tests whether the path provided - is the same as an expected path after canonicalization. - - - - - Returns a constraint that tests whether the path provided - is the same path or under an expected path after canonicalization. - - - - - Returns a constraint that tests whether the path provided - is the same path or under an expected path after canonicalization. - - - - - Returns a constraint that tests whether the actual value falls - within a specified range. - - - - - Returns a ConstraintExpression that negates any - following constraint. - - - - - Returns a ConstraintExpression, which will apply - the following constraint to all members of a collection, - succeeding if all of them succeed. - - - - - Returns a constraint that tests for null - - - - - Returns a constraint that tests for True - - - - - Returns a constraint that tests for False - - - - - Returns a constraint that tests for a positive value - - - - - Returns a constraint that tests for a negative value - - - - - Returns a constraint that tests for NaN - - - - - Returns a constraint that tests for empty - - - - - Returns a constraint that tests whether a collection - contains all unique items. - - - - - Returns a constraint that tests whether an object graph is serializable in binary format. - - - - - Returns a constraint that tests whether an object graph is serializable in xml format. - - - - - Returns a constraint that tests whether a collection is ordered - - - - - The Iz class is a synonym for Is intended for use in VB, - which regards Is as a keyword. - - - - - The List class is a helper class with properties and methods - that supply a number of constraints used with lists and collections. - - - - - List.Map returns a ListMapper, which can be used to map - the original collection to another collection. - - - - - - - ListMapper is used to transform a collection used as an actual argument - producing another collection to be used in the assertion. - - - - - Construct a ListMapper based on a collection - - The collection to be transformed - - - - Produces a collection containing all the values of a property - - The collection of property values - - - - - Randomizer returns a set of random values in a repeatable - way, to allow re-running of tests if necessary. - - - - - Get a randomizer for a particular member, returning - one that has already been created if it exists. - This ensures that the same values are generated - each time the tests are reloaded. - - - - - Get a randomizer for a particular parameter, returning - one that has already been created if it exists. - This ensures that the same values are generated - each time the tests are reloaded. - - - - - Construct a randomizer using a random seed - - - - - Construct a randomizer using a specified seed - - - - - Return an array of random doubles between 0.0 and 1.0. - - - - - - - Return an array of random doubles with values in a specified range. - - - - - Return an array of random ints with values in a specified range. - - - - - Get a random seed for use in creating a randomizer. - - - - - The SpecialValue enum is used to represent TestCase arguments - that cannot be used as arguments to an Attribute. - - - - - Null represents a null value, which cannot be used as an - argument to an attriute under .NET 1.x - - - - - Basic Asserts on strings. - - - - - The Equals method throws an AssertionException. This is done - to make sure there is no mistake by calling this function. - - - - - - - override the default ReferenceEquals to throw an AssertionException. This - implementation makes sure there is no mistake in calling this function - as part of Assert. - - - - - - - Asserts that a string is found within another string. - - The expected string - The string to be examined - The message to display in case of failure - Arguments used in formatting the message - - - - Asserts that a string is found within another string. - - The expected string - The string to be examined - The message to display in case of failure - - - - Asserts that a string is found within another string. - - The expected string - The string to be examined - - - - Asserts that a string is not found within another string. - - The expected string - The string to be examined - The message to display in case of failure - Arguments used in formatting the message - - - - Asserts that a string is found within another string. - - The expected string - The string to be examined - The message to display in case of failure - - - - Asserts that a string is found within another string. - - The expected string - The string to be examined - - - - Asserts that a string starts with another string. - - The expected string - The string to be examined - The message to display in case of failure - Arguments used in formatting the message - - - - Asserts that a string starts with another string. - - The expected string - The string to be examined - The message to display in case of failure - - - - Asserts that a string starts with another string. - - The expected string - The string to be examined - - - - Asserts that a string does not start with another string. - - The expected string - The string to be examined - The message to display in case of failure - Arguments used in formatting the message - - - - Asserts that a string does not start with another string. - - The expected string - The string to be examined - The message to display in case of failure - - - - Asserts that a string does not start with another string. - - The expected string - The string to be examined - - - - Asserts that a string ends with another string. - - The expected string - The string to be examined - The message to display in case of failure - Arguments used in formatting the message - - - - Asserts that a string ends with another string. - - The expected string - The string to be examined - The message to display in case of failure - - - - Asserts that a string ends with another string. - - The expected string - The string to be examined - - - - Asserts that a string does not end with another string. - - The expected string - The string to be examined - The message to display in case of failure - Arguments used in formatting the message - - - - Asserts that a string does not end with another string. - - The expected string - The string to be examined - The message to display in case of failure - - - - Asserts that a string does not end with another string. - - The expected string - The string to be examined - - - - Asserts that two strings are equal, without regard to case. - - The expected string - The actual string - The message to display in case of failure - Arguments used in formatting the message - - - - Asserts that two strings are equal, without regard to case. - - The expected string - The actual string - The message to display in case of failure - - - - Asserts that two strings are equal, without regard to case. - - The expected string - The actual string - - - - Asserts that two strings are not equal, without regard to case. - - The expected string - The actual string - The message to display in case of failure - Arguments used in formatting the message - - - - Asserts that two strings are Notequal, without regard to case. - - The expected string - The actual string - The message to display in case of failure - - - - Asserts that two strings are not equal, without regard to case. - - The expected string - The actual string - - - - Asserts that a string matches an expected regular expression pattern. - - The regex pattern to be matched - The actual string - The message to display in case of failure - Arguments used in formatting the message - - - - Asserts that a string matches an expected regular expression pattern. - - The regex pattern to be matched - The actual string - The message to display in case of failure - - - - Asserts that a string matches an expected regular expression pattern. - - The regex pattern to be matched - The actual string - - - - Asserts that a string does not match an expected regular expression pattern. - - The regex pattern to be used - The actual string - The message to display in case of failure - Arguments used in formatting the message - - - - Asserts that a string does not match an expected regular expression pattern. - - The regex pattern to be used - The actual string - The message to display in case of failure - - - - Asserts that a string does not match an expected regular expression pattern. - - The regex pattern to be used - The actual string - - - - The TestCaseData class represents a set of arguments - and other parameter info to be used for a parameterized - test case. It provides a number of instance modifiers - for use in initializing the test case. - - Note: Instance modifiers are getters that return - the same instance after modifying it's state. - - - - - The argument list to be provided to the test - - - - - The expected result to be returned - - - - - Set to true if this has an expected result - - - - - The expected exception Type - - - - - The FullName of the expected exception - - - - - The name to be used for the test - - - - - The description of the test - - - - - A dictionary of properties, used to add information - to tests without requiring the class to change. - - - - - If true, indicates that the test case is to be ignored - - - - - If true, indicates that the test case is marked explicit - - - - - The reason for ignoring a test case - - - - - Initializes a new instance of the class. - - The arguments. - - - - Initializes a new instance of the class. - - The argument. - - - - Initializes a new instance of the class. - - The first argument. - The second argument. - - - - Initializes a new instance of the class. - - The first argument. - The second argument. - The third argument. - - - - Sets the expected result for the test - - The expected result - A modified TestCaseData - - - - Sets the expected exception type for the test - - Type of the expected exception. - The modified TestCaseData instance - - - - Sets the expected exception type for the test - - FullName of the expected exception. - The modified TestCaseData instance - - - - Sets the name of the test case - - The modified TestCaseData instance - - - - Sets the description for the test case - being constructed. - - The description. - The modified TestCaseData instance. - - - - Applies a category to the test - - - - - - - Applies a named property to the test - - - - - - - - Applies a named property to the test - - - - - - - - Applies a named property to the test - - - - - - - - Ignores this TestCase. - - - - - - Ignores this TestCase, specifying the reason. - - The reason. - - - - - Marks this TestCase as Explicit - - - - - - Marks this TestCase as Explicit, specifying the reason. - - The reason. - - - - - Gets the argument list to be provided to the test - - - - - Gets the expected result - - - - - Returns true if the result has been set - - - - - Gets the expected exception Type - - - - - Gets the FullName of the expected exception - - - - - Gets the name to be used for the test - - - - - Gets the description of the test - - - - - Gets a value indicating whether this is ignored. - - true if ignored; otherwise, false. - - - - Gets a value indicating whether this is explicit. - - true if explicit; otherwise, false. - - - - Gets the ignore reason. - - The ignore reason. - - - - Gets a list of categories associated with this test. - - - - - Gets the property dictionary for this test - - - - - Provide the context information of the current test - - - - - Constructs a TestContext using the provided context dictionary - - A context dictionary - - - - Get the current test context. This is created - as needed. The user may save the context for - use within a test, but it should not be used - outside the test for which it is created. - - - - - Gets a TestAdapter representing the currently executing test in this context. - - - - - Gets a ResultAdapter representing the current result for the test - executing in this context. - - - - - Gets the directory containing the current test assembly. - - - - - Gets the directory to be used for outputing files created - by this test run. - - - - - TestAdapter adapts a Test for consumption by - the user test code. - - - - - Constructs a TestAdapter for this context - - The context dictionary - - - - The name of the test. - - - - - The FullName of the test - - - - - The properties of the test. - - - - - ResultAdapter adapts a TestResult for consumption by - the user test code. - - - - - Construct a ResultAdapter for a context - - The context holding the result - - - - The TestState of current test. This maps to the ResultState - used in nunit.core and is subject to change in the future. - - - - - The TestStatus of current test. This enum will be used - in future versions of NUnit and so is to be preferred - to the TestState value. - - - - - Provides details about a test - - - - - Creates an instance of TestDetails - - The fixture that the test is a member of, if available. - The method that implements the test, if available. - The full name of the test. - A string representing the type of test, e.g. "Test Case". - Indicates if the test represents a suite of tests. - - - - The fixture that the test is a member of, if available. - - - - - The method that implements the test, if available. - - - - - The full name of the test. - - - - - A string representing the type of test, e.g. "Test Case". - - - - - Indicates if the test represents a suite of tests. - - - - - The ResultState enum indicates the result of running a test - - - - - The result is inconclusive - - - - - The test was not runnable. - - - - - The test has been skipped. - - - - - The test has been ignored. - - - - - The test succeeded - - - - - The test failed - - - - - The test encountered an unexpected exception - - - - - The test was cancelled by the user - - - - - The TestStatus enum indicates the result of running a test - - - - - The test was inconclusive - - - - - The test has skipped - - - - - The test succeeded - - - - - The test failed - - - - - Helper class with static methods used to supply constraints - that operate on strings. - - - - - Returns a constraint that succeeds if the actual - value contains the substring supplied as an argument. - - - - - Returns a constraint that fails if the actual - value contains the substring supplied as an argument. - - - - - Returns a constraint that succeeds if the actual - value starts with the substring supplied as an argument. - - - - - Returns a constraint that fails if the actual - value starts with the substring supplied as an argument. - - - - - Returns a constraint that succeeds if the actual - value ends with the substring supplied as an argument. - - - - - Returns a constraint that fails if the actual - value ends with the substring supplied as an argument. - - - - - Returns a constraint that succeeds if the actual - value matches the Regex pattern supplied as an argument. - - - - - Returns a constraint that fails if the actual - value matches the pattern supplied as an argument. - - - - - Returns a ConstraintExpression, which will apply - the following constraint to all members of a collection, - succeeding if all of them succeed. - - - - - TextMessageWriter writes constraint descriptions and messages - in displayable form as a text stream. It tailors the display - of individual message components to form the standard message - format of NUnit assertion failure messages. - - - - - Prefix used for the expected value line of a message - - - - - Prefix used for the actual value line of a message - - - - - Length of a message prefix - - - - - Construct a TextMessageWriter - - - - - Construct a TextMessageWriter, specifying a user message - and optional formatting arguments. - - - - - - - Method to write single line message with optional args, usually - written to precede the general failure message, at a givel - indentation level. - - The indentation level of the message - The message to be written - Any arguments used in formatting the message - - - - Display Expected and Actual lines for a constraint. This - is called by MessageWriter's default implementation of - WriteMessageTo and provides the generic two-line display. - - The constraint that failed - - - - Display Expected and Actual lines for given values. This - method may be called by constraints that need more control over - the display of actual and expected values than is provided - by the default implementation. - - The expected value - The actual value causing the failure - - - - Display Expected and Actual lines for given values, including - a tolerance value on the expected line. - - The expected value - The actual value causing the failure - The tolerance within which the test was made - - - - Display the expected and actual string values on separate lines. - If the mismatch parameter is >=0, an additional line is displayed - line containing a caret that points to the mismatch point. - - The expected string value - The actual string value - The point at which the strings don't match or -1 - If true, case is ignored in string comparisons - If true, clip the strings to fit the max line length - - - - Writes the text for a connector. - - The connector. - - - - Writes the text for a predicate. - - The predicate. - - - - Write the text for a modifier. - - The modifier. - - - - Writes the text for an expected value. - - The expected value. - - - - Writes the text for an actual value. - - The actual value. - - - - Writes the text for a generalized value. - - The value. - - - - Writes the text for a collection value, - starting at a particular point, to a max length - - The collection containing elements to write. - The starting point of the elements to write - The maximum number of elements to write - - - - Write the generic 'Expected' line for a constraint - - The constraint that failed - - - - Write the generic 'Expected' line for a given value - - The expected value - - - - Write the generic 'Expected' line for a given value - and tolerance. - - The expected value - The tolerance within which the test was made - - - - Write the generic 'Actual' line for a constraint - - The constraint for which the actual value is to be written - - - - Write the generic 'Actual' line for a given value - - The actual value causing a failure - - - - Gets or sets the maximum line length for this writer - - - - - Helper class with properties and methods that supply - constraints that operate on exceptions. - - - - - Creates a constraint specifying the exact type of exception expected - - - - - Creates a constraint specifying the exact type of exception expected - - - - - Creates a constraint specifying the type of exception expected - - - - - Creates a constraint specifying the type of exception expected - - - - - Creates a constraint specifying an expected exception - - - - - Creates a constraint specifying an exception with a given InnerException - - - - - Creates a constraint specifying an expected TargetInvocationException - - - - - Creates a constraint specifying an expected TargetInvocationException - - - - - Creates a constraint specifying an expected TargetInvocationException - - - - - Creates a constraint specifying that no exception is thrown - - - -