Browse Source

update AvalonEdit to 5.0.2

pull/478/merge
Siegfried Pammer 10 years ago
parent
commit
177519e19b
  1. 2
      AvalonEdit/Documentation/Code Completion.aml
  2. 192
      AvalonEdit/Documentation/ICSharpCode.AvalonEdit.shfbproj
  3. 563
      AvalonEdit/Documentation/License.html
  4. 10
      AvalonEdit/Documentation/Welcome.aml
  5. 57
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/Document/ChangeTrackingTest.cs
  6. 19
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/Document/CollapsingTests.cs
  7. 19
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/Document/HeightTests.cs
  8. 28
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/Document/LineManagerTests.cs
  9. 19
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/Document/RandomizedLineManagerTest.cs
  10. 24
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/Document/TextAnchorTest.cs
  11. 24
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/Document/TextSegmentTreeTest.cs
  12. 22
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/Document/TextUtilitiesTests.cs
  13. 92
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/Document/UndoStackTests.cs
  14. 22
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/Editing/ChangeDocumentTests.cs
  15. 56
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/Editing/TextSegmentReadOnlySectionTests.cs
  16. 182
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/Highlighting/HighlightedLineMergeTests.cs
  17. 27
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/Highlighting/HtmlClipboardTests.cs
  18. 65
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/ICSharpCode.AvalonEdit.Tests.csproj
  19. 44
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/MultipleUIThreads.cs
  20. 26
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/Properties/AssemblyInfo.cs
  21. 22
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/Search/FindTests.cs
  22. 76
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/Utils/CaretNavigationTests.cs
  23. 55
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/Utils/CompressingTreeListTests.cs
  24. 19
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/Utils/ExtensionMethodsTests.cs
  25. 19
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/Utils/IndentationStringTests.cs
  26. 19
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/Utils/RopeTests.cs
  27. 19
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/WeakReferenceTests.cs
  28. 223
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/XmlParser/ParserTests.cs
  29. 115
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/XmlParser/TextReplacementTests.cs
  30. BIN
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/XmlParser/W3C.zip
  31. 15
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/app.config
  32. 4
      AvalonEdit/ICSharpCode.AvalonEdit.Tests/packages.config
  33. 19
      AvalonEdit/ICSharpCode.AvalonEdit/AvalonEditCommands.cs
  34. 55
      AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.cs
  35. 2
      AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.xaml
  36. 19
      AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionListBox.cs
  37. 22
      AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindow.cs
  38. 23
      AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindowBase.cs
  39. 25
      AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/ICompletionData.cs
  40. 19
      AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/IOverloadProvider.cs
  41. 23
      AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/InsightWindow.cs
  42. 4
      AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/InsightWindow.xaml
  43. 21
      AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/OverloadInsightWindow.cs
  44. 19
      AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/OverloadViewer.cs
  45. 140
      AvalonEdit/ICSharpCode.AvalonEdit/Document/ChangeTrackingCheckpoint.cs
  46. 86
      AvalonEdit/ICSharpCode.AvalonEdit/Document/DocumentChangeEventArgs.cs
  47. 19
      AvalonEdit/ICSharpCode.AvalonEdit/Document/DocumentChangeOperation.cs
  48. 32
      AvalonEdit/ICSharpCode.AvalonEdit/Document/DocumentLine.cs
  49. 19
      AvalonEdit/ICSharpCode.AvalonEdit/Document/DocumentLineTree.cs
  50. 86
      AvalonEdit/ICSharpCode.AvalonEdit/Document/DocumentTextWriter.cs
  51. 19
      AvalonEdit/ICSharpCode.AvalonEdit/Document/GapTextBuffer.cs
  52. 343
      AvalonEdit/ICSharpCode.AvalonEdit/Document/IDocument.cs
  53. 26
      AvalonEdit/ICSharpCode.AvalonEdit/Document/ILineTracker.cs
  54. 142
      AvalonEdit/ICSharpCode.AvalonEdit/Document/ITextAnchor.cs
  55. 389
      AvalonEdit/ICSharpCode.AvalonEdit/Document/ITextSource.cs
  56. 19
      AvalonEdit/ICSharpCode.AvalonEdit/Document/IUndoableOperation.cs
  57. 46
      AvalonEdit/ICSharpCode.AvalonEdit/Document/LineManager.cs
  58. 19
      AvalonEdit/ICSharpCode.AvalonEdit/Document/LineNode.cs
  59. 28
      AvalonEdit/ICSharpCode.AvalonEdit/Document/NewLineFinder.cs
  60. 29
      AvalonEdit/ICSharpCode.AvalonEdit/Document/OffsetChangeMap.cs
  61. 169
      AvalonEdit/ICSharpCode.AvalonEdit/Document/RopeTextSource.cs
  62. 80
      AvalonEdit/ICSharpCode.AvalonEdit/Document/SimpleSegment.cs
  63. 86
      AvalonEdit/ICSharpCode.AvalonEdit/Document/TextAnchor.cs
  64. 19
      AvalonEdit/ICSharpCode.AvalonEdit/Document/TextAnchorNode.cs
  65. 23
      AvalonEdit/ICSharpCode.AvalonEdit/Document/TextAnchorTree.cs
  66. 423
      AvalonEdit/ICSharpCode.AvalonEdit/Document/TextDocument.cs
  67. 22
      AvalonEdit/ICSharpCode.AvalonEdit/Document/TextDocumentWeakEventManager.cs
  68. 153
      AvalonEdit/ICSharpCode.AvalonEdit/Document/TextLocation.cs
  69. 40
      AvalonEdit/ICSharpCode.AvalonEdit/Document/TextSegment.cs
  70. 24
      AvalonEdit/ICSharpCode.AvalonEdit/Document/TextSegmentCollection.cs
  71. 136
      AvalonEdit/ICSharpCode.AvalonEdit/Document/TextSourceVersionProvider.cs
  72. 170
      AvalonEdit/ICSharpCode.AvalonEdit/Document/TextUtilities.cs
  73. 19
      AvalonEdit/ICSharpCode.AvalonEdit/Document/UndoOperationGroup.cs
  74. 24
      AvalonEdit/ICSharpCode.AvalonEdit/Document/UndoStack.cs
  75. 28
      AvalonEdit/ICSharpCode.AvalonEdit/Document/WeakLineTracker.cs
  76. 19
      AvalonEdit/ICSharpCode.AvalonEdit/Editing/AbstractMargin.cs
  77. 83
      AvalonEdit/ICSharpCode.AvalonEdit/Editing/Caret.cs
  78. 35
      AvalonEdit/ICSharpCode.AvalonEdit/Editing/CaretLayer.cs
  79. 257
      AvalonEdit/ICSharpCode.AvalonEdit/Editing/CaretNavigationCommandHandler.cs
  80. 19
      AvalonEdit/ICSharpCode.AvalonEdit/Editing/CaretWeakEventHandler.cs
  81. 19
      AvalonEdit/ICSharpCode.AvalonEdit/Editing/DottedLineMargin.cs
  82. 19
      AvalonEdit/ICSharpCode.AvalonEdit/Editing/DragDropException.cs
  83. 172
      AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs
  84. 32
      AvalonEdit/ICSharpCode.AvalonEdit/Editing/EmptySelection.cs
  85. 23
      AvalonEdit/ICSharpCode.AvalonEdit/Editing/IReadOnlySectionProvider.cs
  86. 221
      AvalonEdit/ICSharpCode.AvalonEdit/Editing/ImeNativeWrapper.cs
  87. 165
      AvalonEdit/ICSharpCode.AvalonEdit/Editing/ImeSupport.cs
  88. 42
      AvalonEdit/ICSharpCode.AvalonEdit/Editing/LineNumberMargin.cs
  89. 26
      AvalonEdit/ICSharpCode.AvalonEdit/Editing/NoReadOnlySections.cs
  90. 182
      AvalonEdit/ICSharpCode.AvalonEdit/Editing/RectangleSelection.cs
  91. 64
      AvalonEdit/ICSharpCode.AvalonEdit/Editing/Selection.cs
  92. 24
      AvalonEdit/ICSharpCode.AvalonEdit/Editing/SelectionColorizer.cs
  93. 19
      AvalonEdit/ICSharpCode.AvalonEdit/Editing/SelectionLayer.cs
  94. 77
      AvalonEdit/ICSharpCode.AvalonEdit/Editing/SelectionMouseHandler.cs
  95. 23
      AvalonEdit/ICSharpCode.AvalonEdit/Editing/SelectionSegment.cs
  96. 66
      AvalonEdit/ICSharpCode.AvalonEdit/Editing/SimpleSelection.cs
  97. 117
      AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextArea.cs
  98. 21
      AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextAreaDefaultInputHandlers.cs
  99. 19
      AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextAreaInputHandler.cs
  100. 27
      AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextSegmentReadOnlySectionProvider.cs
  101. Some files were not shown because too many files have changed in this diff Show More

2
AvalonEdit/Documentation/Code Completion.aml

@ -123,7 +123,7 @@ public class MyCompletionData : ICompletionData
see: see:
<externalLink> <externalLink>
<linkText>Code Completion in SharpDevelop</linkText> <linkText>Code Completion in SharpDevelop</linkText>
<linkUri>http://wiki.sharpdevelop.net/CodeCompletion.ashx</linkUri> <linkUri>https://github.com/icsharpcode/SharpDevelop/wiki/Code-Completion</linkUri>
<linkTarget>_blank</linkTarget> <linkTarget>_blank</linkTarget>
</externalLink> </externalLink>
</para> </para>

192
AvalonEdit/Documentation/ICSharpCode.AvalonEdit.shfbproj

@ -8,7 +8,7 @@
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<SchemaVersion>2.0</SchemaVersion> <SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{850b6602-0a7f-413a-864a-e816b98d7407}</ProjectGuid> <ProjectGuid>{850b6602-0a7f-413a-864a-e816b98d7407}</ProjectGuid>
<SHFBSchemaVersion>1.9.3.0</SHFBSchemaVersion> <SHFBSchemaVersion>1.9.9.0</SHFBSchemaVersion>
<!-- AssemblyName, Name, and RootNamespace are not used by SHFB but Visual <!-- AssemblyName, Name, and RootNamespace are not used by SHFB but Visual
Studio adds them anyway --> Studio adds them anyway -->
<AssemblyName>Documentation</AssemblyName> <AssemblyName>Documentation</AssemblyName>
@ -31,126 +31,110 @@
</WorkingPath> </WorkingPath>
<BuildLogFile> <BuildLogFile>
</BuildLogFile> </BuildLogFile>
<FrameworkVersion>.NET 4.0.30319</FrameworkVersion> <FrameworkVersion>.NET Framework 4.0</FrameworkVersion>
<HelpTitle>AvalonEdit</HelpTitle> <HelpTitle>AvalonEdit</HelpTitle>
<CopyrightText>Copyright 2008-2011, Daniel Grunwald</CopyrightText> <CopyrightText>Copyright 2008-2014, Daniel Grunwald</CopyrightText>
<PresentationStyle>Prototype</PresentationStyle> <PresentationStyle>Prototype</PresentationStyle>
<HelpFileVersion>4.1.0.0</HelpFileVersion> <HelpFileVersion>4.4.0.0</HelpFileVersion>
<ComponentConfigurations> <ComponentConfigurations>
<ComponentConfig id="Code Block Component" enabled="True"> <ComponentConfig id="Reflection Index Data (ESENT Cache)" enabled="True">
<component id="Code Block Component" type="SandcastleBuilder.Components.CodeBlockComponent" assembly="{@SHFBFolder}SandcastleBuilder.Components.dll"> <component id="Reflection Index Data (ESENT Cache)">
<!-- Base path for relative filenames in source attributes <index name="reflection" value="/reflection/apis/api" key="@id" cache="15" localCacheSize="2500" frameworkCachePath="{@LocalDataFolder}Cache\ReflectionIndexCache" projectCachePath="">
(optional) --> <data base="{@SHFBFolder}Data\Reflection" recurse="true" files="*.xml" duplicateWarning="false" groupId="ReflectionIndexCache">
<basePath value="{@HtmlEncProjectFolder}" /> {@ReferenceLinkNamespaceFiles}
<!-- Connect to language filter (optional). If omitted, </data>
language filtering is enabled by default. --> <data files="reflection.xml" groupId="Project_Ref_{@UniqueID}" />
<languageFilter value="true" /> </index>
<!-- Allow missing source files (Optional). If omitted, <copy name="reflection" source="*" target="/document/reference" />
it will generate errors if referenced source files
are missing. -->
<allowMissingSource value="false" />
<!-- Remove region markers from imported code blocks. If omitted,
region markers in imported code blocks are left alone. -->
<removeRegionMarkers value="false" />
<!-- Code colorizer options (required).
Attributes:
Language syntax configuration file (required)
XSLT style file (required)
"Copy" image file URL (required)
Default language (optional)
Enable line numbering (optional)
Enable outlining (optional)
Keep XML comment "see" tags within the code (optional)
Tab size override (optional, 0 = Use syntax file setting)
Use language name as default title (optional) -->
<colorizer syntaxFile="{@SHFBFolder}Colorizer\highlight.xml" styleFile="{@SHFBFolder}Colorizer\highlight.xsl" copyImageUrl="../icons/CopyCode.gif" language="cs" numberLines="false" outlining="false" keepSeeTags="false" tabSize="0" defaultTitle="true" />
</component> </component>
</ComponentConfig> </ComponentConfig>
<ComponentConfig id="IntelliSense Component" enabled="True"><component id="IntelliSense Component" type="SandcastleBuilder.Components.IntelliSenseComponent" assembly="{@SHFBFolder}SandcastleBuilder.Components.dll"> <ComponentConfig id="Comments Index Data (ESENT Cache)" enabled="True"><component id="Comments Index Data (ESENT Cache)">
<!-- Output options (optional) <index name="comments" value="/doc/members/member" key="@name" cache="30" localCacheSize="2500" frameworkCachePath="{@LocalDataFolder}Cache\CommentsIndexCache" projectCachePath="">
Attributes: {@FrameworkCommentList}
Include Namespaces (false by default) {@CommentFileList}
Namespaces filename ("Namespaces" if not specified or empty) </index>
Directory (current folder if not specified or empty) --> <copy name="comments" source="*" target="/document/comments" />
<output includeNamespaces="false" namespacesFile="Namespaces" folder="{@OutputFolder}" />
</component></ComponentConfig>
<ComponentConfig id="Cached MSDN URL References" enabled="True"><component id="Cached MSDN URL References" type="SandcastleBuilder.Components.CachedResolveReferenceLinksComponent" assembly="{@SHFBFolder}SandcastleBuilder.Components.dll" locale="{@Locale}" linkTarget="{@SdkLinkTarget}">
<helpOutput format="HtmlHelp1">
<cache filename="{@LocalDataFolder}Cache\MsdnUrl.cache" />
<targets base="{@SandcastlePath}Data\Reflection" recurse="true" files="*.xml" type="{@HtmlSdkLinkType}" />
<targets files="reflection.xml" type="Local" />
</helpOutput>
<helpOutput format="MSHelp2">
<cache filename="{@LocalDataFolder}Cache\MsdnUrl.cache" />
<targets base="{@SandcastlePath}Data\Reflection" recurse="true" files="*.xml" type="{@MSHelp2SdkLinkType}" />
<targets files="reflection.xml" type="Index" />
</helpOutput>
<helpOutput format="MSHelpViewer">
<cache filename="{@LocalDataFolder}Cache\MsdnUrl.cache" />
<targets base="{@SandcastlePath}Data\Reflection" recurse="true" files="*.xml" type="{@MSHelpViewerSdkLinkType}" />
<targets files="reflection.xml" type="Id" />
</helpOutput>
<helpOutput format="Website">
<cache filename="{@LocalDataFolder}Cache\MsdnUrl.cache" />
<targets base="{@SandcastlePath}Data\Reflection" recurse="true" files="*.xml" type="{@WebsiteSdkLinkType}" />
<targets files="reflection.xml" type="Local" />
</helpOutput>
</component></ComponentConfig> </component></ComponentConfig>
<ComponentConfig id="Post-transform Component" enabled="True"><component id="Post-transform Component" type="SandcastleBuilder.Components.PostTransformComponent" assembly="{@SHFBFolder}SandcastleBuilder.Components.dll"> <ComponentConfig id="Code Block Component" enabled="True"><component id="Code Block Component">
<!-- Code colorizer files (required). <basePath value="{@HtmlEncProjectFolder}" />
Attributes: <outputPaths>{@HelpFormatOutputPaths}</outputPaths>
Stylesheet file (required) <allowMissingSource value="false" />
Script file (required) <removeRegionMarkers value="false" />
"Copy" image file (required) --> <colorizer syntaxFile="{@SHFBFolder}Colorizer\highlight.xml" styleFile="{@SHFBFolder}Colorizer\highlight.xsl" stylesheet="{@SHFBFolder}Colorizer\highlight.css" scriptFile="{@SHFBFolder}Colorizer\highlight.js" disabled="{@DisableCodeBlockComponent}" language="cs" tabSize="0" numberLines="false" outlining="false" keepSeeTags="false" defaultTitle="true" />
<colorizer stylesheet="{@SHFBFolder}Colorizer\highlight.css" scriptFile="{@SHFBFolder}Colorizer\highlight.js" copyImage="{@SHFBFolder}Colorizer\CopyCode.gif" />
<!-- Base output paths for the files (required). These should match
the parent folder of the output path of the HTML files (see
each of the SaveComponent instances below). -->
<outputPaths>
{@HelpFormatOutputPaths}
</outputPaths>
<!-- Logo image file (optional). Filename is required. The height,
width, altText, placement, and alignment attributes are
optional. -->
<logoFile filename="" height="0" width="0" altText="" placement="left" alignment="left" />
</component></ComponentConfig> </component></ComponentConfig>
<ComponentConfig id="Cached Reflection Index Data" enabled="True"><component id="Cached Reflection Index Data" type="SandcastleBuilder.Components.CachedCopyFromIndexComponent" assembly="{@SHFBFolder}SandcastleBuilder.Components.dll"> <ComponentConfig id="IntelliSense Component" enabled="True"><component id="IntelliSense Component"><!-- Output options (optional)
<index name="reflection" value="/reflection/apis/api" key="@id" cache="10"> Attributes:
<cache base="{@SandcastlePath}Data\Reflection" recurse="true" files="*.xml" cacheFile="{@LocalDataFolder}Cache\Reflection.cache" /> Include namespaces (false by default)
<data files="reflection.xml" /> Namespaces comments filename ("Namespaces" if not specified or empty)
</index> Output folder (current folder if not specified or empty) --><output includeNamespaces="false" namespacesFile="Namespaces" folder="{@OutputFolder}\..\IntelliSense" /></component></ComponentConfig>
<copy name="reflection" source="*" target="/document/reference" /> <ComponentConfig id="Resolve Reference Links (ESENT Cache)" enabled="True"><component id="Resolve Reference Links (ESENT Cache)">
<locale value="{@Locale}" />
<linkTarget value="{@SdkLinkTarget}" />
<helpOutput format="HtmlHelp1">
<msdnContentIdCache path="{@LocalDataFolder}Cache\MsdnContentId.cache" cachePath="{@LocalDataFolder}Cache\ESentMsdnContentIdCache" localCacheSize="2500" />
<targets base="{@SHFBFolder}Data\Reflection" recurse="true" files="*.xml" type="{@HtmlSdkLinkType}" id="FrameworkTargets" cachePath="{@LocalDataFolder}Cache\ESentFrameworkTargetCache" localCacheSize="2500">
{@ReferenceLinkNamespaceFiles}
</targets>
<targets files="reflection.xml" type="Local" id="ProjectTargets" cachePath="" localCacheSize="2500" noReload="true" />
</helpOutput>
<helpOutput format="MSHelp2">
<msdnContentIdCache path="{@LocalDataFolder}Cache\MsdnContentId.cache" cachePath="{@LocalDataFolder}Cache\ESentMsdnContentIdCache" localCacheSize="2500" />
<targets base="{@SHFBFolder}Data\Reflection" recurse="true" files="*.xml" type="{@MSHelp2SdkLinkType}" id="FrameworkTargets" cachePath="{@LocalDataFolder}Cache\ESentFrameworkTargetCache" localCacheSize="2500">
{@ReferenceLinkNamespaceFiles}
</targets>
<targets files="reflection.xml" type="Index" id="ProjectTargets" cachePath="" localCacheSize="2500" noReload="true" />
</helpOutput>
<helpOutput format="MSHelpViewer">
<msdnContentIdCache path="{@LocalDataFolder}Cache\MsdnContentId.cache" cachePath="{@LocalDataFolder}Cache\ESentMsdnContentIdCache" localCacheSize="2500" />
<targets base="{@SHFBFolder}Data\Reflection" recurse="true" files="*.xml" type="{@MSHelpViewerSdkLinkType}" id="FrameworkTargets" cachePath="{@LocalDataFolder}Cache\ESentFrameworkTargetCache" localCacheSize="2500">
{@ReferenceLinkNamespaceFiles}
</targets>
<targets files="reflection.xml" type="Id" id="ProjectTargets" cachePath="" localCacheSize="2500" noReload="true" />
</helpOutput>
<helpOutput format="Website">
<msdnContentIdCache path="{@LocalDataFolder}Cache\MsdnContentId.cache" cachePath="{@LocalDataFolder}Cache\ESentMsdnContentIdCache" localCacheSize="2500" />
<targets base="{@SHFBFolder}Data\Reflection" recurse="true" files="*.xml" type="{@WebsiteSdkLinkType}" id="FrameworkTargets" cachePath="{@LocalDataFolder}Cache\ESentFrameworkTargetCache" localCacheSize="2500">
{@ReferenceLinkNamespaceFiles}
</targets>
<targets files="reflection.xml" type="Local" id="ProjectTargets" cachePath="" localCacheSize="2500" noReload="true" />
</helpOutput>
</component></ComponentConfig> </component></ComponentConfig>
<ComponentConfig id="Cached Framework Comments Index Data" enabled="True"><component id="Cached Framework Comments Index Data" type="SandcastleBuilder.Components.CachedCopyFromIndexComponent" assembly="{@SHFBFolder}SandcastleBuilder.Components.dll"> <ComponentConfig id="API Token Resolution" enabled="True"><component id="API Token Resolution">{@TokenFiles}
<index name="comments" value="/doc/members/member" key="@name" cache="100"> <replace elements="/*//token" item="string(.)" /></component></ComponentConfig></ComponentConfigurations>
{@CachedFrameworkCommentList}
{@CommentFileList}
</index>
<copy name="comments" source="*" target="/document/comments" />
</component></ComponentConfig></ComponentConfigurations>
<DocumentationSources> <DocumentationSources>
<DocumentationSource sourceFile="..\ICSharpCode.AvalonEdit\ICSharpCode.AvalonEdit.csproj" /> <DocumentationSource sourceFile="..\ICSharpCode.AvalonEdit\ICSharpCode.AvalonEdit.csproj" />
</DocumentationSources> </DocumentationSources>
<NamespaceSummaries> <NamespaceSummaries>
<NamespaceSummaryItem name="(global)" isDocumented="False" /> <NamespaceSummaryItem name="(global)" isDocumented="False" />
<NamespaceSummaryItem name="ICSharpCode.AvalonEdit" isDocumented="True">This is the main AvalonEdit namespace.</NamespaceSummaryItem> <NamespaceSummaryItem name="ICSharpCode.AvalonEdit" isDocumented="True">This is the main AvalonEdit namespace.</NamespaceSummaryItem>
<NamespaceSummaryItem name="ICSharpCode.AvalonEdit.CodeCompletion" isDocumented="True">This namespace contains classes to show the code completion window.</NamespaceSummaryItem> <NamespaceSummaryItem name="ICSharpCode.AvalonEdit.CodeCompletion" isDocumented="True">This namespace contains classes to show the code completion window.</NamespaceSummaryItem>
<NamespaceSummaryItem name="ICSharpCode.AvalonEdit.Document" isDocumented="True">This namespace contains the document model. <NamespaceSummaryItem name="ICSharpCode.AvalonEdit.Document" isDocumented="True">This namespace contains the document model.
The most important class here is TextDocument, which represents document that can be displayed and edited in the text editor.</NamespaceSummaryItem> The most important class here is TextDocument, which represents document that can be displayed and edited in the text editor.</NamespaceSummaryItem>
<NamespaceSummaryItem name="ICSharpCode.AvalonEdit.Editing" isDocumented="True">This namespace is the home of the TextArea class. It manages user input and handles the caret and the selection.</NamespaceSummaryItem> <NamespaceSummaryItem name="ICSharpCode.AvalonEdit.Editing" isDocumented="True">This namespace is the home of the TextArea class. It manages user input and handles the caret and the selection.</NamespaceSummaryItem>
<NamespaceSummaryItem name="ICSharpCode.AvalonEdit.Folding" isDocumented="True">This namespace contains the folding (code collapsing) implementation.</NamespaceSummaryItem> <NamespaceSummaryItem name="ICSharpCode.AvalonEdit.Folding" isDocumented="True">This namespace contains the folding (code collapsing) implementation.</NamespaceSummaryItem>
<NamespaceSummaryItem name="ICSharpCode.AvalonEdit.Highlighting" isDocumented="True">This namespace contains the engine for highlighting text documents (DocumentHighlighter). <NamespaceSummaryItem name="ICSharpCode.AvalonEdit.Highlighting" isDocumented="True">This namespace contains the engine for highlighting text documents (DocumentHighlighter).
Additionally, the class HighlightingColorizer provides integration of the highlighting engine into the text editor GUI.</NamespaceSummaryItem> Additionally, the class HighlightingColorizer provides integration of the highlighting engine into the text editor GUI.</NamespaceSummaryItem>
<NamespaceSummaryItem name="ICSharpCode.AvalonEdit.Highlighting.Xshd" isDocumented="True">This namespace contains a document model for syntax highlighting definitions (.xshd files).</NamespaceSummaryItem> <NamespaceSummaryItem name="ICSharpCode.AvalonEdit.Highlighting.Xshd" isDocumented="True">This namespace contains a document model for syntax highlighting definitions (.xshd files).</NamespaceSummaryItem>
<NamespaceSummaryItem name="ICSharpCode.AvalonEdit.Indentation" isDocumented="True">This namespace contains the logic for automatic indentation.</NamespaceSummaryItem> <NamespaceSummaryItem name="ICSharpCode.AvalonEdit.Indentation" isDocumented="True">This namespace contains the logic for automatic indentation.</NamespaceSummaryItem>
<NamespaceSummaryItem name="ICSharpCode.AvalonEdit.Rendering" isDocumented="True">This namespace contains the text rendering infrastructure.</NamespaceSummaryItem> <NamespaceSummaryItem name="ICSharpCode.AvalonEdit.Rendering" isDocumented="True">This namespace contains the text rendering infrastructure.</NamespaceSummaryItem>
<NamespaceSummaryItem name="ICSharpCode.AvalonEdit.Utils" isDocumented="True">This namespace contains various utility classes.</NamespaceSummaryItem> <NamespaceSummaryItem name="ICSharpCode.AvalonEdit.Utils" isDocumented="True">This namespace contains various utility classes.</NamespaceSummaryItem>
<NamespaceSummaryItem name="XamlGeneratedNamespace" isDocumented="False" /> <NamespaceSummaryItem name="XamlGeneratedNamespace" isDocumented="False" />
<NamespaceSummaryItem name="ICSharpCode.AvalonEdit.Xml" isDocumented="True">This namespace contains an error-tolerant XML parser with support for incremental parsing, only reparsing the changed regions of a TextDocument.</NamespaceSummaryItem> <NamespaceSummaryItem name="ICSharpCode.AvalonEdit.Snippets" isDocumented="True">Snippets perform automatic text insertion. Snippets can be interactive after they were expanded, for example to allow the user to easily replace fields in the expanded snippet.</NamespaceSummaryItem>
<NamespaceSummaryItem name="ICSharpCode.AvalonEdit.Snippets" isDocumented="True">Snippets perform automatic text insertion. Snippets can be interactive after they were expanded, for example to allow the user to easily replace fields in the expanded snippet.</NamespaceSummaryItem> <NamespaceSummaryItem name="ICSharpCode.AvalonEdit.Search" isDocumented="True">This namespace contains the SearchPanel UI that allows the user to search the current document.</NamespaceSummaryItem></NamespaceSummaries>
</NamespaceSummaries>
<CleanIntermediates>True</CleanIntermediates> <CleanIntermediates>True</CleanIntermediates>
<SyntaxFilters>Standard</SyntaxFilters> <SyntaxFilters>Standard</SyntaxFilters>
<SdkLinkTarget>Blank</SdkLinkTarget>
<RootNamespaceContainer>False</RootNamespaceContainer>
<Preliminary>False</Preliminary>
<NamingMethod>Guid</NamingMethod>
<Language>en-US</Language>
<ContentPlacement>AboveNamespaces</ContentPlacement>
<BuildAssemblerVerbosity>OnlyWarningsAndErrors</BuildAssemblerVerbosity>
<HelpFileFormat>HtmlHelp1, Website</HelpFileFormat>
<IndentHtml>False</IndentHtml>
<KeepLogFile>True</KeepLogFile>
<DisableCodeBlockComponent>False</DisableCodeBlockComponent>
<CppCommentsFixup>False</CppCommentsFixup>
</PropertyGroup> </PropertyGroup>
<!-- There are no properties for these two groups but they need to appear in <!-- There are no properties for these two groups but they need to appear in
order for Visual Studio to perform the build. --> order for Visual Studio to perform the build. -->

563
AvalonEdit/Documentation/License.html

@ -2,565 +2,34 @@
<html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml">
<head> <head>
<title>License</title> <title>License</title>
<meta name="id" content="e41769b2-38f7-4605-b1d8-9cd22a50a685">
<meta name="revisionNumber" content="1"></meta>
<link rel="stylesheet" type="text/css" href="../styles/presentation.css" /> <link rel="stylesheet" type="text/css" href="../styles/presentation.css" />
</head> </head>
<body> <body>
<h3>GNU LESSER GENERAL PUBLIC LICENSE</h3> <h3>The MIT License (MIT)</h3>
<p>
Version 2.1, February 1999
</p>
<pre>Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
</pre>
<h3>Preamble</h3>
<p>
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
</p>
<p>
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
</p>
<p>
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
</p>
<p>
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
</p>
<p>
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
</p>
<p>
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
</p>
<p>
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
</p>
<p>
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
</p>
<p>
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
</p>
<p>
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
</p>
<p>
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
</p>
<p>
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
</p>
<p>
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
</p>
<p>
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
</p>
<p>
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
</p>
<h3>TERMS AND CONDITIONS FOR COPYING,
DISTRIBUTION AND MODIFICATION</h3>
<p>
<strong>0.</strong>
This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
</p>
<p>
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
</p>
<p>
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
</p>
<p>
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
</p>
<p>
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
</p>
<p>
<strong>1.</strong>
You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
</p>
<p>
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
</p>
<p>
<strong>2.</strong>
You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
</p>
<ul>
<li><strong>a)</strong>
The modified work must itself be a software library.</li>
<li><strong>b)</strong>
You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.</li>
<li><strong>c)</strong>
You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.</li>
<li><strong>d)</strong>
If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
<p>
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)</p></li>
</ul>
<p>
These requirements apply to the modified work as a whole. If identifiable
sections of that work are not derived from the Library, and can be
reasonably considered independent and separate works in themselves, then
this License, and its terms, do not apply to those sections when you
distribute them as separate works. But when you distribute the same
sections as part of a whole which is a work based on the Library, the
distribution of the whole must be on the terms of this License, whose
permissions for other licensees extend to the entire whole, and thus to
each and every part regardless of who wrote it.
</p>
<p>
Thus, it is not the intent of this section to claim rights or contest your
rights to work written entirely by you; rather, the intent is to exercise
the right to control the distribution of derivative or collective works
based on the Library.
</p>
<p> <p>
In addition, mere aggregation of another work not based on the Library with Permission is hereby granted, free of charge, to any person obtaining a copy
the Library (or with a work based on the Library) on a volume of a storage of this software and associated documentation files (the "Software"), to deal
or distribution medium does not bring the other work under the scope of in the Software without restriction, including without limitation the rights
this License. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
</p> </p>
<p> <p>
<strong>3.</strong> The above copyright notice and this permission notice shall be included in
You may opt to apply the terms of the ordinary GNU General Public all copies or substantial portions of the Software.
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
</p> </p>
<p> <p>
Once this change is made in a given copy, it is irreversible for THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
that copy, so the ordinary GNU General Public License applies to all IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
subsequent copies and derivative works made from that copy. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
</p> </p>
<p>
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
</p>
<p>
<strong>4.</strong>
You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
</p>
<p>
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
</p>
<p>
<strong>5.</strong>
A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
</p>
<p>
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
</p>
<p>
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
</p>
<p>
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
</p>
<p>
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
</p>
<p>
<strong>6.</strong>
As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
</p>
<p>
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
</p>
<ul>
<li><strong>a)</strong> Accompany the work with the complete
corresponding machine-readable source code for the Library
including whatever changes were used in the work (which must be
distributed under Sections 1 and 2 above); and, if the work is an
executable linked with the Library, with the complete
machine-readable "work that uses the Library", as object code
and/or source code, so that the user can modify the Library and
then relink to produce a modified executable containing the
modified Library. (It is understood that the user who changes the
contents of definitions files in the Library will not necessarily
be able to recompile the application to use the modified
definitions.)</li>
<li><strong>b)</strong> Use a suitable shared library mechanism
for linking with the Library. A suitable mechanism is one that
(1) uses at run time a copy of the library already present on the
user's computer system, rather than copying library functions into
the executable, and (2) will operate properly with a modified
version of the library, if the user installs one, as long as the
modified version is interface-compatible with the version that the
work was made with.</li>
<li><strong>c)</strong> Accompany the work with a written offer,
valid for at least three years, to give the same user the
materials specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.</li>
<li><strong>d)</strong> If distribution of the work is made by
offering access to copy from a designated place, offer equivalent
access to copy the above specified materials from the same
place.</li>
<li><strong>e)</strong> Verify that the user has already received
a copy of these materials or that you have already sent this user
a copy.</li>
</ul>
<p>
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
</p>
<p>
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
</p>
<p>
<strong>7.</strong> You may place library facilities that are a work
based on the Library side-by-side in a single library together with
other library facilities not covered by this License, and distribute
such a combined library, provided that the separate distribution of
the work based on the Library and of the other library facilities is
otherwise permitted, and provided that you do these two things:
</p>
<ul>
<li><strong>a)</strong> Accompany the combined library with a copy
of the same work based on the Library, uncombined with any other
library facilities. This must be distributed under the terms of
the Sections above.</li>
<li><strong>b)</strong> Give prominent notice with the combined
library of the fact that part of it is a work based on the
Library, and explaining where to find the accompanying uncombined
form of the same work.</li>
</ul>
<p>
<strong>8.</strong> You may not copy, modify, sublicense, link with,
or distribute the Library except as expressly provided under this
License. Any attempt otherwise to copy, modify, sublicense, link
with, or distribute the Library is void, and will automatically
terminate your rights under this License. However, parties who have
received copies, or rights, from you under this License will not have
their licenses terminated so long as such parties remain in full
compliance.
</p>
<p>
<strong>9.</strong>
You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
</p>
<p>
<strong>10.</strong>
Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
</p>
<p>
<strong>11.</strong>
If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
</p>
<p>
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
</p>
<p>
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
</p>
<p>
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
</p>
<p>
<strong>12.</strong>
If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
</p>
<p>
<strong>13.</strong>
The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
</p>
<p>
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
</p>
<p>
<strong>14.</strong>
If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
</p>
<p>
<strong>NO WARRANTY</strong>
</p>
<p>
<strong>15.</strong>
BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
</p>
<p>
<strong>16.</strong>
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
</p>
<h3>END OF TERMS AND CONDITIONS</h3>
</body> </body>
</html> </html>

10
AvalonEdit/Documentation/Welcome.aml

@ -41,13 +41,13 @@
<section> <section>
<title>System requirements</title> <title>System requirements</title>
<content> <content>
<para>AvalonEdit requires the <para>There are two versions of AvalonEdit - the normal one requires
<externalLink> <externalLink>
<linkText>.NET Framework 3.5 SP1</linkText> <linkText>.NET Framework 4.0</linkText>
<linkUri>http://www.microsoft.com/downloads/details.aspx?FamilyID=ab99342f-5d1a-413d-8319-81da479ab0d7&amp;DisplayLang=en</linkUri> <linkUri>http://msdn.microsoft.com/en-us/library/w0x726c2.aspx</linkUri>
<linkTarget>_blank</linkTarget> <linkTarget>_blank</linkTarget>
</externalLink>. </externalLink> or higher; but we also offer a modified version for .NET 3.5 SP1.
For compiling AvalonEdit inside Visual Studio 2008, VS08 SP1 is required. For compiling AvalonEdit, you will need a C# 4.0 compiler (SharpDevelop 4.x or Visual Studio 2010).
</para> </para>
<para>AvalonEdit requires FullTrust and will not run as XBAP.</para> <para>AvalonEdit requires FullTrust and will not run as XBAP.</para>
</content> </content>

57
AvalonEdit/ICSharpCode.AvalonEdit.Tests/Document/ChangeTrackingTest.cs

@ -1,8 +1,26 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Linq; using System.Linq;
#if NREFACTORY
using ICSharpCode.NRefactory.Editor;
#endif
using NUnit.Framework; using NUnit.Framework;
namespace ICSharpCode.AvalonEdit.Document namespace ICSharpCode.AvalonEdit.Document
@ -14,11 +32,10 @@ namespace ICSharpCode.AvalonEdit.Document
public void NoChanges() public void NoChanges()
{ {
TextDocument document = new TextDocument("initial text"); TextDocument document = new TextDocument("initial text");
ChangeTrackingCheckpoint checkpoint1, checkpoint2; ITextSource snapshot1 = document.CreateSnapshot();
ITextSource snapshot1 = document.CreateSnapshot(out checkpoint1); ITextSource snapshot2 = document.CreateSnapshot();
ITextSource snapshot2 = document.CreateSnapshot(out checkpoint2); Assert.AreEqual(0, snapshot1.Version.CompareAge(snapshot2.Version));
Assert.AreEqual(0, checkpoint1.CompareAge(checkpoint2)); Assert.AreEqual(0, snapshot1.Version.GetChangesTo(snapshot2.Version).Count());
Assert.AreEqual(0, checkpoint1.GetChangesTo(checkpoint2).Count());
Assert.AreEqual(document.Text, snapshot1.Text); Assert.AreEqual(document.Text, snapshot1.Text);
Assert.AreEqual(document.Text, snapshot2.Text); Assert.AreEqual(document.Text, snapshot2.Text);
} }
@ -27,16 +44,15 @@ namespace ICSharpCode.AvalonEdit.Document
public void ForwardChanges() public void ForwardChanges()
{ {
TextDocument document = new TextDocument("initial text"); TextDocument document = new TextDocument("initial text");
ChangeTrackingCheckpoint checkpoint1, checkpoint2; ITextSource snapshot1 = document.CreateSnapshot();
ITextSource snapshot1 = document.CreateSnapshot(out checkpoint1);
document.Replace(0, 7, "nw"); document.Replace(0, 7, "nw");
document.Insert(1, "e"); document.Insert(1, "e");
ITextSource snapshot2 = document.CreateSnapshot(out checkpoint2); ITextSource snapshot2 = document.CreateSnapshot();
Assert.AreEqual(-1, checkpoint1.CompareAge(checkpoint2)); Assert.AreEqual(-1, snapshot1.Version.CompareAge(snapshot2.Version));
DocumentChangeEventArgs[] arr = checkpoint1.GetChangesTo(checkpoint2).ToArray(); TextChangeEventArgs[] arr = snapshot1.Version.GetChangesTo(snapshot2.Version).ToArray();
Assert.AreEqual(2, arr.Length); Assert.AreEqual(2, arr.Length);
Assert.AreEqual("nw", arr[0].InsertedText); Assert.AreEqual("nw", arr[0].InsertedText.Text);
Assert.AreEqual("e", arr[1].InsertedText); Assert.AreEqual("e", arr[1].InsertedText.Text);
Assert.AreEqual("initial text", snapshot1.Text); Assert.AreEqual("initial text", snapshot1.Text);
Assert.AreEqual("new text", snapshot2.Text); Assert.AreEqual("new text", snapshot2.Text);
@ -46,16 +62,15 @@ namespace ICSharpCode.AvalonEdit.Document
public void BackwardChanges() public void BackwardChanges()
{ {
TextDocument document = new TextDocument("initial text"); TextDocument document = new TextDocument("initial text");
ChangeTrackingCheckpoint checkpoint1, checkpoint2; ITextSource snapshot1 = document.CreateSnapshot();
ITextSource snapshot1 = document.CreateSnapshot(out checkpoint1);
document.Replace(0, 7, "nw"); document.Replace(0, 7, "nw");
document.Insert(1, "e"); document.Insert(1, "e");
ITextSource snapshot2 = document.CreateSnapshot(out checkpoint2); ITextSource snapshot2 = document.CreateSnapshot();
Assert.AreEqual(1, checkpoint2.CompareAge(checkpoint1)); Assert.AreEqual(1, snapshot2.Version.CompareAge(snapshot1.Version));
DocumentChangeEventArgs[] arr = checkpoint2.GetChangesTo(checkpoint1).ToArray(); TextChangeEventArgs[] arr = snapshot2.Version.GetChangesTo(snapshot1.Version).ToArray();
Assert.AreEqual(2, arr.Length); Assert.AreEqual(2, arr.Length);
Assert.AreEqual("", arr[0].InsertedText); Assert.AreEqual("", arr[0].InsertedText.Text);
Assert.AreEqual("initial", arr[1].InsertedText); Assert.AreEqual("initial", arr[1].InsertedText.Text);
Assert.AreEqual("initial text", snapshot1.Text); Assert.AreEqual("initial text", snapshot1.Text);
Assert.AreEqual("new text", snapshot2.Text); Assert.AreEqual("new text", snapshot2.Text);

19
AvalonEdit/ICSharpCode.AvalonEdit.Tests/Document/CollapsingTests.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.AvalonEdit.Rendering;

19
AvalonEdit/ICSharpCode.AvalonEdit.Tests/Document/HeightTests.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Linq; using System.Linq;

28
AvalonEdit/ICSharpCode.AvalonEdit.Tests/Document/LineManagerTests.cs

@ -1,8 +1,24 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using NUnit.Framework; using NUnit.Framework;
namespace ICSharpCode.AvalonEdit.Document namespace ICSharpCode.AvalonEdit.Document
@ -31,10 +47,16 @@ namespace ICSharpCode.AvalonEdit.Document
{ {
document.Text = "Hello,\nWorld!"; document.Text = "Hello,\nWorld!";
Assert.AreEqual(2, document.LineCount); Assert.AreEqual(2, document.LineCount);
var oldLines = document.Lines.ToArray();
document.Text = ""; document.Text = "";
Assert.AreEqual("", document.Text); Assert.AreEqual("", document.Text);
Assert.AreEqual(0, document.TextLength); Assert.AreEqual(0, document.TextLength);
Assert.AreEqual(1, document.LineCount); Assert.AreEqual(1, document.LineCount);
Assert.AreSame(oldLines[0], document.Lines.Single());
Assert.IsFalse(oldLines[0].IsDeleted);
Assert.IsTrue(oldLines[1].IsDeleted);
Assert.IsNull(oldLines[0].NextLine);
Assert.IsNull(oldLines[1].PreviousLine);
} }
[Test] [Test]
@ -105,7 +127,7 @@ namespace ICSharpCode.AvalonEdit.Document
[Test, ExpectedException(typeof(ArgumentNullException))] [Test, ExpectedException(typeof(ArgumentNullException))]
public void InsertNull() public void InsertNull()
{ {
document.Insert(0, null); document.Insert(0, (string)null);
} }
[Test, ExpectedException(typeof(ArgumentNullException))] [Test, ExpectedException(typeof(ArgumentNullException))]

19
AvalonEdit/ICSharpCode.AvalonEdit.Tests/Document/RandomizedLineManagerTest.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;

24
AvalonEdit/ICSharpCode.AvalonEdit.Tests/Document/TextAnchorTest.cs

@ -1,9 +1,27 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using NUnit.Framework;
using System.Collections.Generic; using System.Collections.Generic;
#if NREFACTORY
using ICSharpCode.NRefactory.Editor;
#endif
using NUnit.Framework;
namespace ICSharpCode.AvalonEdit.Document namespace ICSharpCode.AvalonEdit.Document
{ {

24
AvalonEdit/ICSharpCode.AvalonEdit.Tests/Document/TextSegmentTreeTest.cs

@ -1,9 +1,27 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using NUnit.Framework;
using System.Collections.Generic; using System.Collections.Generic;
#if NREFACTORY
using ICSharpCode.NRefactory.Editor;
#endif
using NUnit.Framework;
namespace ICSharpCode.AvalonEdit.Document namespace ICSharpCode.AvalonEdit.Document
{ {

22
AvalonEdit/ICSharpCode.AvalonEdit.Tests/Document/TextUtilitiesTests.cs

@ -1,7 +1,25 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
#if NREFACTORY
using ICSharpCode.NRefactory.Editor;
#endif
using NUnit.Framework; using NUnit.Framework;
namespace ICSharpCode.AvalonEdit.Document namespace ICSharpCode.AvalonEdit.Document

92
AvalonEdit/ICSharpCode.AvalonEdit.Tests/Document/UndoStackTests.cs

@ -0,0 +1,92 @@
// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using NUnit.Framework;
namespace ICSharpCode.AvalonEdit.Document
{
public class UndoStackTests
{
[Test]
public void ContinueUndoGroup()
{
var doc = new TextDocument();
doc.Insert(0, "a");
doc.UndoStack.StartContinuedUndoGroup();
doc.Insert(1, "b");
doc.UndoStack.EndUndoGroup();
doc.UndoStack.Undo();
Assert.AreEqual("", doc.Text);
}
[Test]
public void ContinueEmptyUndoGroup()
{
var doc = new TextDocument();
doc.Insert(0, "a");
doc.UndoStack.StartUndoGroup();
doc.UndoStack.EndUndoGroup();
doc.UndoStack.StartContinuedUndoGroup();
doc.Insert(1, "b");
doc.UndoStack.EndUndoGroup();
doc.UndoStack.Undo();
Assert.AreEqual("a", doc.Text);
}
[Test]
public void ContinueEmptyUndoGroup_WithOptionalEntries()
{
var doc = new TextDocument();
doc.Insert(0, "a");
doc.UndoStack.StartUndoGroup();
doc.UndoStack.PushOptional(new StubUndoableAction());
doc.UndoStack.EndUndoGroup();
doc.UndoStack.StartContinuedUndoGroup();
doc.Insert(1, "b");
doc.UndoStack.EndUndoGroup();
doc.UndoStack.Undo();
Assert.AreEqual("a", doc.Text);
}
[Test]
public void EmptyContinuationGroup()
{
var doc = new TextDocument();
doc.Insert(0, "a");
doc.UndoStack.StartContinuedUndoGroup();
doc.UndoStack.EndUndoGroup();
doc.UndoStack.StartContinuedUndoGroup();
doc.Insert(1, "b");
doc.UndoStack.EndUndoGroup();
doc.UndoStack.Undo();
Assert.AreEqual("", doc.Text);
}
class StubUndoableAction : IUndoableOperation
{
public void Undo()
{
}
public void Redo()
{
}
}
}
}

22
AvalonEdit/ICSharpCode.AvalonEdit.Tests/Editing/ChangeDocumentTests.cs

@ -1,9 +1,27 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Text; using System.Text;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
#if NREFACTORY
using ICSharpCode.NRefactory;
#endif
using NUnit.Framework; using NUnit.Framework;
namespace ICSharpCode.AvalonEdit.Editing namespace ICSharpCode.AvalonEdit.Editing

56
AvalonEdit/ICSharpCode.AvalonEdit.Tests/Editing/TextSegmentReadOnlySectionTests.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
using System; using System;
@ -37,6 +52,15 @@ namespace ICSharpCode.AvalonEdit.Editing
Assert.AreEqual(20, result[0].Length); Assert.AreEqual(20, result[0].Length);
} }
[Test]
public void EmptyDeletionPossibleWhenNothingIsReadOnly()
{
var result = provider.GetDeletableSegments(new SimpleSegment(10, 0)).ToList();
Assert.AreEqual(1, result.Count);
Assert.AreEqual(10, result[0].Offset);
Assert.AreEqual(0, result[0].Length);
}
[Test] [Test]
public void InsertionPossibleBeforeReadOnlySegment() public void InsertionPossibleBeforeReadOnlySegment()
{ {
@ -84,6 +108,34 @@ namespace ICSharpCode.AvalonEdit.Editing
Assert.AreEqual(0, result.Count); Assert.AreEqual(0, result.Count);
} }
[Test]
public void EmptyDeletionImpossibleInReadOnlySegment()
{
segments.Add(new TextSegment { StartOffset = 10, Length = 5 });
var result = provider.GetDeletableSegments(new SimpleSegment(11, 0)).ToList();
Assert.AreEqual(0, result.Count);
}
[Test]
public void EmptyDeletionPossibleAtStartOfReadOnlySegment()
{
segments.Add(new TextSegment { StartOffset = 10, Length = 5 });
var result = provider.GetDeletableSegments(new SimpleSegment(10, 0)).ToList();
Assert.AreEqual(1, result.Count);
Assert.AreEqual(10, result[0].Offset);
Assert.AreEqual(0, result[0].Length);
}
[Test]
public void EmptyDeletionPossibleAtEndOfReadOnlySegment()
{
segments.Add(new TextSegment { StartOffset = 10, Length = 5 });
var result = provider.GetDeletableSegments(new SimpleSegment(15, 0)).ToList();
Assert.AreEqual(1, result.Count);
Assert.AreEqual(15, result[0].Offset);
Assert.AreEqual(0, result[0].Length);
}
[Test] [Test]
public void DeletionAroundReadOnlySegment() public void DeletionAroundReadOnlySegment()
{ {

182
AvalonEdit/ICSharpCode.AvalonEdit.Tests/Highlighting/HighlightedLineMergeTests.cs

@ -0,0 +1,182 @@
// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.NRefactory.Editor;
using NUnit.Framework;
namespace ICSharpCode.AvalonEdit.Highlighting
{
[TestFixture]
public class HighlightedLineMergeTests
{
IDocument document = new TextDocument(new string(' ', 20));
[Test]
public void SimpleMerge1()
{
HighlightedLine baseLine = new HighlightedLine(document, document.GetLineByNumber(1));
baseLine.Sections.Add(MakeSection(0, 1, "B"));
HighlightedLine additionalLine = new HighlightedLine(document, document.GetLineByNumber(1));
additionalLine.Sections.Add(MakeSection(0, 2, "A"));
baseLine.MergeWith(additionalLine);
// The additional section gets split up so that it fits into the tree structure
Assert.That(baseLine.Sections, Is.EqualTo(
new[] {
MakeSection(0, 1, "B"),
MakeSection(0, 1, "A"),
MakeSection(1, 2, "A")
}).Using(new SectionComparer()));
}
[Test]
public void SimpleMerge2()
{
HighlightedLine baseLine = new HighlightedLine(document, document.GetLineByNumber(1));
baseLine.Sections.Add(MakeSection(0, 1, "B"));
baseLine.Sections.Add(MakeSection(0, 1, "BN"));
HighlightedLine additionalLine = new HighlightedLine(document, document.GetLineByNumber(1));
additionalLine.Sections.Add(MakeSection(0, 2, "A"));
baseLine.MergeWith(additionalLine);
// The additional section gets split up so that it fits into the tree structure
Assert.That(baseLine.Sections, Is.EqualTo(
new[] {
MakeSection(0, 1, "B"),
MakeSection(0, 1, "BN"),
MakeSection(0, 1, "A"),
MakeSection(1, 2, "A")
}).Using(new SectionComparer()));
}
HighlightedSection MakeSection(int start, int end, string name)
{
return new HighlightedSection { Offset = start, Length = end - start, Color = new HighlightingColor { Name = name }};
}
class SectionComparer : IEqualityComparer<HighlightedSection>
{
public bool Equals(HighlightedSection a, HighlightedSection b)
{
return a.Offset == b.Offset && a.Length == b.Length && a.Color.Name == b.Color.Name;
}
public int GetHashCode(HighlightedSection obj)
{
return obj.Offset;
}
}
#region Automatic Test
/*
const int combinations = 6 * 3 * 4 * 3 * 3 * 4;
HighlightingColor[] baseLineColors = {
new HighlightingColor { Name = "Base-A" },
new HighlightingColor { Name = "Base-B" },
new HighlightingColor { Name = "Base-N" },
new HighlightingColor { Name = "Base-C" }
};
HighlightingColor[] additionalLineColors = {
new HighlightingColor { Name = "Add-A" },
new HighlightingColor { Name = "Add-B" },
new HighlightingColor { Name = "Add-N" },
new HighlightingColor { Name = "Add-C" }
};
HighlightedLine BuildHighlightedLine(int num, HighlightingColor[] colors)
{
// We are build a HighlightedLine with 4 segments:
// A B C (top-level) and N nested within B.
// These are the integers controlling the generating process:
int aStart = GetNum(ref num, 5); // start offset of A
int aLength = GetNum(ref num, 2); // length of A
int bDistance = GetNum(ref num, 3); // distance from start of B to end of A
int bStart = aStart + aLength + bDistance;
int nDistance = GetNum(ref num, 2); // distance from start of B to start of N, range 0-2
int nLength = GetNum(ref num, 2); // length of N
int bEndDistance = GetNum(ref num, 2); // distance from end of N to end of B
int bLength = nDistance + nLength + bEndDistance;
int cDistance = GetNum(ref num, 3); // distance from end of B to start of C
int cStart = bStart + bLength + cDistance;
int cLength = 1;
Assert.AreEqual(0, num);
var documentLine = document.GetLineByNumber(1);
HighlightedLine line = new HighlightedLine(document, documentLine);
line.Sections.Add(new HighlightedSection { Offset = aStart, Length = aLength, Color = colors[0] });
line.Sections.Add(new HighlightedSection { Offset = bStart, Length = bLength, Color = colors[1] });
line.Sections.Add(new HighlightedSection { Offset = bStart + nDistance, Length = nLength, Color = colors[2] });
line.Sections.Add(new HighlightedSection { Offset = cStart, Length = cLength, Color = colors[3] });
return line;
}
/// <summary>
/// Gets a number between 0 and max (inclusive)
/// </summary>
int GetNum(ref int num, int max)
{
int result = num % (max+1);
num = num / (max + 1);
return result;
}
[Test]
public void TestAll()
{
for (int c1 = 0; c1 < combinations; c1++) {
HighlightedLine line1 = BuildHighlightedLine(c1, additionalLineColors);
for (int c2 = 0; c2 < combinations; c2++) {
HighlightedLine line2 = BuildHighlightedLine(c2, baseLineColors);
HighlightingColor[] expectedPerCharColors = new HighlightingColor[document.TextLength];
ApplyColors(expectedPerCharColors, line2);
ApplyColors(expectedPerCharColors, line1);
try {
line2.MergeWith(line1);
} catch (InvalidOperationException ex) {
throw new InvalidOperationException(string.Format("Error for c1 = {0}, c2 = {1}", c1, c2), ex);
}
HighlightingColor[] actualPerCharColors = new HighlightingColor[document.TextLength];
ApplyColors(actualPerCharColors, line2);
Assert.AreEqual(expectedPerCharColors, actualPerCharColors, string.Format("c1 = {0}, c2 = {1}", c1, c2));
}
}
}
void ApplyColors(HighlightingColor[] perCharColors, HighlightedLine line)
{
foreach (var section in line.Sections) {
for (int i = 0; i < section.Length; i++) {
perCharColors[section.Offset + i] = section.Color;
}
}
}
*/
#endregion
}
}

27
AvalonEdit/ICSharpCode.AvalonEdit.Tests/Highlighting/HtmlClipboardTests.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Windows; using System.Windows;
@ -17,7 +32,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting
public HtmlClipboardTests() public HtmlClipboardTests()
{ {
document = new TextDocument("using System.Text;\n\tstring text = SomeMethod();"); document = new TextDocument("using System.Text;\n\tstring text = SomeMethod();");
highlighter = new DocumentHighlighter(document, HighlightingManager.Instance.GetDefinition("C#").MainRuleSet); highlighter = new DocumentHighlighter(document, HighlightingManager.Instance.GetDefinition("C#"));
} }
[Test] [Test]
@ -25,9 +40,9 @@ namespace ICSharpCode.AvalonEdit.Highlighting
{ {
var segment = new TextSegment { StartOffset = 0, Length = document.TextLength }; var segment = new TextSegment { StartOffset = 0, Length = document.TextLength };
string html = HtmlClipboard.CreateHtmlFragment(document, highlighter, segment, new HtmlOptions()); string html = HtmlClipboard.CreateHtmlFragment(document, highlighter, segment, new HtmlOptions());
Assert.AreEqual("<span style=\"color: #008000; font-weight: bold; \">using</span>&nbsp;System.Text;<br>" + Environment.NewLine + Assert.AreEqual("<span style=\"color: #008000; font-weight: bold; \">using</span> System.Text;<br>" + Environment.NewLine +
"&nbsp;&nbsp;&nbsp;&nbsp;<span style=\"color: #ff0000; \">string</span>&nbsp;" + "&nbsp;&nbsp;&nbsp;&nbsp;<span style=\"color: #ff0000; \">string</span> " +
"text =&nbsp;<span style=\"color: #191970; font-weight: bold; \">SomeMethod</span>();", html); "text = <span style=\"color: #191970; font-weight: bold; \">SomeMethod</span>();", html);
} }
[Test] [Test]

65
AvalonEdit/ICSharpCode.AvalonEdit.Tests/ICSharpCode.AvalonEdit.Tests.csproj

@ -3,13 +3,12 @@
<PropertyGroup> <PropertyGroup>
<ProjectGuid>{6222A3A1-83CE-47A3-A4E4-A018F82D44D8}</ProjectGuid> <ProjectGuid>{6222A3A1-83CE-47A3-A4E4-A018F82D44D8}</ProjectGuid>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <Platform Condition=" '$(Platform)' == '' ">Net40</Platform>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<RootNamespace>ICSharpCode.AvalonEdit</RootNamespace> <RootNamespace>ICSharpCode.AvalonEdit</RootNamespace>
<AssemblyName>ICSharpCode.AvalonEdit.Tests</AssemblyName> <AssemblyName>ICSharpCode.AvalonEdit.Tests</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion> <TargetFrameworkVersion Condition=" '$(Configuration)' == '' ">v4.0</TargetFrameworkVersion>
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<SourceAnalysisOverrideSettingsFile>"C:\Program Files\SharpDevelop\3.0\bin\..\AddIns\AddIns\Misc\SourceAnalysis\Settings.SourceAnalysis"</SourceAnalysisOverrideSettingsFile>
<SignAssembly>True</SignAssembly> <SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>..\ICSharpCode.AvalonEdit\ICSharpCode.AvalonEdit.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>..\ICSharpCode.AvalonEdit\ICSharpCode.AvalonEdit.snk</AssemblyOriginatorKeyFile>
<DelaySign>False</DelaySign> <DelaySign>False</DelaySign>
@ -17,39 +16,55 @@
<AllowUnsafeBlocks>False</AllowUnsafeBlocks> <AllowUnsafeBlocks>False</AllowUnsafeBlocks>
<NoStdLib>False</NoStdLib> <NoStdLib>False</NoStdLib>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors> <TreatWarningsAsErrors>False</TreatWarningsAsErrors>
<OutputPath>..\..\..\..\bin\UnitTests\</OutputPath> <OutputPath>bin\$(Configuration)\</OutputPath>
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent> <RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<NoWin32Manifest>False</NoWin32Manifest>
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
<RegisterForComInterop>False</RegisterForComInterop>
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
<BaseAddress>4194304</BaseAddress>
<PlatformTarget>AnyCPU</PlatformTarget>
<FileAlignment>4096</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Platform)' == 'Net35' ">
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Platform)' == 'Net40' ">
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<DefineConstants>DOTNET4</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Platform)' == 'WithNRefactory' ">
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<DefineConstants>NREFACTORY</DefineConstants>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<DebugType>Full</DebugType> <DebugType>Full</DebugType>
<Optimize>False</Optimize> <Optimize>False</Optimize>
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow> <CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
<DefineConstants>DEBUG;TRACE</DefineConstants> <BaseIntermediateOutputPath>obj\</BaseIntermediateOutputPath>
<DefineDebug>True</DefineDebug>
<DefineTrace>True</DefineTrace>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DebugSymbols>false</DebugSymbols> <DebugSymbols>false</DebugSymbols>
<DebugType>None</DebugType> <DebugType>None</DebugType>
<Optimize>True</Optimize> <Optimize>True</Optimize>
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow> <CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
<DefineConstants>TRACE</DefineConstants> <BaseIntermediateOutputPath>obj\</BaseIntermediateOutputPath>
</PropertyGroup> <DefineTrace>True</DefineTrace>
<PropertyGroup Condition=" '$(Platform)' == 'AnyCPU' ">
<RegisterForComInterop>False</RegisterForComInterop>
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
<BaseAddress>4194304</BaseAddress>
<PlatformTarget>x86</PlatformTarget>
<FileAlignment>4096</FileAlignment>
</PropertyGroup> </PropertyGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
<ItemGroup> <ItemGroup>
<Reference Include="ICSharpCode.SharpZipLib"> <ProjectReference Include="..\..\NRefactory\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj" Condition="$(DefineConstants.Contains('NREFACTORY'))">
<HintPath>..\..\..\AddIns\Misc\AddInManager\RequiredLibraries\ICSharpCode.SharpZipLib.dll</HintPath> <Project>{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}</Project>
</Reference> <Name>ICSharpCode.NRefactory</Name>
</ProjectReference>
<Reference Include="nunit.framework"> <Reference Include="nunit.framework">
<HintPath>..\..\..\Tools\NUnit\nunit.framework.dll</HintPath> <HintPath>..\packages\NUnit.2.6.3\lib\nunit.framework.dll</HintPath>
<Private>True</Private>
</Reference> </Reference>
<Reference Include="PresentationCore"> <Reference Include="PresentationCore">
<RequiredTargetFramework>3.0</RequiredTargetFramework> <RequiredTargetFramework>3.0</RequiredTargetFramework>
@ -77,9 +92,12 @@
<Compile Include="Document\TextAnchorTest.cs" /> <Compile Include="Document\TextAnchorTest.cs" />
<Compile Include="Document\TextSegmentTreeTest.cs" /> <Compile Include="Document\TextSegmentTreeTest.cs" />
<Compile Include="Document\TextUtilitiesTests.cs" /> <Compile Include="Document\TextUtilitiesTests.cs" />
<Compile Include="Document\UndoStackTests.cs" />
<Compile Include="Editing\ChangeDocumentTests.cs" /> <Compile Include="Editing\ChangeDocumentTests.cs" />
<Compile Include="Editing\TextSegmentReadOnlySectionTests.cs" /> <Compile Include="Editing\TextSegmentReadOnlySectionTests.cs" />
<Compile Include="Highlighting\HighlightedLineMergeTests.cs" />
<Compile Include="Highlighting\HtmlClipboardTests.cs" /> <Compile Include="Highlighting\HtmlClipboardTests.cs" />
<Compile Include="MultipleUIThreads.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Document\CollapsingTests.cs" /> <Compile Include="Document\CollapsingTests.cs" />
<Compile Include="Document\HeightTests.cs" /> <Compile Include="Document\HeightTests.cs" />
@ -92,12 +110,6 @@
<Compile Include="Utils\IndentationStringTests.cs" /> <Compile Include="Utils\IndentationStringTests.cs" />
<Compile Include="Utils\RopeTests.cs" /> <Compile Include="Utils\RopeTests.cs" />
<Compile Include="WeakReferenceTests.cs" /> <Compile Include="WeakReferenceTests.cs" />
<Compile Include="XmlParser\ParserTests.cs" />
<Compile Include="XmlParser\TextReplacementTests.cs" />
<None Include="app.config" />
<None Include="XmlParser\W3C.zip">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\ICSharpCode.AvalonEdit\ICSharpCode.AvalonEdit.csproj"> <ProjectReference Include="..\ICSharpCode.AvalonEdit\ICSharpCode.AvalonEdit.csproj">
@ -108,4 +120,7 @@
<ItemGroup> <ItemGroup>
<Folder Include="Search" /> <Folder Include="Search" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
</Project> </Project>

44
AvalonEdit/ICSharpCode.AvalonEdit.Tests/MultipleUIThreads.cs

@ -0,0 +1,44 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Threading;
using System.Windows;
using NUnit.Framework;
namespace ICSharpCode.AvalonEdit
{
[TestFixture]
public class MultipleUIThreads
{
Exception error;
[Test]
public void CreateEditorInstancesOnMultipleUIThreads()
{
Thread t1 = new Thread(new ThreadStart(Run));
Thread t2 = new Thread(new ThreadStart(Run));
t1.SetApartmentState(ApartmentState.STA);
t2.SetApartmentState(ApartmentState.STA);
t1.Start();
t2.Start();
t1.Join();
t2.Join();
if (error != null)
throw new InvalidOperationException(error.Message, error);
}
[STAThread]
void Run()
{
try {
var window = new Window();
window.Content = new TextEditor();
window.ShowActivated = false;
window.Show();
} catch (Exception ex) {
error = ex;
}
}
}
}

26
AvalonEdit/ICSharpCode.AvalonEdit.Tests/Properties/AssemblyInfo.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
#region Using directives #region Using directives
@ -7,6 +22,8 @@ using System;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using NUnit.Framework;
#endregion #endregion
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
@ -31,3 +48,8 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can use the default the Revision and // You can specify all the values or you can use the default the Revision and
// Build Numbers by using the '*' as shown below: // Build Numbers by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.*")]
// Run unit tests on STA thread.
[assembly: RequiresSTA]
namespace ICSharpCode.NRefactory.Editor {}

22
AvalonEdit/ICSharpCode.AvalonEdit.Tests/Search/FindTests.cs

@ -1,9 +1,27 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Linq; using System.Linq;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
#if NREFACTORY
using ICSharpCode.NRefactory.Editor;
#endif
using NUnit.Framework; using NUnit.Framework;
namespace ICSharpCode.AvalonEdit.Search namespace ICSharpCode.AvalonEdit.Search

76
AvalonEdit/ICSharpCode.AvalonEdit.Tests/Utils/CaretNavigationTests.cs

@ -1,9 +1,27 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Windows.Documents; using System.Windows.Documents;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
#if NREFACTORY
using ICSharpCode.NRefactory.Editor;
#endif
using NUnit.Framework; using NUnit.Framework;
namespace ICSharpCode.AvalonEdit.Utils namespace ICSharpCode.AvalonEdit.Utils
@ -82,5 +100,59 @@ namespace ICSharpCode.AvalonEdit.Utils
Assert.AreEqual(0, GetPrevCaretStop("txt ", 5, CaretPositioningMode.WordStart)); Assert.AreEqual(0, GetPrevCaretStop("txt ", 5, CaretPositioningMode.WordStart));
Assert.AreEqual(3, GetPrevCaretStop("txt ", 5, CaretPositioningMode.WordBorder)); Assert.AreEqual(3, GetPrevCaretStop("txt ", 5, CaretPositioningMode.WordBorder));
} }
[Test]
public void SingleCharacterOutsideBMP()
{
string c = "\U0001D49E";
Assert.AreEqual(2, GetNextCaretStop(c, 0, CaretPositioningMode.Normal));
Assert.AreEqual(0, GetPrevCaretStop(c, 2, CaretPositioningMode.Normal));
}
[Test]
public void DetectWordBordersOutsideBMP()
{
string c = " a\U0001D49Eb ";
Assert.AreEqual(1, GetNextCaretStop(c, 0, CaretPositioningMode.WordBorder));
Assert.AreEqual(5, GetNextCaretStop(c, 1, CaretPositioningMode.WordBorder));
Assert.AreEqual(5, GetPrevCaretStop(c, 6, CaretPositioningMode.WordBorder));
Assert.AreEqual(1, GetPrevCaretStop(c, 5, CaretPositioningMode.WordBorder));
}
[Test]
public void DetectWordBordersOutsideBMP2()
{
string c = " \U0001D49E\U0001D4AA ";
Assert.AreEqual(1, GetNextCaretStop(c, 0, CaretPositioningMode.WordBorder));
Assert.AreEqual(5, GetNextCaretStop(c, 1, CaretPositioningMode.WordBorder));
Assert.AreEqual(5, GetPrevCaretStop(c, 6, CaretPositioningMode.WordBorder));
Assert.AreEqual(1, GetPrevCaretStop(c, 5, CaretPositioningMode.WordBorder));
}
[Test]
public void CombiningMark()
{
string str = " x͆ ";
Assert.AreEqual(3, GetNextCaretStop(str, 1, CaretPositioningMode.Normal));
Assert.AreEqual(1, GetPrevCaretStop(str, 3, CaretPositioningMode.Normal));
}
[Test]
public void StackedCombiningMark()
{
string str = " x͆͆͆͆ ";
Assert.AreEqual(6, GetNextCaretStop(str, 1, CaretPositioningMode.Normal));
Assert.AreEqual(1, GetPrevCaretStop(str, 6, CaretPositioningMode.Normal));
}
[Test]
public void SingleClosingBraceAtLineEnd()
{
string str = "\t\t}";
Assert.AreEqual(2, GetNextCaretStop(str, 1, CaretPositioningMode.WordStart));
Assert.AreEqual(-1, GetPrevCaretStop(str, 1, CaretPositioningMode.WordStart));
}
} }
} }

55
AvalonEdit/ICSharpCode.AvalonEdit.Tests/Utils/CompressingTreeListTests.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Linq; using System.Linq;
@ -30,12 +45,7 @@ namespace ICSharpCode.AvalonEdit.Utils
list.InsertRange(0, billion, "A"); list.InsertRange(0, billion, "A");
list.InsertRange(1, billion, "B"); list.InsertRange(1, billion, "B");
Assert.AreEqual(2 * billion, list.Count); Assert.AreEqual(2 * billion, list.Count);
try { Assert.Throws<OverflowException>(delegate { list.InsertRange(2, billion, "C"); });
list.InsertRange(2, billion, "C");
Assert.Fail("Expected OverflowException");
} catch (OverflowException) {
// expected
}
} }
[Test] [Test]
@ -103,5 +113,34 @@ namespace ICSharpCode.AvalonEdit.Utils
list.RemoveRange(0, 3); list.RemoveRange(0, 3);
Assert.AreEqual(new[] { 2, 3, 3 }, list.ToArray()); Assert.AreEqual(new[] { 2, 3, 3 }, list.ToArray());
} }
[Test]
public void Transform()
{
CompressingTreeList<int> list = new CompressingTreeList<int>((a, b) => a == b);
list.AddRange(new[] { 0, 1, 1, 0 });
int calls = 0;
list.Transform(i => { calls++; return i + 1; });
Assert.AreEqual(3, calls);
Assert.AreEqual(new[] { 1, 2, 2, 1 }, list.ToArray());
}
[Test]
public void TransformToZero()
{
CompressingTreeList<int> list = new CompressingTreeList<int>((a, b) => a == b);
list.AddRange(new[] { 0, 1, 1, 0 });
list.Transform(i => 0);
Assert.AreEqual(new[] { 0, 0, 0, 0 }, list.ToArray());
}
[Test]
public void TransformRange()
{
CompressingTreeList<int> list = new CompressingTreeList<int>((a, b) => a == b);
list.AddRange(new[] { 0, 1, 1, 1, 0, 0 });
list.TransformRange(2, 3, i => 0);
Assert.AreEqual(new[] { 0, 1, 0, 0, 0, 0 }, list.ToArray());
}
} }
} }

19
AvalonEdit/ICSharpCode.AvalonEdit.Tests/Utils/ExtensionMethodsTests.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using NUnit.Framework; using NUnit.Framework;

19
AvalonEdit/ICSharpCode.AvalonEdit.Tests/Utils/IndentationStringTests.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using NUnit.Framework; using NUnit.Framework;

19
AvalonEdit/ICSharpCode.AvalonEdit.Tests/Utils/RopeTests.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.IO; using System.IO;

19
AvalonEdit/ICSharpCode.AvalonEdit.Tests/WeakReferenceTests.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Windows.Threading; using System.Windows.Threading;

223
AvalonEdit/ICSharpCode.AvalonEdit.Tests/XmlParser/ParserTests.cs

@ -1,223 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using System.IO;
using System.Text;
using ICSharpCode.AvalonEdit.Xml;
using ICSharpCode.SharpZipLib.Zip;
using NUnit.Framework;
namespace ICSharpCode.AvalonEdit.Xml
{
class TestFile
{
public string Name { get; set; }
public string Content { get; set; }
public string Canonical { get; set; }
public string Description { get; set; }
}
[TestFixture]
public class ParserTests
{
readonly string zipFileName = @"XmlParser\W3C.zip";
List<TestFile> xmlFiles = new List<TestFile>();
[TestFixtureSetUp]
public void OpenZipFile()
{
ZipFile zipFile = new ZipFile(zipFileName);
Dictionary<string, TestFile> xmlFiles = new Dictionary<string, TestFile>();
// Decompress XML files
foreach(ZipEntry zipEntry in zipFile.Cast<ZipEntry>().Where(zip => zip.IsFile && zip.Name.EndsWith(".xml"))) {
Stream stream = zipFile.GetInputStream(zipEntry);
string content = new StreamReader(stream).ReadToEnd();
xmlFiles.Add(zipEntry.Name, new TestFile { Name = zipEntry.Name, Content = content });
}
// Add descriptions
foreach(TestFile metaData in xmlFiles.Values.Where(f => f.Name.StartsWith("ibm/ibm_oasis"))) {
var doc = System.Xml.Linq.XDocument.Parse(metaData.Content);
foreach(var testElem in doc.Descendants("TEST")) {
string uri = "ibm/" + testElem.Attribute("URI").Value;
string description = testElem.Value.Replace("\n ", "\n").TrimStart('\n');
if (xmlFiles.ContainsKey(uri))
xmlFiles[uri].Description = description;
}
}
// Copy canonical forms
foreach(TestFile canonical in xmlFiles.Values.Where(f => f.Name.Contains("/out/"))) {
string uri = canonical.Name.Replace("/out/", "/");
if (xmlFiles.ContainsKey(uri))
xmlFiles[uri].Canonical = canonical.Content;
}
// Copy resuts to field
this.xmlFiles.AddRange(xmlFiles.Values.Where(f => !f.Name.Contains("/out/")));
}
IEnumerable<TestFile> GetXmlFilesStartingWith(string directory)
{
return xmlFiles.Where(f => f.Name.StartsWith(directory));
}
[Test]
public void W3C_Valid()
{
string[] exclude = {
// NAME in DTD infoset
"ibm02v01", "ibm03v01", "ibm85v01", "ibm86v01", "ibm87v01", "ibm88v01", "ibm89v01",
};
TestFiles(GetXmlFilesStartingWith("ibm/valid/"), true, exclude);
}
[Test]
public void W3C_Invalid()
{
string[] exclude = {
// Default attribute value
"ibm56i03",
};
TestFiles(GetXmlFilesStartingWith("ibm/invalid/"), true, exclude);
}
[Test]
public void W3C_NotWellformed()
{
string[] exclude = {
// XML declaration well formed
"ibm23n", "ibm24n", "ibm26n01", "ibm32n", "ibm80n06", "ibm81n01", "ibm81n02", "ibm81n03", "ibm81n04", "ibm81n05", "ibm81n06", "ibm81n07", "ibm81n08", "ibm81n09",
// Invalid chars in a comment - do we care?
"ibm02n",
// Invalid char ref - do we care?
"ibm66n12", "ibm66n13", "ibm66n14", "ibm66n15",
// DTD in wrong location
"ibm27n01", "ibm43n",
// Entity refs depending on DTD
"ibm41n10", "ibm41n11", "ibm41n12", "ibm41n13", "ibm41n14", "ibm68n04", "ibm68n06", "ibm68n07", "ibm68n08", "ibm68n09", "ibm68n10",
// DTD Related tests
"ibm09n01", "ibm09n02", "ibm13n01", "ibm13n02", "ibm13n03", "ibm28n01", "ibm28n02", "ibm28n03", "ibm29n01", "ibm29n03", "ibm29n04", "ibm29n07", "ibm30n01", "ibm31n01", "ibm45n01", "ibm45n02", "ibm45n03", "ibm45n04", "ibm45n05", "ibm45n06", "ibm46n01", "ibm46n02", "ibm46n03", "ibm46n04",
"ibm46n05", "ibm47n01", "ibm47n02", "ibm47n03", "ibm47n04", "ibm47n05", "ibm47n06", "ibm48n01", "ibm48n02", "ibm48n03", "ibm48n04", "ibm48n05", "ibm48n06", "ibm48n07", "ibm49n01", "ibm49n02", "ibm49n03", "ibm49n04", "ibm49n05", "ibm49n06", "ibm50n01", "ibm50n02", "ibm50n03", "ibm50n04",
"ibm50n05", "ibm50n06", "ibm50n07", "ibm51n01", "ibm51n02", "ibm51n03", "ibm51n04", "ibm51n05", "ibm51n06", "ibm51n07", "ibm52n01", "ibm52n02", "ibm52n03", "ibm53n01", "ibm53n02", "ibm53n03", "ibm53n04", "ibm53n05", "ibm53n06", "ibm53n07", "ibm53n08", "ibm54n01", "ibm54n02", "ibm55n01",
"ibm55n02", "ibm55n03", "ibm56n01", "ibm56n02", "ibm56n03", "ibm56n04", "ibm56n05", "ibm56n06", "ibm56n07", "ibm57n01", "ibm58n01", "ibm58n02", "ibm58n03", "ibm58n04", "ibm58n05", "ibm58n06", "ibm58n07", "ibm58n08", "ibm59n01", "ibm59n02", "ibm59n03", "ibm59n04", "ibm59n05", "ibm59n06",
"ibm60n01", "ibm60n02", "ibm60n03", "ibm60n04", "ibm60n05", "ibm60n06", "ibm60n07", "ibm60n08", "ibm61n01", "ibm62n01", "ibm62n02", "ibm62n03", "ibm62n04", "ibm62n05", "ibm62n06", "ibm62n07", "ibm62n08", "ibm63n01", "ibm63n02", "ibm63n03", "ibm63n04", "ibm63n05", "ibm63n06", "ibm63n07",
"ibm64n01", "ibm64n02", "ibm64n03", "ibm65n01", "ibm65n02", "ibm66n01", "ibm66n03", "ibm66n05", "ibm66n07", "ibm66n09", "ibm66n11", "ibm69n01", "ibm69n02", "ibm69n03", "ibm69n04", "ibm69n05", "ibm69n06", "ibm69n07", "ibm70n01", "ibm71n01", "ibm71n02", "ibm71n03", "ibm71n04", "ibm71n05",
"ibm72n01", "ibm72n02", "ibm72n03", "ibm72n04", "ibm72n05", "ibm72n06", "ibm72n09", "ibm73n01", "ibm73n03", "ibm74n01", "ibm75n01", "ibm75n02", "ibm75n03", "ibm75n04", "ibm75n05", "ibm75n06", "ibm75n07", "ibm75n08", "ibm75n09", "ibm75n10", "ibm75n11", "ibm75n12", "ibm75n13", "ibm76n01",
"ibm76n02", "ibm76n03", "ibm76n04", "ibm76n05", "ibm76n06", "ibm76n07", "ibm77n01", "ibm77n02", "ibm77n03", "ibm77n04", "ibm78n01", "ibm78n02", "ibm79n01", "ibm79n02", "ibm82n01", "ibm82n02", "ibm82n03", "ibm82n04", "ibm82n08", "ibm83n01", "ibm83n03", "ibm83n04", "ibm83n05", "ibm83n06",
// No idea what this is
"misc/432gewf", "ibm28an01",
};
TestFiles(GetXmlFilesStartingWith("ibm/not-wf/"), false, exclude);
}
StringBuilder errorOutput;
void TestFiles(IEnumerable<TestFile> files, bool areWellFormed, string[] exclude)
{
errorOutput = new StringBuilder();
int testsRun = 0;
int ignored = 0;
foreach (TestFile file in files) {
if (exclude.Any(exc => file.Name.Contains(exc))) {
ignored++;
} else {
testsRun++;
TestFile(file, areWellFormed);
}
}
if (testsRun == 0) {
Assert.Fail("Test files not found");
}
if (errorOutput.Length > 0) {
// Can not output ]]> otherwise nuint will crash
Assert.Fail(errorOutput.Replace("]]>", "]]~NUNIT~>").ToString());
}
}
/// <remarks>
/// If using DTD, canonical representation is not checked
/// If using DTD, uknown entiry references are not error
/// </remarks>
bool TestFile(TestFile testFile, bool isWellFormed)
{
bool passed = true;
string content = testFile.Content;
Debug.WriteLine("Testing " + testFile.Name + "...");
AXmlParser parser = new AXmlParser();
bool usingDTD = content.Contains("<!DOCTYPE") && (content.Contains("<!ENTITY") || content.Contains(" SYSTEM "));
if (usingDTD)
parser.UnknownEntityReferenceIsError = false;
AXmlDocument document;
parser.Lock.EnterWriteLock();
try {
document = parser.Parse(content, null);
} finally {
parser.Lock.ExitWriteLock();
}
string printed = PrettyPrintAXmlVisitor.PrettyPrint(document);
if (content != printed) {
errorOutput.AppendFormat("Output of pretty printed XML for \"{0}\" does not match the original.\n", testFile.Name);
errorOutput.AppendFormat("Pretty printed:\n{0}\n", Indent(printed));
passed = false;
}
if (isWellFormed && !usingDTD) {
string canonicalPrint = CanonicalPrintAXmlVisitor.Print(document);
if (testFile.Canonical != null) {
if (testFile.Canonical != canonicalPrint) {
errorOutput.AppendFormat("Canonical XML for \"{0}\" does not match the excpected.\n", testFile.Name);
errorOutput.AppendFormat("Expected:\n{0}\n", Indent(testFile.Canonical));
errorOutput.AppendFormat("Seen:\n{0}\n", Indent(canonicalPrint));
passed = false;
}
} else {
errorOutput.AppendFormat("Can not find canonical output for \"{0}\"", testFile.Name);
errorOutput.AppendFormat("Suggested canonical output:\n{0}\n", Indent(canonicalPrint));
passed = false;
}
}
bool hasErrors = document.SyntaxErrors.FirstOrDefault() != null;
if (isWellFormed && hasErrors) {
errorOutput.AppendFormat("Syntax error(s) in well formed file \"{0}\":\n", testFile.Name);
foreach (var error in document.SyntaxErrors) {
string followingText = content.Substring(error.StartOffset, Math.Min(10, content.Length - error.StartOffset));
errorOutput.AppendFormat("Error ({0}-{1}): {2} (followed by \"{3}\")\n", error.StartOffset, error.EndOffset, error.Message, followingText);
}
passed = false;
}
if (!isWellFormed && !hasErrors) {
errorOutput.AppendFormat("No syntax errors reported for mallformed file \"{0}\"\n", testFile.Name);
passed = false;
}
// Epilog
if (!passed) {
if (testFile.Description != null) {
errorOutput.AppendFormat("Test description:\n{0}\n", Indent(testFile.Description));
}
errorOutput.AppendFormat("File content:\n{0}\n", Indent(content));
errorOutput.AppendLine();
}
return passed;
}
string Indent(string text)
{
return " " + text.TrimEnd().Replace("\n", "\n ");
}
}
}

115
AvalonEdit/ICSharpCode.AvalonEdit.Tests/XmlParser/TextReplacementTests.cs

@ -1,115 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Xml;
using NUnit.Framework;
namespace ICSharpCode.AvalonEdit.Xml
{
[TestFixture]
public class TextReplacementTests
{
#region Test Data
string initialDocumentText = @"<UserControl x:Class='ICSharpCode.Profiler.Controls.TimeLineCell'
xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Grid>
</Grid>
</UserControl>";
string finalDocumentText = @"<UserControl x:Class='ICSharpCode.Profiler.Controls.TimeLineCell'
xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height='20' />
<RowDefinition Height='20' />
<RowDefinition Height='Auto' />
</Grid.RowDefinitions>
<StackPanel Orientation='Horizontal'>
<TextBlock Text='Test' />
</StackPanel>
<local:TimeLineControl x:Name='t1' Grid.Row='1' />
<TextBlock Grid.Row='2' Text='Test' />
</Grid>
</UserControl>";
int offset = @"<UserControl x:Class='ICSharpCode.Profiler.Controls.TimeLineCell'
xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
".Length;
string original = @" <Grid>
</Grid>";
string replacement = @" <Grid>
<Grid.RowDefinitions>
<RowDefinition Height='20' />
<RowDefinition Height='20' />
<RowDefinition Height='Auto' />
</Grid.RowDefinitions>
<StackPanel Orientation='Horizontal'>
<TextBlock Text='Test' />
</StackPanel>
<local:TimeLineControl x:Name='t1' Grid.Row='1' />
<TextBlock Grid.Row='2' Text='Test' />
</Grid>";
#endregion
[Test]
public void ReplacementTest1()
{
/*
* REPRODUCTION STEPS
*
* 1. Run XmlDOM project
* 2. paste text from initialDocumentText (see Test Data region)
* 3. select lines 4 to 6
* 4. replace with replacement (see Test Data region)
* 5. exception thrown:
* ICSharpCode.AvalonEdit.Xml.InternalException : Assertion failed: cached elements must not have zero length
* at ICSharpCode.AvalonEdit.Xml.AXmlParser.Assert(Boolean condition, String message)
* in c:\Projects\SharpDevelop\4.0\SharpDevelop\src\Libraries\AvalonEdit\ICSharpCode.AvalonEdit\Xml\AXmlParser.cs:line 121
* at ICSharpCode.AvalonEdit.Xml.TagReader.TryReadFromCacheOrNew[T](T& res, Predicate`1 condition)
* in c:\Projects\SharpDevelop\4.0\SharpDevelop\src\Libraries\AvalonEdit\ICSharpCode.AvalonEdit\Xml\TagReader.cs:line 39
* at ICSharpCode.AvalonEdit.Xml.TagReader.<ReadText>d__12.MoveNext()
* in c:\Projects\SharpDevelop\4.0\SharpDevelop\src\Libraries\AvalonEdit\ICSharpCode.AvalonEdit\Xml\TagReader.cs:line 456
* at System.Collections.Generic.List`1.InsertRange(Int32 index, IEnumerable`1 collection)
* at System.Collections.Generic.List`1.AddRange(IEnumerable`1 collection)
* at ICSharpCode.AvalonEdit.Xml.TagReader.ReadAllTags()
* in c:\Projects\SharpDevelop\4.0\SharpDevelop\src\Libraries\AvalonEdit\ICSharpCode.AvalonEdit\Xml\TagReader.cs:line 73
* at ICSharpCode.AvalonEdit.Xml.AXmlParser.Parse(String input, IEnumerable`1 changesSinceLastParse)
* in c:\Projects\SharpDevelop\4.0\SharpDevelop\src\Libraries\AvalonEdit\ICSharpCode.AvalonEdit\Xml\AXmlParser.cs:line 161
* at ICSharpCode.AvalonEdit.Tests.XmlParser.TextReplacementTests.RunTest()
* in c:\Projects\SharpDevelop\4.0\SharpDevelop\src\Libraries\AvalonEdit\ICSharpCode.AvalonEdit.Tests\XmlParser\TextReplacementTests.cs:line 114
* at ICSharpCode.AvalonEdit.Tests.XmlParser.TextReplacementTests.TestMethod(
* ) in c:\Projects\SharpDevelop\4.0\SharpDevelop\src\Libraries\AvalonEdit\ICSharpCode.AvalonEdit.Tests\XmlParser\TextReplacementTests.cs:line 97
* */
Assert.DoesNotThrow(RunTest1);
}
void RunTest1()
{
AXmlParser parser = new AXmlParser();
try {
parser.Lock.EnterWriteLock();
parser.Parse(initialDocumentText, null); // full reparse
IList<DocumentChangeEventArgs> changes = new List<DocumentChangeEventArgs>();
changes.Add(new DocumentChangeEventArgs(offset, original, replacement));
parser.Parse(finalDocumentText, changes);
} finally {
parser.Lock.ExitWriteLock();
}
}
}
}

BIN
AvalonEdit/ICSharpCode.AvalonEdit.Tests/XmlParser/W3C.zip

Binary file not shown.

15
AvalonEdit/ICSharpCode.AvalonEdit.Tests/app.config

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="NUnit">
<section name="TestRunner"
type="System.Configuration.NameValueSectionHandler" />
</sectionGroup>
</configSections>
<NUnit>
<TestRunner>
<!-- Valid values are STA,MTA. Others ignored. -->
<add key="ApartmentState" value="STA" />
</TestRunner>
</NUnit>
</configuration>

4
AvalonEdit/ICSharpCode.AvalonEdit.Tests/packages.config

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NUnit" version="2.6.3" targetFramework="net45" />
</packages>

19
AvalonEdit/ICSharpCode.AvalonEdit/AvalonEditCommands.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Windows.Input; using System.Windows.Input;

55
AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -11,6 +26,7 @@ using System.Windows.Controls.Primitives;
using System.Windows.Documents; using System.Windows.Documents;
using System.Windows.Input; using System.Windows.Input;
using System.Linq; using System.Linq;
using ICSharpCode.AvalonEdit.Utils;
namespace ICSharpCode.AvalonEdit.CodeCompletion namespace ICSharpCode.AvalonEdit.CodeCompletion
{ {
@ -165,14 +181,21 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
{ {
base.OnMouseDoubleClick(e); base.OnMouseDoubleClick(e);
if (e.ChangedButton == MouseButton.Left) { if (e.ChangedButton == MouseButton.Left) {
e.Handled = true; // only process double clicks on the ListBoxItems, not on the scroll bar
RequestInsertion(e); if (ExtensionMethods.VisualAncestorsAndSelf(e.OriginalSource as DependencyObject).TakeWhile(obj => obj != this).Any(obj => obj is ListBoxItem)) {
e.Handled = true;
RequestInsertion(e);
}
} }
} }
/// <summary> /// <summary>
/// Gets/Sets the selected item. /// Gets/Sets the selected item.
/// </summary> /// </summary>
/// <remarks>
/// The setter of this property does not scroll to the selected item.
/// You might want to also call <see cref="ScrollIntoView"/>.
/// </remarks>
public ICompletionData SelectedItem { public ICompletionData SelectedItem {
get { get {
return (listBox != null ? listBox.SelectedItem : null) as ICompletionData; return (listBox != null ? listBox.SelectedItem : null) as ICompletionData;
@ -180,10 +203,22 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
set { set {
if (listBox == null && value != null) if (listBox == null && value != null)
ApplyTemplate(); ApplyTemplate();
listBox.SelectedItem = value; if (listBox != null) // may still be null if ApplyTemplate fails, or if listBox and value both are null
listBox.SelectedItem = value;
} }
} }
/// <summary>
/// Scrolls the specified item into view.
/// </summary>
public void ScrollIntoView(ICompletionData item)
{
if (listBox == null)
ApplyTemplate();
if (listBox != null)
listBox.ScrollIntoView(item);
}
/// <summary> /// <summary>
/// Occurs when the SelectedItem property changes. /// Occurs when the SelectedItem property changes.
/// </summary> /// </summary>
@ -324,7 +359,7 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
// 5 = match start // 5 = match start
// 4 = match CamelCase when length of query is 1 or 2 characters // 4 = match CamelCase when length of query is 1 or 2 characters
// 3 = match substring case sensitive // 3 = match substring case sensitive
// 2 = match sustring // 2 = match substring
// 1 = match CamelCase // 1 = match CamelCase
// -1 = no match // -1 = no match
if (query == itemText) if (query == itemText)
@ -361,11 +396,15 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
static bool CamelCaseMatch(string text, string query) static bool CamelCaseMatch(string text, string query)
{ {
// We take the first letter of the text regardless of whether or not it's upper case so we match
// against camelCase text as well as PascalCase text ("cct" matches "camelCaseText")
var theFirstLetterOfEachWord = text.Take(1).Concat(text.Skip(1).Where(char.IsUpper));
int i = 0; int i = 0;
foreach (char upper in text.Where(c => char.IsUpper(c))) { foreach (var letter in theFirstLetterOfEachWord) {
if (i > query.Length - 1) if (i > query.Length - 1)
return true; // return true here for CamelCase partial match ("CQ" matches "CodeQualityAnalysis") return true; // return true here for CamelCase partial match ("CQ" matches "CodeQualityAnalysis")
if (char.ToUpper(query[i], CultureInfo.InvariantCulture) != upper) if (char.ToUpperInvariant(query[i]) != char.ToUpperInvariant(letter))
return false; return false;
i++; i++;
} }

2
AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.xaml

@ -44,7 +44,7 @@
<DataTemplate> <DataTemplate>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Image Source="{Binding Image}" Width="16" Height="16" Margin="0,0,2,0"/> <Image Source="{Binding Image}" Width="16" Height="16" Margin="0,0,2,0"/>
<ContentControl Content="{Binding Content}"/> <ContentPresenter Content="{Binding Content}"/>
</StackPanel> </StackPanel>
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>

19
AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionListBox.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Windows; using System.Windows;

22
AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindow.cs

@ -1,14 +1,26 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Diagnostics;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Controls.Primitives; using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Threading;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Editing; using ICSharpCode.AvalonEdit.Editing;

23
AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindowBase.cs

@ -1,17 +1,34 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Linq;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls.Primitives; using System.Windows.Controls.Primitives;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Threading; using System.Windows.Threading;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Editing; using ICSharpCode.AvalonEdit.Editing;
using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.AvalonEdit.Utils;
using ICSharpCode.NRefactory.Editor;
namespace ICSharpCode.AvalonEdit.CodeCompletion namespace ICSharpCode.AvalonEdit.CodeCompletion
{ {

25
AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/ICompletionData.cs

@ -1,10 +1,29 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Windows.Media; using System.Windows.Media;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Editing; using ICSharpCode.AvalonEdit.Editing;
#if NREFACTORY
using ICSharpCode.NRefactory.Editor;
#else
using ICSharpCode.AvalonEdit.Document;
#endif
namespace ICSharpCode.AvalonEdit.CodeCompletion namespace ICSharpCode.AvalonEdit.CodeCompletion
{ {

19
AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/IOverloadProvider.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;

23
AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/InsightWindow.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Windows; using System.Windows;
@ -34,14 +49,14 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnSourceInitialized(EventArgs e) protected override void OnSourceInitialized(EventArgs e)
{ {
base.OnSourceInitialized(e);
Rect caret = this.TextArea.Caret.CalculateCaretRectangle(); Rect caret = this.TextArea.Caret.CalculateCaretRectangle();
Point pointOnScreen = this.TextArea.TextView.PointToScreen(caret.Location - this.TextArea.TextView.ScrollOffset); Point pointOnScreen = this.TextArea.TextView.PointToScreen(caret.Location - this.TextArea.TextView.ScrollOffset);
Rect workingArea = System.Windows.Forms.Screen.FromPoint(pointOnScreen.ToSystemDrawing()).WorkingArea.ToWpf().TransformFromDevice(this); Rect workingArea = System.Windows.Forms.Screen.FromPoint(pointOnScreen.ToSystemDrawing()).WorkingArea.ToWpf().TransformFromDevice(this);
MaxHeight = workingArea.Height; MaxHeight = workingArea.Height;
MaxWidth = Math.Min(workingArea.Width, Math.Max(1000, workingArea.Width * 0.6)); MaxWidth = Math.Min(workingArea.Width, Math.Max(1000, workingArea.Width * 0.6));
base.OnSourceInitialized(e);
} }
/// <summary> /// <summary>

4
AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/InsightWindow.xaml

@ -65,8 +65,8 @@
<ControlTemplate TargetType="{x:Type Button}"> <ControlTemplate TargetType="{x:Type Button}">
<Border Name="bd" <Border Name="bd"
Background="{TemplateBinding Background}" CornerRadius="2"> Background="{TemplateBinding Background}" CornerRadius="2">
<ContentControl Margin="{TemplateBinding Padding}" <ContentPresenter Margin="{TemplateBinding Padding}"
Content="{TemplateBinding Content}"/> Content="{TemplateBinding Content}"/>
</Border> </Border>
<ControlTemplate.Triggers> <ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true"> <Trigger Property="IsMouseOver" Value="true">

21
AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/OverloadInsightWindow.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Windows; using System.Windows;
@ -37,7 +52,7 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
protected override void OnKeyDown(KeyEventArgs e) protected override void OnKeyDown(KeyEventArgs e)
{ {
base.OnKeyDown(e); base.OnKeyDown(e);
if (!e.Handled && this.Provider.Count > 1) { if (!e.Handled && this.Provider != null && this.Provider.Count > 1) {
switch (e.Key) { switch (e.Key) {
case Key.Up: case Key.Up:
e.Handled = true; e.Handled = true;

19
AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/OverloadViewer.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;

140
AvalonEdit/ICSharpCode.AvalonEdit/Document/ChangeTrackingCheckpoint.cs

@ -1,140 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using ICSharpCode.AvalonEdit.Utils;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace ICSharpCode.AvalonEdit.Document
{
/// <summary>
/// <para>A checkpoint that allows tracking changes to a TextDocument.</para>
/// <para>
/// Use <see cref="TextDocument.CreateSnapshot(out ChangeTrackingCheckpoint)"/> to create a checkpoint.
/// </para>
/// </summary>
/// <remarks>
/// <para>The <see cref="ChangeTrackingCheckpoint"/> class allows tracking document changes, even from background threads.</para>
/// <para>Once you have two checkpoints, you can call <see cref="ChangeTrackingCheckpoint.GetChangesTo"/> to retrieve the complete list
/// of document changes that happened between those versions of the document.</para>
/// </remarks>
public sealed class ChangeTrackingCheckpoint
{
// Object that is unique per document.
// Used to determine if two checkpoints belong to the same document.
// We don't use a reference to the document itself to allow the GC to reclaim the document memory
// even if there are still references to checkpoints.
readonly object documentIdentifier;
// 'value' is the change from the previous checkpoint to this checkpoint
// TODO: store the change in the older checkpoint instead - if only a reference to the
// newest document version exists, the GC should be able to collect all DocumentChangeEventArgs.
readonly DocumentChangeEventArgs value;
readonly int id;
ChangeTrackingCheckpoint next;
internal ChangeTrackingCheckpoint(object documentIdentifier)
{
this.documentIdentifier = documentIdentifier;
}
internal ChangeTrackingCheckpoint(object documentIdentifier, DocumentChangeEventArgs value, int id)
{
this.documentIdentifier = documentIdentifier;
this.value = value;
this.id = id;
}
internal ChangeTrackingCheckpoint Append(DocumentChangeEventArgs change)
{
Debug.Assert(this.next == null);
this.next = new ChangeTrackingCheckpoint(this.documentIdentifier, change, unchecked( this.id + 1 ));
return this.next;
}
/// <summary>
/// Creates a change tracking checkpoint for the specified document.
/// This method is thread-safe.
/// If you need a ChangeTrackingCheckpoint that's consistent with a snapshot of the document,
/// use <see cref="TextDocument.CreateSnapshot(out ChangeTrackingCheckpoint)"/>.
/// </summary>
public static ChangeTrackingCheckpoint Create(TextDocument document)
{
if (document == null)
throw new ArgumentNullException("document");
return document.CreateChangeTrackingCheckpoint();
}
/// <summary>
/// Gets whether this checkpoint belongs to the same document as the other checkpoint.
/// </summary>
public bool BelongsToSameDocumentAs(ChangeTrackingCheckpoint other)
{
if (other == null)
throw new ArgumentNullException("other");
return documentIdentifier == other.documentIdentifier;
}
/// <summary>
/// Compares the age of this checkpoint to the other checkpoint.
/// </summary>
/// <remarks>This method is thread-safe.</remarks>
/// <exception cref="ArgumentException">Raised if 'other' belongs to a different document than this checkpoint.</exception>
/// <returns>-1 if this checkpoint is older than <paramref name="other"/>.
/// 0 if <c>this</c>==<paramref name="other"/>.
/// 1 if this checkpoint is newer than <paramref name="other"/>.</returns>
public int CompareAge(ChangeTrackingCheckpoint other)
{
if (other == null)
throw new ArgumentNullException("other");
if (other.documentIdentifier != this.documentIdentifier)
throw new ArgumentException("Checkpoints do not belong to the same document.");
// We will allow overflows, but assume that the maximum distance between checkpoints is 2^31-1.
// This is guaranteed on x86 because so many checkpoints don't fit into memory.
return Math.Sign(unchecked( this.id - other.id ));
}
/// <summary>
/// Gets the changes from this checkpoint to the other checkpoint.
/// If 'other' is older than this checkpoint, reverse changes are calculated.
/// </summary>
/// <remarks>This method is thread-safe.</remarks>
/// <exception cref="ArgumentException">Raised if 'other' belongs to a different document than this checkpoint.</exception>
public IEnumerable<DocumentChangeEventArgs> GetChangesTo(ChangeTrackingCheckpoint other)
{
int result = CompareAge(other);
if (result < 0)
return GetForwardChanges(other);
else if (result > 0)
return other.GetForwardChanges(this).Reverse().Select(change => change.Invert());
else
return Empty<DocumentChangeEventArgs>.Array;
}
IEnumerable<DocumentChangeEventArgs> GetForwardChanges(ChangeTrackingCheckpoint other)
{
// Return changes from this(exclusive) to other(inclusive).
ChangeTrackingCheckpoint node = this;
do {
node = node.next;
yield return node.value;
} while (node != other);
}
/// <summary>
/// Calculates where the offset has moved in the other buffer version.
/// </summary>
/// <remarks>This method is thread-safe.</remarks>
/// <exception cref="ArgumentException">Raised if 'other' belongs to a different document than this checkpoint.</exception>
public int MoveOffsetTo(ChangeTrackingCheckpoint other, int oldOffset, AnchorMovementType movement)
{
int offset = oldOffset;
foreach (DocumentChangeEventArgs e in GetChangesTo(other)) {
offset = e.GetNewOffset(offset, movement);
}
return offset;
}
}
}

86
AvalonEdit/ICSharpCode.AvalonEdit/Document/DocumentChangeEventArgs.cs

@ -1,8 +1,24 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using ICSharpCode.AvalonEdit.Utils;
using System; using System;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.NRefactory.Editor;
namespace ICSharpCode.AvalonEdit.Document namespace ICSharpCode.AvalonEdit.Document
{ {
@ -11,37 +27,8 @@ namespace ICSharpCode.AvalonEdit.Document
/// This class is thread-safe. /// This class is thread-safe.
/// </summary> /// </summary>
[Serializable] [Serializable]
public class DocumentChangeEventArgs : EventArgs public class DocumentChangeEventArgs : TextChangeEventArgs
{ {
/// <summary>
/// The offset at which the change occurs.
/// </summary>
public int Offset { get; private set; }
/// <summary>
/// The text that was removed.
/// </summary>
public string RemovedText { get; private set; }
/// <summary>
/// The number of characters removed.
/// </summary>
public int RemovalLength {
get { return RemovedText.Length; }
}
/// <summary>
/// The text that was inserted.
/// </summary>
public string InsertedText { get; private set; }
/// <summary>
/// The number of characters inserted.
/// </summary>
public int InsertionLength {
get { return InsertedText.Length; }
}
volatile OffsetChangeMap offsetChangeMap; volatile OffsetChangeMap offsetChangeMap;
/// <summary> /// <summary>
@ -77,7 +64,7 @@ namespace ICSharpCode.AvalonEdit.Document
/// <summary> /// <summary>
/// Gets the new offset where the specified offset moves after this document change. /// Gets the new offset where the specified offset moves after this document change.
/// </summary> /// </summary>
public int GetNewOffset(int offset, AnchorMovementType movementType) public override int GetNewOffset(int offset, AnchorMovementType movementType = AnchorMovementType.Default)
{ {
if (offsetChangeMap != null) if (offsetChangeMap != null)
return offsetChangeMap.GetNewOffset(offset, movementType); return offsetChangeMap.GetNewOffset(offset, movementType);
@ -97,28 +84,33 @@ namespace ICSharpCode.AvalonEdit.Document
/// Creates a new DocumentChangeEventArgs object. /// Creates a new DocumentChangeEventArgs object.
/// </summary> /// </summary>
public DocumentChangeEventArgs(int offset, string removedText, string insertedText, OffsetChangeMap offsetChangeMap) public DocumentChangeEventArgs(int offset, string removedText, string insertedText, OffsetChangeMap offsetChangeMap)
: base(offset, removedText, insertedText)
{
SetOffsetChangeMap(offsetChangeMap);
}
/// <summary>
/// Creates a new DocumentChangeEventArgs object.
/// </summary>
public DocumentChangeEventArgs(int offset, ITextSource removedText, ITextSource insertedText, OffsetChangeMap offsetChangeMap)
: base(offset, removedText, insertedText)
{
SetOffsetChangeMap(offsetChangeMap);
}
void SetOffsetChangeMap(OffsetChangeMap offsetChangeMap)
{ {
ThrowUtil.CheckNotNegative(offset, "offset");
ThrowUtil.CheckNotNull(removedText, "removedText");
ThrowUtil.CheckNotNull(insertedText, "insertedText");
this.Offset = offset;
this.RemovedText = removedText;
this.InsertedText = insertedText;
if (offsetChangeMap != null) { if (offsetChangeMap != null) {
if (!offsetChangeMap.IsFrozen) if (!offsetChangeMap.IsFrozen)
throw new ArgumentException("The OffsetChangeMap must be frozen before it can be used in DocumentChangeEventArgs"); throw new ArgumentException("The OffsetChangeMap must be frozen before it can be used in DocumentChangeEventArgs");
if (!offsetChangeMap.IsValidForDocumentChange(offset, removedText.Length, insertedText.Length)) if (!offsetChangeMap.IsValidForDocumentChange(this.Offset, this.RemovalLength, this.InsertionLength))
throw new ArgumentException("OffsetChangeMap is not valid for this document change", "offsetChangeMap"); throw new ArgumentException("OffsetChangeMap is not valid for this document change", "offsetChangeMap");
this.offsetChangeMap = offsetChangeMap; this.offsetChangeMap = offsetChangeMap;
} }
} }
/// <summary> /// <inheritdoc/>
/// Creates DocumentChangeEventArgs for the reverse change. public override TextChangeEventArgs Invert()
/// </summary>
public DocumentChangeEventArgs Invert()
{ {
OffsetChangeMap map = this.OffsetChangeMapOrNull; OffsetChangeMap map = this.OffsetChangeMapOrNull;
if (map != null) { if (map != null) {

19
AvalonEdit/ICSharpCode.AvalonEdit/Document/DocumentChangeOperation.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Diagnostics; using System.Diagnostics;

32
AvalonEdit/ICSharpCode.AvalonEdit/Document/DocumentLine.cs

@ -1,9 +1,27 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
#if NREFACTORY
using ICSharpCode.NRefactory.Editor;
#endif
namespace ICSharpCode.AvalonEdit.Document namespace ICSharpCode.AvalonEdit.Document
{ {
@ -22,7 +40,7 @@ namespace ICSharpCode.AvalonEdit.Document
/// and the data structure also updates all offsets in O(lg N) whenever a line is inserted or removed. /// and the data structure also updates all offsets in O(lg N) whenever a line is inserted or removed.
/// </para> /// </para>
/// </remarks> /// </remarks>
public sealed partial class DocumentLine : ISegment public sealed partial class DocumentLine : IDocumentLine
{ {
#region Constructor #region Constructor
#if DEBUG #if DEBUG
@ -221,6 +239,14 @@ namespace ICSharpCode.AvalonEdit.Document
} }
} }
} }
IDocumentLine IDocumentLine.NextLine {
get { return this.NextLine; }
}
IDocumentLine IDocumentLine.PreviousLine {
get { return this.PreviousLine; }
}
#endregion #endregion
#region ToString #region ToString

19
AvalonEdit/ICSharpCode.AvalonEdit/Document/DocumentLineTree.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;

86
AvalonEdit/ICSharpCode.AvalonEdit/Document/DocumentTextWriter.cs

@ -0,0 +1,86 @@
// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.IO;
using System.Text;
#if NREFACTORY
using ICSharpCode.NRefactory.Editor;
#endif
namespace ICSharpCode.AvalonEdit.Document
{
/// <summary>
/// A TextWriter implementation that directly inserts into a document.
/// </summary>
public class DocumentTextWriter : TextWriter
{
readonly IDocument document;
int insertionOffset;
/// <summary>
/// Creates a new DocumentTextWriter that inserts into document, starting at insertionOffset.
/// </summary>
public DocumentTextWriter(IDocument document, int insertionOffset)
{
this.insertionOffset = insertionOffset;
if (document == null)
throw new ArgumentNullException("document");
this.document = document;
var line = document.GetLineByOffset(insertionOffset);
if (line.DelimiterLength == 0)
line = line.PreviousLine;
if (line != null)
this.NewLine = document.GetText(line.EndOffset, line.DelimiterLength);
}
/// <summary>
/// Gets/Sets the current insertion offset.
/// </summary>
public int InsertionOffset {
get { return insertionOffset; }
set { insertionOffset = value; }
}
/// <inheritdoc/>
public override void Write(char value)
{
document.Insert(insertionOffset, value.ToString());
insertionOffset++;
}
/// <inheritdoc/>
public override void Write(char[] buffer, int index, int count)
{
document.Insert(insertionOffset, new string(buffer, index, count));
insertionOffset += count;
}
/// <inheritdoc/>
public override void Write(string value)
{
document.Insert(insertionOffset, value);
insertionOffset += value.Length;
}
/// <inheritdoc/>
public override Encoding Encoding {
get { return Encoding.UTF8; }
}
}
}

19
AvalonEdit/ICSharpCode.AvalonEdit/Document/GapTextBuffer.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Diagnostics; using System.Diagnostics;

343
AvalonEdit/ICSharpCode.AvalonEdit/Document/IDocument.cs

@ -0,0 +1,343 @@
// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
namespace ICSharpCode.AvalonEdit.Document
{
#if !NREFACTORY
/// <summary>
/// A document representing a source code file for refactoring.
/// Line and column counting starts at 1.
/// Offset counting starts at 0.
/// </summary>
public interface IDocument : ITextSource, IServiceProvider
{
#if NREFACTORY
/// <summary>
/// Creates an immutable snapshot of this document.
/// </summary>
IDocument CreateDocumentSnapshot();
#endif
/// <summary>
/// Gets/Sets the text of the whole document..
/// </summary>
new string Text { get; set; } // hides ITextSource.Text to add the setter
/// <summary>
/// This event is called directly before a change is applied to the document.
/// </summary>
/// <remarks>
/// It is invalid to modify the document within this event handler.
/// Aborting the change (by throwing an exception) is likely to cause corruption of data structures
/// that listen to the Changing and Changed events.
/// </remarks>
event EventHandler<TextChangeEventArgs> TextChanging;
/// <summary>
/// This event is called directly after a change is applied to the document.
/// </summary>
/// <remarks>
/// It is invalid to modify the document within this event handler.
/// Aborting the event handler (by throwing an exception) is likely to cause corruption of data structures
/// that listen to the Changing and Changed events.
/// </remarks>
event EventHandler<TextChangeEventArgs> TextChanged;
/// <summary>
/// This event is called after a group of changes is completed.
/// </summary>
/// <seealso cref="EndUndoableAction"/>
event EventHandler ChangeCompleted;
/// <summary>
/// Gets the number of lines in the document.
/// </summary>
int LineCount { get; }
/// <summary>
/// Gets the document line with the specified number.
/// </summary>
/// <param name="lineNumber">The number of the line to retrieve. The first line has number 1.</param>
IDocumentLine GetLineByNumber(int lineNumber);
/// <summary>
/// Gets the document line that contains the specified offset.
/// </summary>
IDocumentLine GetLineByOffset(int offset);
/// <summary>
/// Gets the offset from a text location.
/// </summary>
/// <seealso cref="GetLocation"/>
int GetOffset(int line, int column);
/// <summary>
/// Gets the offset from a text location.
/// </summary>
/// <seealso cref="GetLocation"/>
int GetOffset(TextLocation location);
/// <summary>
/// Gets the location from an offset.
/// </summary>
/// <seealso cref="GetOffset(TextLocation)"/>
TextLocation GetLocation(int offset);
/// <summary>
/// Inserts text.
/// </summary>
/// <param name="offset">The offset at which the text is inserted.</param>
/// <param name="text">The new text.</param>
/// <remarks>
/// Anchors positioned exactly at the insertion offset will move according to their movement type.
/// For AnchorMovementType.Default, they will move behind the inserted text.
/// The caret will also move behind the inserted text.
/// </remarks>
void Insert(int offset, string text);
/// <summary>
/// Inserts text.
/// </summary>
/// <param name="offset">The offset at which the text is inserted.</param>
/// <param name="text">The new text.</param>
/// <remarks>
/// Anchors positioned exactly at the insertion offset will move according to their movement type.
/// For AnchorMovementType.Default, they will move behind the inserted text.
/// The caret will also move behind the inserted text.
/// </remarks>
void Insert(int offset, ITextSource text);
/// <summary>
/// Inserts text.
/// </summary>
/// <param name="offset">The offset at which the text is inserted.</param>
/// <param name="text">The new text.</param>
/// <param name="defaultAnchorMovementType">
/// Anchors positioned exactly at the insertion offset will move according to the anchor's movement type.
/// For AnchorMovementType.Default, they will move according to the movement type specified by this parameter.
/// The caret will also move according to the <paramref name="defaultAnchorMovementType"/> parameter.
/// </param>
void Insert(int offset, string text, AnchorMovementType defaultAnchorMovementType);
/// <summary>
/// Inserts text.
/// </summary>
/// <param name="offset">The offset at which the text is inserted.</param>
/// <param name="text">The new text.</param>
/// <param name="defaultAnchorMovementType">
/// Anchors positioned exactly at the insertion offset will move according to the anchor's movement type.
/// For AnchorMovementType.Default, they will move according to the movement type specified by this parameter.
/// The caret will also move according to the <paramref name="defaultAnchorMovementType"/> parameter.
/// </param>
void Insert(int offset, ITextSource text, AnchorMovementType defaultAnchorMovementType);
/// <summary>
/// Removes text.
/// </summary>
/// <param name="offset">Starting offset of the text to be removed.</param>
/// <param name="length">Length of the text to be removed.</param>
void Remove(int offset, int length);
/// <summary>
/// Replaces text.
/// </summary>
/// <param name="offset">The starting offset of the text to be replaced.</param>
/// <param name="length">The length of the text to be replaced.</param>
/// <param name="newText">The new text.</param>
void Replace(int offset, int length, string newText);
/// <summary>
/// Replaces text.
/// </summary>
/// <param name="offset">The starting offset of the text to be replaced.</param>
/// <param name="length">The length of the text to be replaced.</param>
/// <param name="newText">The new text.</param>
void Replace(int offset, int length, ITextSource newText);
/// <summary>
/// Make the document combine the following actions into a single
/// action for undo purposes.
/// </summary>
void StartUndoableAction();
/// <summary>
/// Ends the undoable action started with <see cref="StartUndoableAction"/>.
/// </summary>
void EndUndoableAction();
/// <summary>
/// Creates an undo group. Dispose the returned value to close the undo group.
/// </summary>
/// <returns>An object that closes the undo group when Dispose() is called.</returns>
IDisposable OpenUndoGroup();
/// <summary>
/// Creates a new <see cref="ITextAnchor"/> at the specified offset.
/// </summary>
/// <inheritdoc cref="ITextAnchor" select="remarks|example"/>
ITextAnchor CreateAnchor(int offset);
/// <summary>
/// Gets the name of the file the document is stored in.
/// Could also be a non-existent dummy file name or null if no name has been set.
/// </summary>
string FileName { get; }
/// <summary>
/// Fired when the file name of the document changes.
/// </summary>
event EventHandler FileNameChanged;
}
/// <summary>
/// A line inside a <see cref="IDocument"/>.
/// </summary>
public interface IDocumentLine : ISegment
{
/// <summary>
/// Gets the length of this line, including the line delimiter.
/// </summary>
int TotalLength { get; }
/// <summary>
/// Gets the length of the line terminator.
/// Returns 1 or 2; or 0 at the end of the document.
/// </summary>
int DelimiterLength { get; }
/// <summary>
/// Gets the number of this line.
/// The first line has the number 1.
/// </summary>
int LineNumber { get; }
/// <summary>
/// Gets the previous line. Returns null if this is the first line in the document.
/// </summary>
IDocumentLine PreviousLine { get; }
/// <summary>
/// Gets the next line. Returns null if this is the last line in the document.
/// </summary>
IDocumentLine NextLine { get; }
/// <summary>
/// Gets whether the line was deleted.
/// </summary>
bool IsDeleted { get; }
}
/// <summary>
/// Describes a change of the document text.
/// This class is thread-safe.
/// </summary>
[Serializable]
public class TextChangeEventArgs : EventArgs
{
readonly int offset;
readonly ITextSource removedText;
readonly ITextSource insertedText;
/// <summary>
/// The offset at which the change occurs.
/// </summary>
public int Offset {
get { return offset; }
}
/// <summary>
/// The text that was removed.
/// </summary>
public ITextSource RemovedText {
get { return removedText; }
}
/// <summary>
/// The number of characters removed.
/// </summary>
public int RemovalLength {
get { return removedText.TextLength; }
}
/// <summary>
/// The text that was inserted.
/// </summary>
public ITextSource InsertedText {
get { return insertedText; }
}
/// <summary>
/// The number of characters inserted.
/// </summary>
public int InsertionLength {
get { return insertedText.TextLength; }
}
/// <summary>
/// Creates a new TextChangeEventArgs object.
/// </summary>
public TextChangeEventArgs(int offset, string removedText, string insertedText)
{
if (offset < 0)
throw new ArgumentOutOfRangeException("offset", offset, "offset must not be negative");
this.offset = offset;
this.removedText = removedText != null ? new StringTextSource(removedText) : StringTextSource.Empty;
this.insertedText = insertedText != null ? new StringTextSource(insertedText) : StringTextSource.Empty;
}
/// <summary>
/// Creates a new TextChangeEventArgs object.
/// </summary>
public TextChangeEventArgs(int offset, ITextSource removedText, ITextSource insertedText)
{
if (offset < 0)
throw new ArgumentOutOfRangeException("offset", offset, "offset must not be negative");
this.offset = offset;
this.removedText = removedText ?? StringTextSource.Empty;
this.insertedText = insertedText ?? StringTextSource.Empty;
}
/// <summary>
/// Gets the new offset where the specified offset moves after this document change.
/// </summary>
public virtual int GetNewOffset(int offset, AnchorMovementType movementType = AnchorMovementType.Default)
{
if (offset >= this.Offset && offset <= this.Offset + this.RemovalLength) {
if (movementType == AnchorMovementType.BeforeInsertion)
return this.Offset;
else
return this.Offset + this.InsertionLength;
} else if (offset > this.Offset) {
return offset + this.InsertionLength - this.RemovalLength;
} else {
return offset;
}
}
/// <summary>
/// Creates TextChangeEventArgs for the reverse change.
/// </summary>
public virtual TextChangeEventArgs Invert()
{
return new TextChangeEventArgs(offset, insertedText, removedText);
}
}
#endif
}

26
AvalonEdit/ICSharpCode.AvalonEdit/Document/ILineTracker.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
@ -51,5 +66,12 @@ namespace ICSharpCode.AvalonEdit.Document
/// throw away their data and rebuild the document. /// throw away their data and rebuild the document.
/// </summary> /// </summary>
void RebuildDocument(); void RebuildDocument();
/// <summary>
/// Notifies the line tracker that a document change (a single change, not a change group) has completed.
/// This method gets called after the change has been performed, but before the <see cref="TextDocument.Changed"/> event
/// is raised.
/// </summary>
void ChangeComplete(DocumentChangeEventArgs e);
} }
} }

142
AvalonEdit/ICSharpCode.AvalonEdit/Document/ITextAnchor.cs

@ -0,0 +1,142 @@
// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using ICSharpCode.NRefactory;
namespace ICSharpCode.AvalonEdit.Document
{
#if !NREFACTORY
/// <summary>
/// The TextAnchor class references an offset (a position between two characters).
/// It automatically updates the offset when text is inserted/removed in front of the anchor.
/// </summary>
/// <remarks>
/// <para>Use the <see cref="ITextAnchor.Offset"/> property to get the offset from a text anchor.
/// Use the <see cref="IDocument.CreateAnchor"/> method to create an anchor from an offset.
/// </para>
/// <para>
/// The document will automatically update all text anchors; and because it uses weak references to do so,
/// the garbage collector can simply collect the anchor object when you don't need it anymore.
/// </para>
/// <para>Moreover, the document is able to efficiently update a large number of anchors without having to look
/// at each anchor object individually. Updating the offsets of all anchors usually only takes time logarithmic
/// to the number of anchors. Retrieving the <see cref="ITextAnchor.Offset"/> property also runs in O(lg N).</para>
/// </remarks>
/// <example>
/// Usage:
/// <code>TextAnchor anchor = document.CreateAnchor(offset);
/// ChangeMyDocument();
/// int newOffset = anchor.Offset;
/// </code>
/// </example>
public interface ITextAnchor
{
/// <summary>
/// Gets the text location of this anchor.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown when trying to get the Offset from a deleted anchor.</exception>
TextLocation Location { get; }
/// <summary>
/// Gets the offset of the text anchor.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown when trying to get the Offset from a deleted anchor.</exception>
int Offset { get; }
/// <summary>
/// Controls how the anchor moves.
/// </summary>
/// <remarks>Anchor movement is ambiguous if text is inserted exactly at the anchor's location.
/// Does the anchor stay before the inserted text, or does it move after it?
/// The property <see cref="MovementType"/> will be used to determine which of these two options the anchor will choose.
/// The default value is <see cref="AnchorMovementType.Default"/>.</remarks>
AnchorMovementType MovementType { get; set; }
/// <summary>
/// <para>
/// Specifies whether the anchor survives deletion of the text containing it.
/// </para><para>
/// <c>false</c>: The anchor is deleted when the a selection that includes the anchor is deleted.
/// <c>true</c>: The anchor is not deleted.
/// </para>
/// </summary>
/// <remarks><inheritdoc cref="IsDeleted" /></remarks>
bool SurviveDeletion { get; set; }
/// <summary>
/// Gets whether the anchor was deleted.
/// </summary>
/// <remarks>
/// <para>When a piece of text containing an anchor is removed, then that anchor will be deleted.
/// First, the <see cref="IsDeleted"/> property is set to true on all deleted anchors,
/// then the <see cref="Deleted"/> events are raised.
/// You cannot retrieve the offset from an anchor that has been deleted.</para>
/// <para>This deletion behavior might be useful when using anchors for building a bookmark feature,
/// but in other cases you want to still be able to use the anchor. For those cases, set <c><see cref="SurviveDeletion"/> = true</c>.</para>
/// </remarks>
bool IsDeleted { get; }
/// <summary>
/// Occurs after the anchor was deleted.
/// </summary>
/// <remarks>
/// <inheritdoc cref="IsDeleted" />
/// <para>Due to the 'weak reference' nature of text anchors, you will receive
/// the Deleted event only while your code holds a reference to the TextAnchor object.
/// </para>
/// </remarks>
event EventHandler Deleted;
/// <summary>
/// Gets the line number of the anchor.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown when trying to get the Offset from a deleted anchor.</exception>
int Line { get; }
/// <summary>
/// Gets the column number of this anchor.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown when trying to get the Offset from a deleted anchor.</exception>
int Column { get; }
}
/// <summary>
/// Defines how a text anchor moves.
/// </summary>
public enum AnchorMovementType
{
/// <summary>
/// When text is inserted at the anchor position, the type of the insertion
/// determines where the caret moves to. For normal insertions, the anchor will move
/// after the inserted text.
/// </summary>
Default,
/// <summary>
/// Behaves like a start marker - when text is inserted at the anchor position, the anchor will stay
/// before the inserted text.
/// </summary>
BeforeInsertion,
/// <summary>
/// Behave like an end marker - when text is insered at the anchor position, the anchor will move
/// after the inserted text.
/// </summary>
AfterInsertion
}
#endif
}

389
AvalonEdit/ICSharpCode.AvalonEdit/Document/ITextSource.cs

@ -1,29 +1,61 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using ICSharpCode.AvalonEdit.Utils; using System.Collections.Generic;
using System; using System;
using System.IO; using System.IO;
namespace ICSharpCode.AvalonEdit.Document namespace ICSharpCode.AvalonEdit.Document
{ {
#if !NREFACTORY
/// <summary> /// <summary>
/// Interface for read-only access to a text source. /// A read-only view on a (potentially mutable) text source.
/// The IDocument interface derives from this interface.
/// </summary> /// </summary>
/// <seealso cref="TextDocument"/>
/// <seealso cref="StringTextSource"/>
public interface ITextSource public interface ITextSource
{ {
/// <summary> /// <summary>
/// Gets the whole text as string. /// Gets a version identifier for this text source.
/// Returns null for unversioned text sources.
/// </summary> /// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")] ITextSourceVersion Version { get; }
string Text { get; }
/// <summary>
/// Creates an immutable snapshot of this text source.
/// Unlike all other methods in this interface, this method is thread-safe.
/// </summary>
ITextSource CreateSnapshot();
/// <summary>
/// Creates an immutable snapshot of a part of this text source.
/// Unlike all other methods in this interface, this method is thread-safe.
/// </summary>
ITextSource CreateSnapshot(int offset, int length);
/// <summary> /// <summary>
/// Is raised when the Text property changes. /// Creates a new TextReader to read from this text source.
/// </summary> /// </summary>
event EventHandler TextChanged; TextReader CreateReader();
/// <summary>
/// Creates a new TextReader to read from this text source.
/// </summary>
TextReader CreateReader(int offset, int length);
/// <summary> /// <summary>
/// Gets the total text length. /// Gets the total text length.
@ -33,6 +65,12 @@ namespace ICSharpCode.AvalonEdit.Document
/// it doesn't require creating a String object.</remarks> /// it doesn't require creating a String object.</remarks>
int TextLength { get; } int TextLength { get; }
/// <summary>
/// Gets the whole text as string.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")]
string Text { get; }
/// <summary> /// <summary>
/// Gets a character at the specified position in the document. /// Gets a character at the specified position in the document.
/// </summary> /// </summary>
@ -40,18 +78,9 @@ namespace ICSharpCode.AvalonEdit.Document
/// <exception cref="ArgumentOutOfRangeException">Offset is outside the valid range (0 to TextLength-1).</exception> /// <exception cref="ArgumentOutOfRangeException">Offset is outside the valid range (0 to TextLength-1).</exception>
/// <returns>The character at the specified position.</returns> /// <returns>The character at the specified position.</returns>
/// <remarks>This is the same as Text[offset], but is more efficient because /// <remarks>This is the same as Text[offset], but is more efficient because
/// it doesn't require creating a String object.</remarks> /// it doesn't require creating a String object.</remarks>
char GetCharAt(int offset); char GetCharAt(int offset);
/// <summary>
/// Gets the index of the first occurrence of any character in the specified array.
/// </summary>
/// <param name="anyOf"></param>
/// <param name="startIndex">Start index of the search.</param>
/// <param name="count">Length of the area to search.</param>
/// <returns>The first index where any character was found; or -1 if no occurrence was found.</returns>
int IndexOfAny(char[] anyOf, int startIndex, int count);
/// <summary> /// <summary>
/// Retrieves the text for a portion of the document. /// Retrieves the text for a portion of the document.
/// </summary> /// </summary>
@ -61,121 +90,149 @@ namespace ICSharpCode.AvalonEdit.Document
string GetText(int offset, int length); string GetText(int offset, int length);
/// <summary> /// <summary>
/// Creates a snapshot of the current text. /// Retrieves the text for a portion of the document.
/// </summary> /// </summary>
/// <remarks> /// <exception cref="ArgumentOutOfRangeException">offset or length is outside the valid range.</exception>
/// This method is generally not thread-safe when called on a mutable text buffer, but the resulting text buffer is immutable and thread-safe. string GetText(ISegment segment);
/// However, some implementing classes may provide additional thread-safety guarantees, see <see cref="TextDocument.CreateSnapshot()">TextDocument.CreateSnapshot</see>.
/// </remarks>
ITextSource CreateSnapshot();
/// <summary> /// <summary>
/// Creates a snapshot of a part of the current text. /// Writes the text from this document into the TextWriter.
/// </summary> /// </summary>
/// <remarks> void WriteTextTo(TextWriter writer);
/// This method is generally not thread-safe when called on a mutable text buffer, but the resulting text buffer is immutable and thread-safe.
/// However, some implementing classes may provide additional thread-safety guarantees, see <see cref="TextDocument.CreateSnapshot()">TextDocument.CreateSnapshot</see>.
/// </remarks>
ITextSource CreateSnapshot(int offset, int length);
/// <summary> /// <summary>
/// Creates a text reader. /// Writes the text from this document into the TextWriter.
/// If the text is changed while a reader is active, the reader will continue to read from the old text version.
/// </summary> /// </summary>
TextReader CreateReader(); void WriteTextTo(TextWriter writer, int offset, int length);
}
/// <summary>
/// Implements the ITextSource interface by wrapping another TextSource
/// and viewing only a part of the text.
/// </summary>
[Obsolete("This class will be removed in a future version of AvalonEdit")]
public sealed class TextSourceView : ITextSource
{
readonly ITextSource baseTextSource;
readonly ISegment viewedSegment;
/// <summary> /// <summary>
/// Creates a new TextSourceView object. /// Gets the index of the first occurrence of the character in the specified array.
/// </summary> /// </summary>
/// <param name="baseTextSource">The base text source.</param> /// <param name="c">Character to search for</param>
/// <param name="viewedSegment">A text segment from the base text source</param> /// <param name="startIndex">Start index of the area to search.</param>
public TextSourceView(ITextSource baseTextSource, ISegment viewedSegment) /// <param name="count">Length of the area to search.</param>
{ /// <returns>The first index where the character was found; or -1 if no occurrence was found.</returns>
if (baseTextSource == null) int IndexOf(char c, int startIndex, int count);
throw new ArgumentNullException("baseTextSource");
if (viewedSegment == null)
throw new ArgumentNullException("viewedSegment");
this.baseTextSource = baseTextSource;
this.viewedSegment = viewedSegment;
}
/// <inheritdoc/> /// <summary>
public event EventHandler TextChanged { /// Gets the index of the first occurrence of any character in the specified array.
add { baseTextSource.TextChanged += value; } /// </summary>
remove { baseTextSource.TextChanged -= value; } /// <param name="anyOf">Characters to search for</param>
} /// <param name="startIndex">Start index of the area to search.</param>
/// <param name="count">Length of the area to search.</param>
/// <returns>The first index where any character was found; or -1 if no occurrence was found.</returns>
int IndexOfAny(char[] anyOf, int startIndex, int count);
/// <inheritdoc/> /// <summary>
public string Text { /// Gets the index of the first occurrence of the specified search text in this text source.
get { /// </summary>
return baseTextSource.GetText(viewedSegment.Offset, viewedSegment.Length); /// <param name="searchText">The search text</param>
} /// <param name="startIndex">Start index of the area to search.</param>
} /// <param name="count">Length of the area to search.</param>
/// <param name="comparisonType">String comparison to use.</param>
/// <returns>The first index where the search term was found; or -1 if no occurrence was found.</returns>
int IndexOf(string searchText, int startIndex, int count, StringComparison comparisonType);
/// <inheritdoc/> /// <summary>
public int TextLength { /// Gets the index of the last occurrence of the specified character in this text source.
get { return viewedSegment.Length; } /// </summary>
} /// <param name="c">The search character</param>
/// <param name="startIndex">Start index of the area to search.</param>
/// <param name="count">Length of the area to search.</param>
/// <returns>The last index where the search term was found; or -1 if no occurrence was found.</returns>
/// <remarks>The search proceeds backwards from (startIndex+count) to startIndex.
/// This is different than the meaning of the parameters on string.LastIndexOf!</remarks>
int LastIndexOf(char c, int startIndex, int count);
/// <inheritdoc/> /// <summary>
public char GetCharAt(int offset) /// Gets the index of the last occurrence of the specified search text in this text source.
{ /// </summary>
return baseTextSource.GetCharAt(viewedSegment.Offset + offset); /// <param name="searchText">The search text</param>
} /// <param name="startIndex">Start index of the area to search.</param>
/// <param name="count">Length of the area to search.</param>
/// <param name="comparisonType">String comparison to use.</param>
/// <returns>The last index where the search term was found; or -1 if no occurrence was found.</returns>
/// <remarks>The search proceeds backwards from (startIndex+count) to startIndex.
/// This is different than the meaning of the parameters on string.LastIndexOf!</remarks>
int LastIndexOf(string searchText, int startIndex, int count, StringComparison comparisonType);
/// <inheritdoc/> /* What about:
public string GetText(int offset, int length) void Insert (int offset, string value);
{ void Remove (int offset, int count);
return baseTextSource.GetText(viewedSegment.Offset + offset, length); void Remove (ISegment segment);
}
/// <inheritdoc/> void Replace (int offset, int count, string value);
public ITextSource CreateSnapshot()
{
return baseTextSource.CreateSnapshot(viewedSegment.Offset, viewedSegment.Length);
}
/// <inheritdoc/> Or more search operations:
public ITextSource CreateSnapshot(int offset, int length)
{
return baseTextSource.CreateSnapshot(viewedSegment.Offset + offset, length);
}
/// <inheritdoc/> IEnumerable<int> SearchForward (string pattern, int startIndex);
public TextReader CreateReader() IEnumerable<int> SearchForwardIgnoreCase (string pattern, int startIndex);
{
return CreateSnapshot().CreateReader();
}
/// <inheritdoc/> IEnumerable<int> SearchBackward (string pattern, int startIndex);
public int IndexOfAny(char[] anyOf, int startIndex, int count) IEnumerable<int> SearchBackwardIgnoreCase (string pattern, int startIndex);
{ */
int offset = viewedSegment.Offset; }
int result = baseTextSource.IndexOfAny(anyOf, startIndex + offset, count);
return result >= 0 ? result - offset : result; /// <summary>
} /// Represents a version identifier for a text source.
/// </summary>
/// <remarks>
/// Verions can be used to efficiently detect whether a document has changed and needs reparsing;
/// or even to implement incremental parsers.
/// It is a separate class from ITextSource to allow the GC to collect the text source while
/// the version checkpoint is still in use.
/// </remarks>
public interface ITextSourceVersion
{
/// <summary>
/// Gets whether this checkpoint belongs to the same document as the other checkpoint.
/// </summary>
/// <remarks>
/// Returns false when given <c>null</c>.
/// </remarks>
bool BelongsToSameDocumentAs(ITextSourceVersion other);
/// <summary>
/// Compares the age of this checkpoint to the other checkpoint.
/// </summary>
/// <remarks>This method is thread-safe.</remarks>
/// <exception cref="ArgumentException">Raised if 'other' belongs to a different document than this version.</exception>
/// <returns>-1 if this version is older than <paramref name="other"/>.
/// 0 if <c>this</c> version instance represents the same version as <paramref name="other"/>.
/// 1 if this version is newer than <paramref name="other"/>.</returns>
int CompareAge(ITextSourceVersion other);
/// <summary>
/// Gets the changes from this checkpoint to the other checkpoint.
/// If 'other' is older than this checkpoint, reverse changes are calculated.
/// </summary>
/// <remarks>This method is thread-safe.</remarks>
/// <exception cref="ArgumentException">Raised if 'other' belongs to a different document than this checkpoint.</exception>
IEnumerable<TextChangeEventArgs> GetChangesTo(ITextSourceVersion other);
/// <summary>
/// Calculates where the offset has moved in the other buffer version.
/// </summary>
/// <exception cref="ArgumentException">Raised if 'other' belongs to a different document than this checkpoint.</exception>
int MoveOffsetTo(ITextSourceVersion other, int oldOffset, AnchorMovementType movement = AnchorMovementType.Default);
} }
/// <summary> /// <summary>
/// Implements the ITextSource interface using a string. /// Implements the ITextSource interface using a string.
/// </summary> /// </summary>
public sealed class StringTextSource : ITextSource [Serializable]
public class StringTextSource : ITextSource
{ {
/// <summary>
/// Gets a text source containing the empty string.
/// </summary>
public static readonly StringTextSource Empty = new StringTextSource(string.Empty);
readonly string text; readonly string text;
readonly ITextSourceVersion version;
/// <summary> /// <summary>
/// Creates a new StringTextSource. /// Creates a new StringTextSource with the given text.
/// </summary> /// </summary>
public StringTextSource(string text) public StringTextSource(string text)
{ {
@ -184,12 +241,20 @@ namespace ICSharpCode.AvalonEdit.Document
this.text = text; this.text = text;
} }
// Text can never change /// <summary>
event EventHandler ITextSource.TextChanged { add {} remove {} } /// Creates a new StringTextSource with the given text.
/// </summary>
public StringTextSource(string text, ITextSourceVersion version)
{
if (text == null)
throw new ArgumentNullException("text");
this.text = text;
this.version = version;
}
/// <inheritdoc/> /// <inheritdoc/>
public string Text { public ITextSourceVersion Version {
get { return text; } get { return version; }
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -198,123 +263,95 @@ namespace ICSharpCode.AvalonEdit.Document
} }
/// <inheritdoc/> /// <inheritdoc/>
public char GetCharAt(int offset) public string Text {
{ get { return text; }
// GetCharAt must throw ArgumentOutOfRangeException, not IndexOutOfRangeException
if (offset < 0 || offset >= text.Length)
throw new ArgumentOutOfRangeException("offset", offset, "offset must be between 0 and " + (text.Length - 1));
return text[offset];
} }
/// <inheritdoc/> /// <inheritdoc/>
public string GetText(int offset, int length) public ITextSource CreateSnapshot()
{ {
return text.Substring(offset, length); return this; // StringTextSource is immutable
} }
/// <inheritdoc/> /// <inheritdoc/>
public TextReader CreateReader() public ITextSource CreateSnapshot(int offset, int length)
{ {
return new StringReader(text); return new StringTextSource(text.Substring(offset, length));
} }
/// <inheritdoc/> /// <inheritdoc/>
public ITextSource CreateSnapshot() public TextReader CreateReader()
{ {
return this; // StringTextSource already is immutable return new StringReader(text);
} }
/// <inheritdoc/> /// <inheritdoc/>
public ITextSource CreateSnapshot(int offset, int length) public TextReader CreateReader(int offset, int length)
{ {
return new StringTextSource(text.Substring(offset, length)); return new StringReader(text.Substring(offset, length));
} }
/// <inheritdoc/> /// <inheritdoc/>
public int IndexOfAny(char[] anyOf, int startIndex, int count) public void WriteTextTo(TextWriter writer)
{
return text.IndexOfAny(anyOf, startIndex, count);
}
}
/// <summary>
/// Implements the ITextSource interface using a rope.
/// </summary>
public sealed class RopeTextSource : ITextSource
{
readonly Rope<char> rope;
/// <summary>
/// Creates a new RopeTextSource.
/// </summary>
public RopeTextSource(Rope<char> rope)
{ {
if (rope == null) writer.Write(text);
throw new ArgumentNullException("rope");
this.rope = rope;
} }
/// <summary> /// <inheritdoc/>
/// Returns a clone of the rope used for this text source. public void WriteTextTo(TextWriter writer, int offset, int length)
/// </summary>
/// <remarks>
/// RopeTextSource only publishes a copy of the contained rope to ensure that the underlying rope cannot be modified.
/// Unless the creator of the RopeTextSource still has a reference on the rope, RopeTextSource is immutable.
/// </remarks>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification="Not a property because it creates a clone")]
public Rope<char> GetRope()
{ {
return rope.Clone(); writer.Write(text.Substring(offset, length));
} }
// Change event is not supported
event EventHandler ITextSource.TextChanged { add {} remove {} }
/// <inheritdoc/> /// <inheritdoc/>
public string Text { public char GetCharAt(int offset)
get { return rope.ToString(); } {
return text[offset];
} }
/// <inheritdoc/> /// <inheritdoc/>
public int TextLength { public string GetText(int offset, int length)
get { return rope.Length; } {
return text.Substring(offset, length);
} }
/// <inheritdoc/> /// <inheritdoc/>
public char GetCharAt(int offset) public string GetText(ISegment segment)
{ {
return rope[offset]; if (segment == null)
throw new ArgumentNullException("segment");
return text.Substring(segment.Offset, segment.Length);
} }
/// <inheritdoc/> /// <inheritdoc/>
public string GetText(int offset, int length) public int IndexOf(char c, int startIndex, int count)
{ {
return rope.ToString(offset, length); return text.IndexOf(c, startIndex, count);
} }
/// <inheritdoc/> /// <inheritdoc/>
public TextReader CreateReader() public int IndexOfAny(char[] anyOf, int startIndex, int count)
{ {
return new RopeTextReader(rope); return text.IndexOfAny(anyOf, startIndex, count);
} }
/// <inheritdoc/> /// <inheritdoc/>
public ITextSource CreateSnapshot() public int IndexOf(string searchText, int startIndex, int count, StringComparison comparisonType)
{ {
// we clone the underlying rope because the creator of the RopeTextSource might be modifying it return text.IndexOf(searchText, startIndex, count, comparisonType);
return new RopeTextSource(rope.Clone());
} }
/// <inheritdoc/> /// <inheritdoc/>
public ITextSource CreateSnapshot(int offset, int length) public int LastIndexOf(char c, int startIndex, int count)
{ {
return new RopeTextSource(rope.GetRange(offset, length)); return text.LastIndexOf(c, startIndex + count - 1, count);
} }
/// <inheritdoc/> /// <inheritdoc/>
public int IndexOfAny(char[] anyOf, int startIndex, int count) public int LastIndexOf(string searchText, int startIndex, int count, StringComparison comparisonType)
{ {
return rope.IndexOfAny(anyOf, startIndex, count); return text.LastIndexOf(searchText, startIndex + count - 1, count, comparisonType);
} }
} }
#endif
} }

19
AvalonEdit/ICSharpCode.AvalonEdit/Document/IUndoableOperation.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;

46
AvalonEdit/ICSharpCode.AvalonEdit/Document/LineManager.cs

@ -1,11 +1,26 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using ICSharpCode.AvalonEdit.Utils;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using ICSharpCode.NRefactory.Editor;
namespace ICSharpCode.AvalonEdit.Document namespace ICSharpCode.AvalonEdit.Document
{ {
@ -89,6 +104,13 @@ namespace ICSharpCode.AvalonEdit.Document
{ {
// keep the first document line // keep the first document line
DocumentLine ls = documentLineTree.GetByNumber(1); DocumentLine ls = documentLineTree.GetByNumber(1);
// but mark all other lines as deleted, and detach them from the other nodes
for (DocumentLine line = ls.NextLine; line != null; line = line.NextLine) {
line.isDeleted = true;
line.parent = line.left = line.right = null;
}
// Reset the first line to detach it from the deleted lines
ls.ResetLine();
SimpleSegment ds = NewLineFinder.NextNewLine(document, 0); SimpleSegment ds = NewLineFinder.NextNewLine(document, 0);
List<DocumentLine> lines = new List<DocumentLine>(); List<DocumentLine> lines = new List<DocumentLine>();
int lastDelimiterEnd = 0; int lastDelimiterEnd = 0;
@ -101,7 +123,6 @@ namespace ICSharpCode.AvalonEdit.Document
ls = new DocumentLine(document); ls = new DocumentLine(document);
ds = NewLineFinder.NextNewLine(document, lastDelimiterEnd); ds = NewLineFinder.NextNewLine(document, lastDelimiterEnd);
} }
ls.ResetLine();
ls.TotalLength = document.TextLength - lastDelimiterEnd; ls.TotalLength = document.TextLength - lastDelimiterEnd;
lines.Add(ls); lines.Add(ls);
documentLineTree.RebuildTree(lines); documentLineTree.RebuildTree(lines);
@ -181,7 +202,7 @@ namespace ICSharpCode.AvalonEdit.Document
#endregion #endregion
#region Insert #region Insert
public void Insert(int offset, string text) public void Insert(int offset, ITextSource text)
{ {
DocumentLine line = documentLineTree.GetByOffset(offset); DocumentLine line = documentLineTree.GetByOffset(offset);
int lineOffset = line.Offset; int lineOffset = line.Offset;
@ -202,7 +223,7 @@ namespace ICSharpCode.AvalonEdit.Document
if (ds == SimpleSegment.Invalid) { if (ds == SimpleSegment.Invalid) {
// no newline is being inserted, all text is inserted in a single line // no newline is being inserted, all text is inserted in a single line
//line.InsertedLinePart(offset - line.Offset, text.Length); //line.InsertedLinePart(offset - line.Offset, text.Length);
SetLineLength(line, line.TotalLength + text.Length); SetLineLength(line, line.TotalLength + text.TextLength);
return; return;
} }
//DocumentLine firstLine = line; //DocumentLine firstLine = line;
@ -224,9 +245,9 @@ namespace ICSharpCode.AvalonEdit.Document
} }
//firstLine.SplitTo(line); //firstLine.SplitTo(line);
// insert rest after last delimiter // insert rest after last delimiter
if (lastDelimiterEnd != text.Length) { if (lastDelimiterEnd != text.TextLength) {
//line.InsertedLinePart(0, text.Length - lastDelimiterEnd); //line.InsertedLinePart(0, text.Length - lastDelimiterEnd);
SetLineLength(line, line.TotalLength + text.Length - lastDelimiterEnd); SetLineLength(line, line.TotalLength + text.TextLength - lastDelimiterEnd);
} }
} }
@ -284,5 +305,14 @@ namespace ICSharpCode.AvalonEdit.Document
return line; return line;
} }
#endregion #endregion
#region ChangeComplete
public void ChangeComplete(DocumentChangeEventArgs e)
{
foreach (ILineTracker lt in lineTrackers) {
lt.ChangeComplete(e);
}
}
#endregion
} }
} }

19
AvalonEdit/ICSharpCode.AvalonEdit/Document/LineNode.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;

28
AvalonEdit/ICSharpCode.AvalonEdit/Document/NewLineFinder.cs

@ -1,10 +1,26 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text; using System.Text;
#if NREFACTORY
using ICSharpCode.NRefactory.Editor;
#endif
namespace ICSharpCode.AvalonEdit.Document namespace ICSharpCode.AvalonEdit.Document
{ {
@ -118,9 +134,9 @@ namespace ICSharpCode.AvalonEdit.Document
/// <summary> /// <summary>
/// Gets the newline sequence used in the document at the specified line. /// Gets the newline sequence used in the document at the specified line.
/// </summary> /// </summary>
public static string GetNewLineFromDocument(TextDocument document, int lineNumber) public static string GetNewLineFromDocument(IDocument document, int lineNumber)
{ {
DocumentLine line = document.GetLineByNumber(lineNumber); IDocumentLine line = document.GetLineByNumber(lineNumber);
if (line.DelimiterLength == 0) { if (line.DelimiterLength == 0) {
// at the end of the document, there's no line delimiter, so use the delimiter // at the end of the document, there's no line delimiter, so use the delimiter
// from the previous line // from the previous line

29
AvalonEdit/ICSharpCode.AvalonEdit/Document/OffsetChangeMap.cs

@ -1,12 +1,29 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.AvalonEdit.Utils;
#if NREFACTORY
using ICSharpCode.NRefactory.Editor;
#endif
namespace ICSharpCode.AvalonEdit.Document namespace ICSharpCode.AvalonEdit.Document
{ {
@ -18,7 +35,7 @@ namespace ICSharpCode.AvalonEdit.Document
/// <summary> /// <summary>
/// Normal replace. /// Normal replace.
/// Anchors in front of the replaced region will stay in front, anchors after the replaced region will stay after. /// Anchors in front of the replaced region will stay in front, anchors after the replaced region will stay after.
/// Anchors in the middle of the removed region will be deleted. Ifthey survive deletion, /// Anchors in the middle of the removed region will be deleted. If they survive deletion,
/// they move depending on their AnchorMovementType. /// they move depending on their AnchorMovementType.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
@ -108,7 +125,7 @@ namespace ICSharpCode.AvalonEdit.Document
/// <summary> /// <summary>
/// Gets the new offset where the specified offset moves after this document change. /// Gets the new offset where the specified offset moves after this document change.
/// </summary> /// </summary>
public int GetNewOffset(int offset, AnchorMovementType movementType) public int GetNewOffset(int offset, AnchorMovementType movementType = AnchorMovementType.Default)
{ {
IList<OffsetChangeMapEntry> items = this.Items; IList<OffsetChangeMapEntry> items = this.Items;
int count = items.Count; int count = items.Count;
@ -255,7 +272,7 @@ namespace ICSharpCode.AvalonEdit.Document
/// <summary> /// <summary>
/// Gets the new offset where the specified offset moves after this document change. /// Gets the new offset where the specified offset moves after this document change.
/// </summary> /// </summary>
public int GetNewOffset(int oldOffset, AnchorMovementType movementType) public int GetNewOffset(int oldOffset, AnchorMovementType movementType = AnchorMovementType.Default)
{ {
int insertionLength = this.InsertionLength; int insertionLength = this.InsertionLength;
int removalLength = this.RemovalLength; int removalLength = this.RemovalLength;

169
AvalonEdit/ICSharpCode.AvalonEdit/Document/RopeTextSource.cs

@ -0,0 +1,169 @@
// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.IO;
using ICSharpCode.AvalonEdit.Utils;
#if NREFACTORY
using ICSharpCode.NRefactory.Editor;
#endif
namespace ICSharpCode.AvalonEdit.Document
{
/// <summary>
/// Implements the ITextSource interface using a rope.
/// </summary>
[Serializable]
public sealed class RopeTextSource : ITextSource
{
readonly Rope<char> rope;
readonly ITextSourceVersion version;
/// <summary>
/// Creates a new RopeTextSource.
/// </summary>
public RopeTextSource(Rope<char> rope)
{
if (rope == null)
throw new ArgumentNullException("rope");
this.rope = rope.Clone();
}
/// <summary>
/// Creates a new RopeTextSource.
/// </summary>
public RopeTextSource(Rope<char> rope, ITextSourceVersion version)
{
if (rope == null)
throw new ArgumentNullException("rope");
this.rope = rope.Clone();
this.version = version;
}
/// <summary>
/// Returns a clone of the rope used for this text source.
/// </summary>
/// <remarks>
/// RopeTextSource only publishes a copy of the contained rope to ensure that the underlying rope cannot be modified.
/// </remarks>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification="Not a property because it creates a clone")]
public Rope<char> GetRope()
{
return rope.Clone();
}
/// <inheritdoc/>
public string Text {
get { return rope.ToString(); }
}
/// <inheritdoc/>
public int TextLength {
get { return rope.Length; }
}
/// <inheritdoc/>
public char GetCharAt(int offset)
{
return rope[offset];
}
/// <inheritdoc/>
public string GetText(int offset, int length)
{
return rope.ToString(offset, length);
}
/// <inheritdoc/>
public string GetText(ISegment segment)
{
return rope.ToString(segment.Offset, segment.Length);
}
/// <inheritdoc/>
public TextReader CreateReader()
{
return new RopeTextReader(rope);
}
/// <inheritdoc/>
public TextReader CreateReader(int offset, int length)
{
return new RopeTextReader(rope.GetRange(offset, length));
}
/// <inheritdoc/>
public ITextSource CreateSnapshot()
{
return this;
}
/// <inheritdoc/>
public ITextSource CreateSnapshot(int offset, int length)
{
return new RopeTextSource(rope.GetRange(offset, length));
}
/// <inheritdoc/>
public int IndexOf(char c, int startIndex, int count)
{
return rope.IndexOf(c, startIndex, count);
}
/// <inheritdoc/>
public int IndexOfAny(char[] anyOf, int startIndex, int count)
{
return rope.IndexOfAny(anyOf, startIndex, count);
}
/// <inheritdoc/>
public int LastIndexOf(char c, int startIndex, int count)
{
return rope.LastIndexOf(c, startIndex, count);
}
/// <inheritdoc/>
public ITextSourceVersion Version {
get { return version; }
}
/// <inheritdoc/>
public int IndexOf(string searchText, int startIndex, int count, StringComparison comparisonType)
{
return rope.IndexOf(searchText, startIndex, count, comparisonType);
}
/// <inheritdoc/>
public int LastIndexOf(string searchText, int startIndex, int count, StringComparison comparisonType)
{
return rope.LastIndexOf(searchText, startIndex, count, comparisonType);
}
/// <inheritdoc/>
public void WriteTextTo(TextWriter writer)
{
rope.WriteTo(writer, 0, rope.Length);
}
/// <inheritdoc/>
public void WriteTextTo(TextWriter writer, int offset, int length)
{
rope.WriteTo(writer, offset, length);
}
}
}

80
AvalonEdit/ICSharpCode.AvalonEdit/Document/ISegment.cs → AvalonEdit/ICSharpCode.AvalonEdit/Document/SimpleSegment.cs

@ -1,75 +1,52 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using ICSharpCode.AvalonEdit.Utils;
using System.Globalization; using System.Globalization;
using ICSharpCode.AvalonEdit.Utils;
#if NREFACTORY
using ICSharpCode.NRefactory.Editor;
#endif
namespace ICSharpCode.AvalonEdit.Document namespace ICSharpCode.AvalonEdit.Document
{ {
/// <summary> /// <summary>
/// An (Offset,Length)-pair. /// Represents a simple segment (Offset,Length pair) that is not automatically updated
/// on document changes.
/// </summary> /// </summary>
/// <seealso cref="TextSegment"/> struct SimpleSegment : IEquatable<SimpleSegment>, ISegment
/// <seealso cref="AnchorSegment"/>
public interface ISegment
{
/// <summary>
/// Gets the start offset of the segment.
/// </summary>
int Offset { get; }
/// <summary>
/// Gets the length of the segment.
/// </summary>
/// <remarks>Must not be negative.</remarks>
int Length { get; }
/// <summary>
/// Gets the end offset of the segment.
/// </summary>
/// <remarks>EndOffset = Offset + Length;</remarks>
int EndOffset { get; }
}
static class SegmentExtensions
{ {
/// <summary> public static readonly SimpleSegment Invalid = new SimpleSegment(-1, -1);
/// Gets whether the segment contains the offset.
/// </summary>
/// <returns>
/// True, if offset is between segment.Start and segment.End (inclusive); otherwise, false.
/// </returns>
public static bool Contains(this ISegment segment, int offset)
{
int start = segment.Offset;
int end = start + segment.Length;
return offset >= start && offset <= end;
}
/// <summary> /// <summary>
/// Gets the overlapping portion of the segments. /// Gets the overlapping portion of the segments.
/// Returns SimpleSegment.Invalid if the segments don't overlap. /// Returns SimpleSegment.Invalid if the segments don't overlap.
/// </summary> /// </summary>
public static SimpleSegment GetOverlap(this ISegment segment, ISegment other) public static SimpleSegment GetOverlap(ISegment segment1, ISegment segment2)
{ {
int start = Math.Max(segment.Offset, other.Offset); int start = Math.Max(segment1.Offset, segment2.Offset);
int end = Math.Min(segment.EndOffset, other.EndOffset); int end = Math.Min(segment1.EndOffset, segment2.EndOffset);
if (end < start) if (end < start)
return SimpleSegment.Invalid; return SimpleSegment.Invalid;
else else
return new SimpleSegment(start, end - start); return new SimpleSegment(start, end - start);
} }
}
/// <summary>
/// Represents a simple segment (Offset,Length pair) that is not automatically updated
/// on document changes.
/// </summary>
struct SimpleSegment : IEquatable<SimpleSegment>, ISegment
{
public static readonly SimpleSegment Invalid = new SimpleSegment(-1, -1);
public readonly int Offset, Length; public readonly int Offset, Length;
@ -127,6 +104,7 @@ namespace ICSharpCode.AvalonEdit.Document
return !left.Equals(right); return !left.Equals(right);
} }
/// <inheritdoc/>
public override string ToString() public override string ToString()
{ {
return "[Offset=" + Offset.ToString(CultureInfo.InvariantCulture) + ", Length=" + Length.ToString(CultureInfo.InvariantCulture) + "]"; return "[Offset=" + Offset.ToString(CultureInfo.InvariantCulture) + ", Length=" + Length.ToString(CultureInfo.InvariantCulture) + "]";

86
AvalonEdit/ICSharpCode.AvalonEdit/Document/TextAnchor.cs

@ -1,8 +1,26 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.AvalonEdit.Utils;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Editor;
using ICSharpCode.AvalonEdit.Document;
namespace ICSharpCode.AvalonEdit.Document namespace ICSharpCode.AvalonEdit.Document
{ {
@ -33,7 +51,7 @@ namespace ICSharpCode.AvalonEdit.Document
/// int newOffset = anchor.Offset; /// int newOffset = anchor.Offset;
/// </code> /// </code>
/// </example> /// </example>
public sealed class TextAnchor public sealed class TextAnchor : ITextAnchor
{ {
readonly TextDocument document; readonly TextDocument document;
internal TextAnchorNode node; internal TextAnchorNode node;
@ -50,37 +68,13 @@ namespace ICSharpCode.AvalonEdit.Document
get { return document; } get { return document; }
} }
/// <summary> /// <inheritdoc/>
/// Controls how the anchor moves.
/// </summary>
/// <remarks>Anchor movement is ambiguous if text is inserted exactly at the anchor's location.
/// Does the anchor stay before the inserted text, or does it move after it?
/// The property <see cref="MovementType"/> will be used to determine which of these two options the anchor will choose.
/// The default value is <see cref="AnchorMovementType.Default"/>.</remarks>
public AnchorMovementType MovementType { get; set; } public AnchorMovementType MovementType { get; set; }
/// <summary> /// <inheritdoc/>
/// <para>
/// Specifies whether the anchor survives deletion of the text containing it.
/// </para><para>
/// <c>false</c>: The anchor is deleted when the a selection that includes the anchor is deleted.
/// <c>true</c>: The anchor is not deleted.
/// </para>
/// </summary>
/// <remarks><inheritdoc cref="IsDeleted" /></remarks>
public bool SurviveDeletion { get; set; } public bool SurviveDeletion { get; set; }
/// <summary> /// <inheritdoc/>
/// Gets whether the anchor was deleted.
/// </summary>
/// <remarks>
/// <para>When a piece of text containing an anchor is removed, then that anchor will be deleted.
/// First, the <see cref="IsDeleted"/> property is set to true on all deleted anchors,
/// then the <see cref="Deleted"/> events are raised.
/// You cannot retrieve the offset from an anchor that has been deleted.</para>
/// <para>This deletion behavior might be useful when using anchors for building a bookmark feature,
/// but in other cases you want to still be able to use the anchor. For those cases, set <c><see cref="SurviveDeletion"/> = true</c>.</para>
/// </remarks>
public bool IsDeleted { public bool IsDeleted {
get { get {
document.DebugVerifyAccess(); document.DebugVerifyAccess();
@ -88,14 +82,7 @@ namespace ICSharpCode.AvalonEdit.Document
} }
} }
/// <summary> /// <inheritdoc/>
/// Occurs after the anchor was deleted.
/// </summary>
/// <remarks>
/// <inheritdoc cref="IsDeleted" />
/// <para>Due to the 'weak reference' nature of TextAnchor, you will receive the Deleted event only
/// while your code holds a reference to the TextAnchor object.</para>
/// </remarks>
public event EventHandler Deleted; public event EventHandler Deleted;
internal void OnDeleted(DelayedEvents delayedEvents) internal void OnDeleted(DelayedEvents delayedEvents)
@ -168,27 +155,4 @@ namespace ICSharpCode.AvalonEdit.Document
return "[TextAnchor Offset=" + Offset + "]"; return "[TextAnchor Offset=" + Offset + "]";
} }
} }
/// <summary>
/// Defines how a text anchor moves.
/// </summary>
public enum AnchorMovementType
{
/// <summary>
/// When text is inserted at the anchor position, the type of the insertion
/// determines where the caret moves to. For normal insertions, the anchor will stay
/// behind the inserted text.
/// </summary>
Default,
/// <summary>
/// When text is inserted at the anchor position, the anchor will stay
/// before the inserted text.
/// </summary>
BeforeInsertion,
/// <summary>
/// When text is insered at the anchor position, the anchor will move
/// after the inserted text.
/// </summary>
AfterInsertion
}
} }

19
AvalonEdit/ICSharpCode.AvalonEdit/Document/TextAnchorNode.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;

23
AvalonEdit/ICSharpCode.AvalonEdit/Document/TextAnchorTree.cs

@ -1,12 +1,29 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Text; using System.Text;
using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.AvalonEdit.Utils;
#if NREFACTORY
using ICSharpCode.NRefactory.Editor;
#endif
namespace ICSharpCode.AvalonEdit.Document namespace ICSharpCode.AvalonEdit.Document
{ {

423
AvalonEdit/ICSharpCode.AvalonEdit/Document/TextDocument.cs

@ -1,16 +1,32 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.ComponentModel; using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using System.Globalization; using System.Globalization;
using System.Linq.Expressions;
using System.Threading; using System.Threading;
using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.AvalonEdit.Utils;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Editor;
namespace ICSharpCode.AvalonEdit.Document namespace ICSharpCode.AvalonEdit.Document
{ {
@ -22,7 +38,7 @@ namespace ICSharpCode.AvalonEdit.Document
/// <inheritdoc cref="VerifyAccess"/> /// <inheritdoc cref="VerifyAccess"/>
/// <para>However, there is a single method that is thread-safe: <see cref="CreateSnapshot()"/> (and its overloads).</para> /// <para>However, there is a single method that is thread-safe: <see cref="CreateSnapshot()"/> (and its overloads).</para>
/// </remarks> /// </remarks>
public sealed class TextDocument : ITextSource, INotifyPropertyChanged public sealed class TextDocument : IDocument, INotifyPropertyChanged
{ {
#region Thread ownership #region Thread ownership
readonly object lockObject = new object(); readonly object lockObject = new object();
@ -73,7 +89,7 @@ namespace ICSharpCode.AvalonEdit.Document
readonly DocumentLineTree lineTree; readonly DocumentLineTree lineTree;
readonly LineManager lineManager; readonly LineManager lineManager;
readonly TextAnchorTree anchorTree; readonly TextAnchorTree anchorTree;
ChangeTrackingCheckpoint currentCheckpoint; readonly TextSourceVersionProvider versionProvider = new TextSourceVersionProvider();
/// <summary> /// <summary>
/// Create an empty text document. /// Create an empty text document.
@ -116,6 +132,11 @@ namespace ICSharpCode.AvalonEdit.Document
if (textSource == null) if (textSource == null)
throw new ArgumentNullException("textSource"); throw new ArgumentNullException("textSource");
#if NREFACTORY
if (textSource is ReadOnlyDocument)
textSource = textSource.CreateSnapshot(); // retrieve underlying text source, which might be a RopeTextSource
#endif
RopeTextSource rts = textSource as RopeTextSource; RopeTextSource rts = textSource as RopeTextSource;
if (rts != null) if (rts != null)
return rts.GetRope(); return rts.GetRope();
@ -156,12 +177,41 @@ namespace ICSharpCode.AvalonEdit.Document
return GetText(segment.Offset, segment.Length); return GetText(segment.Offset, segment.Length);
} }
int ITextSource.IndexOfAny(char[] anyOf, int startIndex, int count) /// <inheritdoc/>
public int IndexOf(char c, int startIndex, int count)
{
DebugVerifyAccess();
return rope.IndexOf(c, startIndex, count);
}
/// <inheritdoc/>
public int LastIndexOf(char c, int startIndex, int count)
{
DebugVerifyAccess();
return rope.LastIndexOf(c, startIndex, count);
}
/// <inheritdoc/>
public int IndexOfAny(char[] anyOf, int startIndex, int count)
{ {
DebugVerifyAccess(); // frequently called (NewLineFinder), so must be fast in release builds DebugVerifyAccess(); // frequently called (NewLineFinder), so must be fast in release builds
return rope.IndexOfAny(anyOf, startIndex, count); return rope.IndexOfAny(anyOf, startIndex, count);
} }
/// <inheritdoc/>
public int IndexOf(string searchText, int startIndex, int count, StringComparison comparisonType)
{
DebugVerifyAccess();
return rope.IndexOf(searchText, startIndex, count, comparisonType);
}
/// <inheritdoc/>
public int LastIndexOf(string searchText, int startIndex, int count, StringComparison comparisonType)
{
DebugVerifyAccess();
return rope.LastIndexOf(searchText, startIndex, count, comparisonType);
}
/// <inheritdoc/> /// <inheritdoc/>
public char GetCharAt(int offset) public char GetCharAt(int offset)
{ {
@ -192,10 +242,17 @@ namespace ICSharpCode.AvalonEdit.Document
} }
} }
/// <inheritdoc/> /// <summary>
/// This event is called after a group of changes is completed.
/// </summary>
/// <remarks><inheritdoc cref="Changing"/></remarks> /// <remarks><inheritdoc cref="Changing"/></remarks>
public event EventHandler TextChanged; public event EventHandler TextChanged;
event EventHandler IDocument.ChangeCompleted {
add { this.TextChanged += value; }
remove { this.TextChanged -= value; }
}
/// <inheritdoc/> /// <inheritdoc/>
public int TextLength { public int TextLength {
get { get {
@ -257,12 +314,27 @@ namespace ICSharpCode.AvalonEdit.Document
/// </remarks> /// </remarks>
public event EventHandler<DocumentChangeEventArgs> Changing; public event EventHandler<DocumentChangeEventArgs> Changing;
// Unfortunately EventHandler<T> is invariant, so we have to use two separate events
private event EventHandler<TextChangeEventArgs> textChanging;
event EventHandler<TextChangeEventArgs> IDocument.TextChanging {
add { textChanging += value; }
remove { textChanging -= value; }
}
/// <summary> /// <summary>
/// Is raised after the document has changed. /// Is raised after the document has changed.
/// </summary> /// </summary>
/// <remarks><inheritdoc cref="Changing"/></remarks> /// <remarks><inheritdoc cref="Changing"/></remarks>
public event EventHandler<DocumentChangeEventArgs> Changed; public event EventHandler<DocumentChangeEventArgs> Changed;
private event EventHandler<TextChangeEventArgs> textChanged;
event EventHandler<TextChangeEventArgs> IDocument.TextChanged {
add { textChanged += value; }
remove { textChanged -= value; }
}
/// <summary> /// <summary>
/// Creates a snapshot of the current text. /// Creates a snapshot of the current text.
/// </summary> /// </summary>
@ -278,53 +350,63 @@ namespace ICSharpCode.AvalonEdit.Document
public ITextSource CreateSnapshot() public ITextSource CreateSnapshot()
{ {
lock (lockObject) { lock (lockObject) {
return new RopeTextSource(rope.Clone()); return new RopeTextSource(rope, versionProvider.CurrentVersion);
} }
} }
/// <summary> /// <summary>
/// Creates a snapshot of the current text. /// Creates a snapshot of a part of the current text.
/// Additionally, creates a checkpoint that allows tracking document changes.
/// </summary> /// </summary>
/// <remarks><inheritdoc cref="CreateSnapshot()"/><inheritdoc cref="ChangeTrackingCheckpoint"/></remarks> /// <remarks><inheritdoc cref="CreateSnapshot()"/></remarks>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Justification = "Need to return snapshot and checkpoint together to ensure thread-safety")] public ITextSource CreateSnapshot(int offset, int length)
public ITextSource CreateSnapshot(out ChangeTrackingCheckpoint checkpoint)
{ {
lock (lockObject) { lock (lockObject) {
if (currentCheckpoint == null) return new RopeTextSource(rope.GetRange(offset, length));
currentCheckpoint = new ChangeTrackingCheckpoint(lockObject);
checkpoint = currentCheckpoint;
return new RopeTextSource(rope.Clone());
} }
} }
internal ChangeTrackingCheckpoint CreateChangeTrackingCheckpoint() #if NREFACTORY
/// <inheritdoc/>
public IDocument CreateDocumentSnapshot()
{ {
lock (lockObject) { return new ReadOnlyDocument(this, fileName);
if (currentCheckpoint == null)
currentCheckpoint = new ChangeTrackingCheckpoint(lockObject);
return currentCheckpoint;
}
} }
#endif
/// <summary> /// <inheritdoc/>
/// Creates a snapshot of a part of the current text. public ITextSourceVersion Version {
/// </summary> get { return versionProvider.CurrentVersion; }
/// <remarks><inheritdoc cref="CreateSnapshot()"/></remarks> }
public ITextSource CreateSnapshot(int offset, int length)
/// <inheritdoc/>
public System.IO.TextReader CreateReader()
{ {
lock (lockObject) { lock (lockObject) {
return new RopeTextSource(rope.GetRange(offset, length)); return new RopeTextReader(rope);
} }
} }
/// <inheritdoc/> /// <inheritdoc/>
public System.IO.TextReader CreateReader() public System.IO.TextReader CreateReader(int offset, int length)
{ {
lock (lockObject) { lock (lockObject) {
return new RopeTextReader(rope); return new RopeTextReader(rope.GetRange(offset, length));
} }
} }
/// <inheritdoc/>
public void WriteTextTo(System.IO.TextWriter writer)
{
VerifyAccess();
rope.WriteTo(writer, 0, rope.Length);
}
/// <inheritdoc/>
public void WriteTextTo(System.IO.TextWriter writer, int offset, int length)
{
VerifyAccess();
rope.WriteTo(writer, offset, length);
}
#endregion #endregion
#region BeginUpdate / EndUpdate #region BeginUpdate / EndUpdate
@ -408,6 +490,21 @@ namespace ICSharpCode.AvalonEdit.Document
/// </summary> /// </summary>
/// <remarks><inheritdoc cref="Changing"/></remarks> /// <remarks><inheritdoc cref="Changing"/></remarks>
public event EventHandler UpdateFinished; public event EventHandler UpdateFinished;
void IDocument.StartUndoableAction()
{
BeginUpdate();
}
void IDocument.EndUndoableAction()
{
EndUpdate();
}
IDisposable IDocument.OpenUndoGroup()
{
return RunUpdate();
}
#endregion #endregion
#region Fire events after update #region Fire events after update
@ -456,9 +553,69 @@ namespace ICSharpCode.AvalonEdit.Document
/// <summary> /// <summary>
/// Inserts text. /// Inserts text.
/// </summary> /// </summary>
/// <param name="offset">The offset at which the text is inserted.</param>
/// <param name="text">The new text.</param>
/// <remarks>
/// Anchors positioned exactly at the insertion offset will move according to their movement type.
/// For AnchorMovementType.Default, they will move behind the inserted text.
/// The caret will also move behind the inserted text.
/// </remarks>
public void Insert(int offset, string text) public void Insert(int offset, string text)
{ {
Replace(offset, 0, text); Replace(offset, 0, new StringTextSource(text), null);
}
/// <summary>
/// Inserts text.
/// </summary>
/// <param name="offset">The offset at which the text is inserted.</param>
/// <param name="text">The new text.</param>
/// <remarks>
/// Anchors positioned exactly at the insertion offset will move according to their movement type.
/// For AnchorMovementType.Default, they will move behind the inserted text.
/// The caret will also move behind the inserted text.
/// </remarks>
public void Insert(int offset, ITextSource text)
{
Replace(offset, 0, text, null);
}
/// <summary>
/// Inserts text.
/// </summary>
/// <param name="offset">The offset at which the text is inserted.</param>
/// <param name="text">The new text.</param>
/// <param name="defaultAnchorMovementType">
/// Anchors positioned exactly at the insertion offset will move according to the anchor's movement type.
/// For AnchorMovementType.Default, they will move according to the movement type specified by this parameter.
/// The caret will also move according to the <paramref name="defaultAnchorMovementType"/> parameter.
/// </param>
public void Insert(int offset, string text, AnchorMovementType defaultAnchorMovementType)
{
if (defaultAnchorMovementType == AnchorMovementType.BeforeInsertion) {
Replace(offset, 0, new StringTextSource(text), OffsetChangeMappingType.KeepAnchorBeforeInsertion);
} else {
Replace(offset, 0, new StringTextSource(text), null);
}
}
/// <summary>
/// Inserts text.
/// </summary>
/// <param name="offset">The offset at which the text is inserted.</param>
/// <param name="text">The new text.</param>
/// <param name="defaultAnchorMovementType">
/// Anchors positioned exactly at the insertion offset will move according to the anchor's movement type.
/// For AnchorMovementType.Default, they will move according to the movement type specified by this parameter.
/// The caret will also move according to the <paramref name="defaultAnchorMovementType"/> parameter.
/// </param>
public void Insert(int offset, ITextSource text, AnchorMovementType defaultAnchorMovementType)
{
if (defaultAnchorMovementType == AnchorMovementType.BeforeInsertion) {
Replace(offset, 0, text, OffsetChangeMappingType.KeepAnchorBeforeInsertion);
} else {
Replace(offset, 0, text, null);
}
} }
/// <summary> /// <summary>
@ -472,9 +629,11 @@ namespace ICSharpCode.AvalonEdit.Document
/// <summary> /// <summary>
/// Removes text. /// Removes text.
/// </summary> /// </summary>
/// <param name="offset">Starting offset of the text to be removed.</param>
/// <param name="length">Length of the text to be removed.</param>
public void Remove(int offset, int length) public void Remove(int offset, int length)
{ {
Replace(offset, length, string.Empty); Replace(offset, length, StringTextSource.Empty);
} }
internal bool inDocumentChanging; internal bool inDocumentChanging;
@ -483,6 +642,16 @@ namespace ICSharpCode.AvalonEdit.Document
/// Replaces text. /// Replaces text.
/// </summary> /// </summary>
public void Replace(ISegment segment, string text) public void Replace(ISegment segment, string text)
{
if (segment == null)
throw new ArgumentNullException("segment");
Replace(segment.Offset, segment.Length, new StringTextSource(text), null);
}
/// <summary>
/// Replaces text.
/// </summary>
public void Replace(ISegment segment, ITextSource text)
{ {
if (segment == null) if (segment == null)
throw new ArgumentNullException("segment"); throw new ArgumentNullException("segment");
@ -492,7 +661,21 @@ namespace ICSharpCode.AvalonEdit.Document
/// <summary> /// <summary>
/// Replaces text. /// Replaces text.
/// </summary> /// </summary>
/// <param name="offset">The starting offset of the text to be replaced.</param>
/// <param name="length">The length of the text to be replaced.</param>
/// <param name="text">The new text.</param>
public void Replace(int offset, int length, string text) public void Replace(int offset, int length, string text)
{
Replace(offset, length, new StringTextSource(text), null);
}
/// <summary>
/// Replaces text.
/// </summary>
/// <param name="offset">The starting offset of the text to be replaced.</param>
/// <param name="length">The length of the text to be replaced.</param>
/// <param name="text">The new text.</param>
public void Replace(int offset, int length, ITextSource text)
{ {
Replace(offset, length, text, null); Replace(offset, length, text, null);
} }
@ -506,6 +689,19 @@ namespace ICSharpCode.AvalonEdit.Document
/// <param name="offsetChangeMappingType">The offsetChangeMappingType determines how offsets inside the old text are mapped to the new text. /// <param name="offsetChangeMappingType">The offsetChangeMappingType determines how offsets inside the old text are mapped to the new text.
/// This affects how the anchors and segments inside the replaced region behave.</param> /// This affects how the anchors and segments inside the replaced region behave.</param>
public void Replace(int offset, int length, string text, OffsetChangeMappingType offsetChangeMappingType) public void Replace(int offset, int length, string text, OffsetChangeMappingType offsetChangeMappingType)
{
Replace(offset, length, new StringTextSource(text), offsetChangeMappingType);
}
/// <summary>
/// Replaces text.
/// </summary>
/// <param name="offset">The starting offset of the text to be replaced.</param>
/// <param name="length">The length of the text to be replaced.</param>
/// <param name="text">The new text.</param>
/// <param name="offsetChangeMappingType">The offsetChangeMappingType determines how offsets inside the old text are mapped to the new text.
/// This affects how the anchors and segments inside the replaced region behave.</param>
public void Replace(int offset, int length, ITextSource text, OffsetChangeMappingType offsetChangeMappingType)
{ {
if (text == null) if (text == null)
throw new ArgumentNullException("text"); throw new ArgumentNullException("text");
@ -516,33 +712,33 @@ namespace ICSharpCode.AvalonEdit.Document
break; break;
case OffsetChangeMappingType.KeepAnchorBeforeInsertion: case OffsetChangeMappingType.KeepAnchorBeforeInsertion:
Replace(offset, length, text, OffsetChangeMap.FromSingleElement( Replace(offset, length, text, OffsetChangeMap.FromSingleElement(
new OffsetChangeMapEntry(offset, length, text.Length, false, true))); new OffsetChangeMapEntry(offset, length, text.TextLength, false, true)));
break; break;
case OffsetChangeMappingType.RemoveAndInsert: case OffsetChangeMappingType.RemoveAndInsert:
if (length == 0 || text.Length == 0) { if (length == 0 || text.TextLength == 0) {
// only insertion or only removal? // only insertion or only removal?
// OffsetChangeMappingType doesn't matter, just use Normal. // OffsetChangeMappingType doesn't matter, just use Normal.
Replace(offset, length, text, null); Replace(offset, length, text, null);
} else { } else {
OffsetChangeMap map = new OffsetChangeMap(2); OffsetChangeMap map = new OffsetChangeMap(2);
map.Add(new OffsetChangeMapEntry(offset, length, 0)); map.Add(new OffsetChangeMapEntry(offset, length, 0));
map.Add(new OffsetChangeMapEntry(offset, 0, text.Length)); map.Add(new OffsetChangeMapEntry(offset, 0, text.TextLength));
map.Freeze(); map.Freeze();
Replace(offset, length, text, map); Replace(offset, length, text, map);
} }
break; break;
case OffsetChangeMappingType.CharacterReplace: case OffsetChangeMappingType.CharacterReplace:
if (length == 0 || text.Length == 0) { if (length == 0 || text.TextLength == 0) {
// only insertion or only removal? // only insertion or only removal?
// OffsetChangeMappingType doesn't matter, just use Normal. // OffsetChangeMappingType doesn't matter, just use Normal.
Replace(offset, length, text, null); Replace(offset, length, text, null);
} else if (text.Length > length) { } else if (text.TextLength > length) {
// look at OffsetChangeMappingType.CharacterReplace XML comments on why we need to replace // look at OffsetChangeMappingType.CharacterReplace XML comments on why we need to replace
// the last character // the last character
OffsetChangeMapEntry entry = new OffsetChangeMapEntry(offset + length - 1, 1, 1 + text.Length - length); OffsetChangeMapEntry entry = new OffsetChangeMapEntry(offset + length - 1, 1, 1 + text.TextLength - length);
Replace(offset, length, text, OffsetChangeMap.FromSingleElement(entry)); Replace(offset, length, text, OffsetChangeMap.FromSingleElement(entry));
} else if (text.Length < length) { } else if (text.TextLength < length) {
OffsetChangeMapEntry entry = new OffsetChangeMapEntry(offset + text.Length, length - text.Length, 0, true, false); OffsetChangeMapEntry entry = new OffsetChangeMapEntry(offset + text.TextLength, length - text.TextLength, 0, true, false);
Replace(offset, length, text, OffsetChangeMap.FromSingleElement(entry)); Replace(offset, length, text, OffsetChangeMap.FromSingleElement(entry));
} else { } else {
Replace(offset, length, text, OffsetChangeMap.Empty); Replace(offset, length, text, OffsetChangeMap.Empty);
@ -569,10 +765,30 @@ namespace ICSharpCode.AvalonEdit.Document
/// DocumentChangeEventArgs instance. /// DocumentChangeEventArgs instance.
/// </param> /// </param>
public void Replace(int offset, int length, string text, OffsetChangeMap offsetChangeMap) public void Replace(int offset, int length, string text, OffsetChangeMap offsetChangeMap)
{
Replace(offset, length, new StringTextSource(text), offsetChangeMap);
}
/// <summary>
/// Replaces text.
/// </summary>
/// <param name="offset">The starting offset of the text to be replaced.</param>
/// <param name="length">The length of the text to be replaced.</param>
/// <param name="text">The new text.</param>
/// <param name="offsetChangeMap">The offsetChangeMap determines how offsets inside the old text are mapped to the new text.
/// This affects how the anchors and segments inside the replaced region behave.
/// If you pass null (the default when using one of the other overloads), the offsets are changed as
/// in OffsetChangeMappingType.Normal mode.
/// If you pass OffsetChangeMap.Empty, then everything will stay in its old place (OffsetChangeMappingType.CharacterReplace mode).
/// The offsetChangeMap must be a valid 'explanation' for the document change. See <see cref="OffsetChangeMap.IsValidForDocumentChange"/>.
/// Passing an OffsetChangeMap to the Replace method will automatically freeze it to ensure the thread safety of the resulting
/// DocumentChangeEventArgs instance.
/// </param>
public void Replace(int offset, int length, ITextSource text, OffsetChangeMap offsetChangeMap)
{ {
if (text == null) if (text == null)
throw new ArgumentNullException("text"); throw new ArgumentNullException("text");
text = text.CreateSnapshot();
if (offsetChangeMap != null) if (offsetChangeMap != null)
offsetChangeMap.Freeze(); offsetChangeMap.Freeze();
@ -596,23 +812,33 @@ namespace ICSharpCode.AvalonEdit.Document
} }
} }
void DoReplace(int offset, int length, string newText, OffsetChangeMap offsetChangeMap) void DoReplace(int offset, int length, ITextSource newText, OffsetChangeMap offsetChangeMap)
{ {
if (length == 0 && newText.Length == 0) if (length == 0 && newText.TextLength == 0)
return; return;
// trying to replace a single character in 'Normal' mode? // trying to replace a single character in 'Normal' mode?
// for single characters, 'CharacterReplace' mode is equivalent, but more performant // for single characters, 'CharacterReplace' mode is equivalent, but more performant
// (we don't have to touch the anchorTree at all in 'CharacterReplace' mode) // (we don't have to touch the anchorTree at all in 'CharacterReplace' mode)
if (length == 1 && newText.Length == 1 && offsetChangeMap == null) if (length == 1 && newText.TextLength == 1 && offsetChangeMap == null)
offsetChangeMap = OffsetChangeMap.Empty; offsetChangeMap = OffsetChangeMap.Empty;
string removedText = rope.ToString(offset, length); ITextSource removedText;
if (length == 0) {
removedText = StringTextSource.Empty;
} else if (length < 100) {
removedText = new StringTextSource(rope.ToString(offset, length));
} else {
// use a rope if the removed string is long
removedText = new RopeTextSource(rope.GetRange(offset, length));
}
DocumentChangeEventArgs args = new DocumentChangeEventArgs(offset, removedText, newText, offsetChangeMap); DocumentChangeEventArgs args = new DocumentChangeEventArgs(offset, removedText, newText, offsetChangeMap);
// fire DocumentChanging event // fire DocumentChanging event
if (Changing != null) if (Changing != null)
Changing(this, args); Changing(this, args);
if (textChanging != null)
textChanging(this, args);
undoStack.Push(this, args); undoStack.Push(this, args);
@ -621,16 +847,18 @@ namespace ICSharpCode.AvalonEdit.Document
DelayedEvents delayedEvents = new DelayedEvents(); DelayedEvents delayedEvents = new DelayedEvents();
lock (lockObject) { lock (lockObject) {
// create linked list of checkpoints, if required // create linked list of checkpoints
if (currentCheckpoint != null) { versionProvider.AppendChange(args);
currentCheckpoint = currentCheckpoint.Append(args);
}
// now update the textBuffer and lineTree // now update the textBuffer and lineTree
if (offset == 0 && length == rope.Length) { if (offset == 0 && length == rope.Length) {
// optimize replacing the whole document // optimize replacing the whole document
rope.Clear(); rope.Clear();
rope.InsertText(0, newText); var newRopeTextSource = newText as RopeTextSource;
if (newRopeTextSource != null)
rope.InsertRange(0, newRopeTextSource.GetRope());
else
rope.InsertText(0, newText.Text);
lineManager.Rebuild(); lineManager.Rebuild();
} else { } else {
rope.RemoveRange(offset, length); rope.RemoveRange(offset, length);
@ -638,7 +866,11 @@ namespace ICSharpCode.AvalonEdit.Document
#if DEBUG #if DEBUG
lineTree.CheckProperties(); lineTree.CheckProperties();
#endif #endif
rope.InsertText(offset, newText); var newRopeTextSource = newText as RopeTextSource;
if (newRopeTextSource != null)
rope.InsertRange(offset, newRopeTextSource.GetRope());
else
rope.InsertText(offset, newText.Text);
lineManager.Insert(offset, newText); lineManager.Insert(offset, newText);
#if DEBUG #if DEBUG
lineTree.CheckProperties(); lineTree.CheckProperties();
@ -655,12 +887,16 @@ namespace ICSharpCode.AvalonEdit.Document
} }
} }
lineManager.ChangeComplete(args);
// raise delayed events after our data structures are consistent again // raise delayed events after our data structures are consistent again
delayedEvents.RaiseEvents(); delayedEvents.RaiseEvents();
// fire DocumentChanged event // fire DocumentChanged event
if (Changed != null) if (Changed != null)
Changed(this, args); Changed(this, args);
if (textChanged != null)
textChanged(this, args);
} }
#endregion #endregion
@ -684,6 +920,11 @@ namespace ICSharpCode.AvalonEdit.Document
return lineTree.GetByNumber(number); return lineTree.GetByNumber(number);
} }
IDocumentLine IDocument.GetLineByNumber(int lineNumber)
{
return GetLineByNumber(lineNumber);
}
/// <summary> /// <summary>
/// Gets a document lines by offset. /// Gets a document lines by offset.
/// Runtime: O(log n) /// Runtime: O(log n)
@ -697,8 +938,14 @@ namespace ICSharpCode.AvalonEdit.Document
} }
return lineTree.GetByOffset(offset); return lineTree.GetByOffset(offset);
} }
IDocumentLine IDocument.GetLineByOffset(int offset)
{
return GetLineByOffset(offset);
}
#endregion #endregion
#region GetOffset / GetLocation
/// <summary> /// <summary>
/// Gets the offset from a text location. /// Gets the offset from a text location.
/// </summary> /// </summary>
@ -731,7 +978,9 @@ namespace ICSharpCode.AvalonEdit.Document
DocumentLine line = GetLineByOffset(offset); DocumentLine line = GetLineByOffset(offset);
return new TextLocation(line.LineNumber, offset - line.Offset + 1); return new TextLocation(line.LineNumber, offset - line.Offset + 1);
} }
#endregion
#region Line Trackers
readonly ObservableCollection<ILineTracker> lineTrackers = new ObservableCollection<ILineTracker>(); readonly ObservableCollection<ILineTracker> lineTrackers = new ObservableCollection<ILineTracker>();
/// <summary> /// <summary>
@ -744,7 +993,9 @@ namespace ICSharpCode.AvalonEdit.Document
return lineTrackers; return lineTrackers;
} }
} }
#endregion
#region UndoStack
UndoStack undoStack; UndoStack undoStack;
/// <summary> /// <summary>
@ -764,7 +1015,9 @@ namespace ICSharpCode.AvalonEdit.Document
} }
} }
} }
#endregion
#region CreateAnchor
/// <summary> /// <summary>
/// Creates a new <see cref="TextAnchor"/> at the specified offset. /// Creates a new <see cref="TextAnchor"/> at the specified offset.
/// </summary> /// </summary>
@ -778,6 +1031,12 @@ namespace ICSharpCode.AvalonEdit.Document
return anchorTree.CreateAnchor(offset); return anchorTree.CreateAnchor(offset);
} }
ITextAnchor IDocument.CreateAnchor(int offset)
{
return CreateAnchor(offset);
}
#endregion
#region LineCount #region LineCount
/// <summary> /// <summary>
/// Gets the total number of lines in the document. /// Gets the total number of lines in the document.
@ -832,5 +1091,63 @@ namespace ICSharpCode.AvalonEdit.Document
#endif #endif
} }
#endregion #endregion
#region Service Provider
IServiceProvider serviceProvider;
/// <summary>
/// Gets/Sets the service provider associated with this document.
/// By default, every TextDocument has its own ServiceContainer; and has the document itself
/// registered as <see cref="IDocument"/> and <see cref="TextDocument"/>.
/// </summary>
public IServiceProvider ServiceProvider {
get {
VerifyAccess();
if (serviceProvider == null) {
var container = new ServiceContainer();
container.AddService(typeof(IDocument), this);
container.AddService(typeof(TextDocument), this);
serviceProvider = container;
}
return serviceProvider;
}
set {
VerifyAccess();
if (value == null)
throw new ArgumentNullException();
serviceProvider = value;
}
}
object IServiceProvider.GetService(Type serviceType)
{
return this.ServiceProvider.GetService(serviceType);
}
#endregion
#region FileName
string fileName;
/// <inheritdoc/>
public event EventHandler FileNameChanged;
void OnFileNameChanged(EventArgs e)
{
EventHandler handler = this.FileNameChanged;
if (handler != null)
handler(this, e);
}
/// <inheritdoc/>
public string FileName {
get { return fileName; }
set {
if (fileName != value) {
fileName = value;
OnFileNameChanged(EventArgs.Empty);
}
}
}
#endregion
} }
} }

22
AvalonEdit/ICSharpCode.AvalonEdit/Document/TextDocumentWeakEventManager.cs

@ -1,7 +1,25 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
#if NREFACTORY
using ICSharpCode.NRefactory.Editor;
#endif
using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.AvalonEdit.Utils;
namespace ICSharpCode.AvalonEdit.Document namespace ICSharpCode.AvalonEdit.Document

153
AvalonEdit/ICSharpCode.AvalonEdit/Document/TextLocation.cs

@ -1,19 +1,38 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.ComponentModel;
using System.Globalization; using System.Globalization;
namespace ICSharpCode.AvalonEdit.Document namespace ICSharpCode.AvalonEdit.Document
{ {
#if !NREFACTORY
/// <summary> /// <summary>
/// A line/column position. /// A line/column position.
/// Text editor lines/columns are counted started from one. /// Text editor lines/columns are counted started from one.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// The document provides the methods <see cref="TextDocument.GetLocation"/> and /// The document provides the methods <see cref="IDocument.GetLocation"/> and
/// <see cref="TextDocument.GetOffset(TextLocation)"/> to convert between offsets and TextLocations. /// <see cref="IDocument.GetOffset(TextLocation)"/> to convert between offsets and TextLocations.
/// </remarks> /// </remarks>
[Serializable]
[TypeConverter(typeof(TextLocationConverter))]
public struct TextLocation : IComparable<TextLocation>, IEquatable<TextLocation> public struct TextLocation : IComparable<TextLocation>, IEquatable<TextLocation>
{ {
/// <summary> /// <summary>
@ -23,31 +42,27 @@ namespace ICSharpCode.AvalonEdit.Document
/// <summary> /// <summary>
/// Creates a TextLocation instance. /// Creates a TextLocation instance.
/// <para>
/// Warning: the parameters are (line, column).
/// Not (column, line) as in ICSharpCode.TextEditor!
/// </para>
/// </summary> /// </summary>
public TextLocation(int line, int column) public TextLocation(int line, int column)
{ {
y = line; this.line = line;
x = column; this.column = column;
} }
int x, y; readonly int column, line;
/// <summary> /// <summary>
/// Gets the line number. /// Gets the line number.
/// </summary> /// </summary>
public int Line { public int Line {
get { return y; } get { return line; }
} }
/// <summary> /// <summary>
/// Gets the column number. /// Gets the column number.
/// </summary> /// </summary>
public int Column { public int Column {
get { return x; } get { return column; }
} }
/// <summary> /// <summary>
@ -55,7 +70,7 @@ namespace ICSharpCode.AvalonEdit.Document
/// </summary> /// </summary>
public bool IsEmpty { public bool IsEmpty {
get { get {
return x <= 0 && y <= 0; return column <= 0 && line <= 0;
} }
} }
@ -64,7 +79,7 @@ namespace ICSharpCode.AvalonEdit.Document
/// </summary> /// </summary>
public override string ToString() public override string ToString()
{ {
return string.Format(CultureInfo.InvariantCulture, "(Line {1}, Col {0})", this.x, this.y); return string.Format(CultureInfo.InvariantCulture, "(Line {1}, Col {0})", this.column, this.line);
} }
/// <summary> /// <summary>
@ -72,7 +87,7 @@ namespace ICSharpCode.AvalonEdit.Document
/// </summary> /// </summary>
public override int GetHashCode() public override int GetHashCode()
{ {
return unchecked (87 * x.GetHashCode() ^ y.GetHashCode()); return unchecked (191 * column.GetHashCode() ^ line.GetHashCode());
} }
/// <summary> /// <summary>
@ -97,7 +112,7 @@ namespace ICSharpCode.AvalonEdit.Document
/// </summary> /// </summary>
public static bool operator ==(TextLocation left, TextLocation right) public static bool operator ==(TextLocation left, TextLocation right)
{ {
return left.x == right.x && left.y == right.y; return left.column == right.column && left.line == right.line;
} }
/// <summary> /// <summary>
@ -105,7 +120,7 @@ namespace ICSharpCode.AvalonEdit.Document
/// </summary> /// </summary>
public static bool operator !=(TextLocation left, TextLocation right) public static bool operator !=(TextLocation left, TextLocation right)
{ {
return left.x != right.x || left.y != right.y; return left.column != right.column || left.line != right.line;
} }
/// <summary> /// <summary>
@ -113,10 +128,10 @@ namespace ICSharpCode.AvalonEdit.Document
/// </summary> /// </summary>
public static bool operator <(TextLocation left, TextLocation right) public static bool operator <(TextLocation left, TextLocation right)
{ {
if (left.y < right.y) if (left.line < right.line)
return true; return true;
else if (left.y == right.y) else if (left.line == right.line)
return left.x < right.x; return left.column < right.column;
else else
return false; return false;
} }
@ -126,10 +141,10 @@ namespace ICSharpCode.AvalonEdit.Document
/// </summary> /// </summary>
public static bool operator >(TextLocation left, TextLocation right) public static bool operator >(TextLocation left, TextLocation right)
{ {
if (left.y > right.y) if (left.line > right.line)
return true; return true;
else if (left.y == right.y) else if (left.line == right.line)
return left.x > right.x; return left.column > right.column;
else else
return false; return false;
} }
@ -163,4 +178,94 @@ namespace ICSharpCode.AvalonEdit.Document
return 1; return 1;
} }
} }
/// <summary>
/// Converts strings of the form '0+[;,]0+' to a <see cref="TextLocation"/>.
/// </summary>
public class TextLocationConverter : TypeConverter
{
/// <inheritdoc/>
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
}
/// <inheritdoc/>
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return destinationType == typeof(TextLocation) || base.CanConvertTo(context, destinationType);
}
/// <inheritdoc/>
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string) {
string[] parts = ((string)value).Split(';', ',');
if (parts.Length == 2) {
return new TextLocation(int.Parse(parts[0], culture), int.Parse(parts[1], culture));
}
}
return base.ConvertFrom(context, culture, value);
}
/// <inheritdoc/>
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (value is TextLocation && destinationType == typeof(string)) {
var loc = (TextLocation)value;
return loc.Line.ToString(culture) + ";" + loc.Column.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
/// <summary>
/// An (Offset,Length)-pair.
/// </summary>
public interface ISegment
{
/// <summary>
/// Gets the start offset of the segment.
/// </summary>
int Offset { get; }
/// <summary>
/// Gets the length of the segment.
/// </summary>
/// <remarks>For line segments (IDocumentLine), the length does not include the line delimeter.</remarks>
int Length { get; }
/// <summary>
/// Gets the end offset of the segment.
/// </summary>
/// <remarks>EndOffset = Offset + Length;</remarks>
int EndOffset { get; }
}
/// <summary>
/// Extension methods for <see cref="ISegment"/>.
/// </summary>
public static class ISegmentExtensions
{
/// <summary>
/// Gets whether <paramref name="segment"/> fully contains the specified segment.
/// </summary>
/// <remarks>
/// Use <c>segment.Contains(offset, 0)</c> to detect whether a segment (end inclusive) contains offset;
/// use <c>segment.Contains(offset, 1)</c> to detect whether a segment (end exclusive) contains offset.
/// </remarks>
public static bool Contains (this ISegment segment, int offset, int length)
{
return segment.Offset <= offset && offset + length <= segment.EndOffset;
}
/// <summary>
/// Gets whether <paramref name="thisSegment"/> fully contains the specified segment.
/// </summary>
public static bool Contains (this ISegment thisSegment, ISegment segment)
{
return segment != null && thisSegment.Offset <= segment.Offset && segment.EndOffset <= thisSegment.EndOffset;
}
}
#endif
} }

40
AvalonEdit/ICSharpCode.AvalonEdit/Document/TextSegment.cs

@ -1,8 +1,26 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Diagnostics; using System.Diagnostics;
#if NREFACTORY
using ICSharpCode.NRefactory.Editor;
#endif
namespace ICSharpCode.AvalonEdit.Document namespace ICSharpCode.AvalonEdit.Document
{ {
@ -120,6 +138,7 @@ namespace ICSharpCode.AvalonEdit.Document
} else { } else {
nodeLength = value; nodeLength = value;
} }
OnSegmentChanged();
} }
} }
} }
@ -155,12 +174,23 @@ namespace ICSharpCode.AvalonEdit.Document
set { set {
if (value < 0) if (value < 0)
throw new ArgumentOutOfRangeException("value", "Length must not be negative"); throw new ArgumentOutOfRangeException("value", "Length must not be negative");
segmentLength = value; if (segmentLength != value) {
if (ownerTree != null) segmentLength = value;
ownerTree.UpdateAugmentedData(this); if (ownerTree != null)
ownerTree.UpdateAugmentedData(this);
OnSegmentChanged();
}
} }
} }
/// <summary>
/// This method gets called when the StartOffset/Length/EndOffset properties are set.
/// It is not called when StartOffset/Length/EndOffset change due to document changes
/// </summary>
protected virtual void OnSegmentChanged()
{
}
internal TextSegment LeftMost { internal TextSegment LeftMost {
get { get {
TextSegment node = this; TextSegment node = this;

24
AvalonEdit/ICSharpCode.AvalonEdit/Document/TextSegmentCollection.cs

@ -1,7 +1,21 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using ICSharpCode.AvalonEdit.Utils;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
@ -9,6 +23,10 @@ using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Windows; using System.Windows;
using ICSharpCode.AvalonEdit.Utils;
#if NREFACTORY
using ICSharpCode.NRefactory.Editor;
#endif
namespace ICSharpCode.AvalonEdit.Document namespace ICSharpCode.AvalonEdit.Document
{ {

136
AvalonEdit/ICSharpCode.AvalonEdit/Document/TextSourceVersionProvider.cs

@ -0,0 +1,136 @@
// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.AvalonEdit.Utils;
namespace ICSharpCode.AvalonEdit.Document
{
#if !NREFACTORY
/// <summary>
/// Provides ITextSourceVersion instances.
/// </summary>
public class TextSourceVersionProvider
{
Version currentVersion;
/// <summary>
/// Creates a new TextSourceVersionProvider instance.
/// </summary>
public TextSourceVersionProvider()
{
this.currentVersion = new Version(this);
}
/// <summary>
/// Gets the current version.
/// </summary>
public ITextSourceVersion CurrentVersion {
get { return currentVersion; }
}
/// <summary>
/// Replaces the current version with a new version.
/// </summary>
/// <param name="change">Change from current version to new version</param>
public void AppendChange(TextChangeEventArgs change)
{
if (change == null)
throw new ArgumentNullException("change");
currentVersion.change = change;
currentVersion.next = new Version(currentVersion);
currentVersion = currentVersion.next;
}
[DebuggerDisplay("Version #{id}")]
sealed class Version : ITextSourceVersion
{
// Reference back to the provider.
// Used to determine if two checkpoints belong to the same document.
readonly TextSourceVersionProvider provider;
// ID used for CompareAge()
readonly int id;
// the change from this version to the next version
internal TextChangeEventArgs change;
internal Version next;
internal Version(TextSourceVersionProvider provider)
{
this.provider = provider;
}
internal Version(Version prev)
{
this.provider = prev.provider;
this.id = unchecked( prev.id + 1 );
}
public bool BelongsToSameDocumentAs(ITextSourceVersion other)
{
Version o = other as Version;
return o != null && provider == o.provider;
}
public int CompareAge(ITextSourceVersion other)
{
if (other == null)
throw new ArgumentNullException("other");
Version o = other as Version;
if (o == null || provider != o.provider)
throw new ArgumentException("Versions do not belong to the same document.");
// We will allow overflows, but assume that the maximum distance between checkpoints is 2^31-1.
// This is guaranteed on x86 because so many checkpoints don't fit into memory.
return Math.Sign(unchecked( this.id - o.id ));
}
public IEnumerable<TextChangeEventArgs> GetChangesTo(ITextSourceVersion other)
{
int result = CompareAge(other);
Version o = (Version)other;
if (result < 0)
return GetForwardChanges(o);
else if (result > 0)
return o.GetForwardChanges(this).Reverse().Select(change => change.Invert());
else
return Empty<TextChangeEventArgs>.Array;
}
IEnumerable<TextChangeEventArgs> GetForwardChanges(Version other)
{
// Return changes from this(inclusive) to other(exclusive).
for (Version node = this; node != other; node = node.next) {
yield return node.change;
}
}
public int MoveOffsetTo(ITextSourceVersion other, int oldOffset, AnchorMovementType movement)
{
int offset = oldOffset;
foreach (var e in GetChangesTo(other)) {
offset = e.GetNewOffset(offset, movement);
}
return offset;
}
}
}
#endif
}

170
AvalonEdit/ICSharpCode.AvalonEdit/Document/TextUtilities.cs

@ -1,9 +1,27 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Globalization; using System.Globalization;
using System.Windows.Documents; using System.Windows.Documents;
#if NREFACTORY
using ICSharpCode.NRefactory.Editor;
#endif
namespace ICSharpCode.AvalonEdit.Document namespace ICSharpCode.AvalonEdit.Document
{ {
@ -13,7 +31,7 @@ namespace ICSharpCode.AvalonEdit.Document
public enum CaretPositioningMode public enum CaretPositioningMode
{ {
/// <summary> /// <summary>
/// Normal positioning (stop at every caret position) /// Normal positioning (stop after every grapheme)
/// </summary> /// </summary>
Normal, Normal,
/// <summary> /// <summary>
@ -31,7 +49,12 @@ namespace ICSharpCode.AvalonEdit.Document
/// <summary> /// <summary>
/// Stop only on word borders, and anywhere in the middle of symbols. /// Stop only on word borders, and anywhere in the middle of symbols.
/// </summary> /// </summary>
WordBorderOrSymbol WordBorderOrSymbol,
/// <summary>
/// Stop between every Unicode codepoint, even within the same grapheme.
/// This is used to implement deleting the previous grapheme when Backspace is pressed.
/// </summary>
EveryCodepoint
} }
/// <summary> /// <summary>
@ -198,12 +221,43 @@ namespace ICSharpCode.AvalonEdit.Document
{ {
if (c == '\r' || c == '\n') if (c == '\r' || c == '\n')
return CharacterClass.LineTerminator; return CharacterClass.LineTerminator;
else if (char.IsWhiteSpace(c)) if (c == '_')
return CharacterClass.Whitespace;
else if (char.IsLetterOrDigit(c) || c == '_')
return CharacterClass.IdentifierPart; return CharacterClass.IdentifierPart;
else return GetCharacterClass(char.GetUnicodeCategory(c));
}
static CharacterClass GetCharacterClass(char highSurrogate, char lowSurrogate)
{
if (char.IsSurrogatePair(highSurrogate, lowSurrogate)) {
return GetCharacterClass(char.GetUnicodeCategory(highSurrogate.ToString() + lowSurrogate.ToString(), 0));
} else {
// malformed surrogate pair
return CharacterClass.Other; return CharacterClass.Other;
}
}
static CharacterClass GetCharacterClass(UnicodeCategory c)
{
switch (c) {
case UnicodeCategory.SpaceSeparator:
case UnicodeCategory.LineSeparator:
case UnicodeCategory.ParagraphSeparator:
case UnicodeCategory.Control:
return CharacterClass.Whitespace;
case UnicodeCategory.UppercaseLetter:
case UnicodeCategory.LowercaseLetter:
case UnicodeCategory.TitlecaseLetter:
case UnicodeCategory.ModifierLetter:
case UnicodeCategory.OtherLetter:
case UnicodeCategory.DecimalDigitNumber:
return CharacterClass.IdentifierPart;
case UnicodeCategory.NonSpacingMark:
case UnicodeCategory.SpacingCombiningMark:
case UnicodeCategory.EnclosingMark:
return CharacterClass.CombiningMark;
default:
return CharacterClass.Other;
}
} }
#endregion #endregion
@ -226,13 +280,16 @@ namespace ICSharpCode.AvalonEdit.Document
{ {
if (textSource == null) if (textSource == null)
throw new ArgumentNullException("textSource"); throw new ArgumentNullException("textSource");
if (mode != CaretPositioningMode.Normal switch (mode) {
&& mode != CaretPositioningMode.WordBorder case CaretPositioningMode.Normal:
&& mode != CaretPositioningMode.WordStart case CaretPositioningMode.EveryCodepoint:
&& mode != CaretPositioningMode.WordBorderOrSymbol case CaretPositioningMode.WordBorder:
&& mode != CaretPositioningMode.WordStartOrSymbol) case CaretPositioningMode.WordBorderOrSymbol:
{ case CaretPositioningMode.WordStart:
throw new ArgumentException("Unsupported CaretPositioningMode: " + mode, "mode"); case CaretPositioningMode.WordStartOrSymbol:
break; // OK
default:
throw new ArgumentException("Unsupported CaretPositioningMode: " + mode, "mode");
} }
if (direction != LogicalDirection.Backward if (direction != LogicalDirection.Backward
&& direction != LogicalDirection.Forward) && direction != LogicalDirection.Forward)
@ -242,7 +299,7 @@ namespace ICSharpCode.AvalonEdit.Document
int textLength = textSource.TextLength; int textLength = textSource.TextLength;
if (textLength <= 0) { if (textLength <= 0) {
// empty document? has a normal caret position at 0, though no word borders // empty document? has a normal caret position at 0, though no word borders
if (mode == CaretPositioningMode.Normal) { if (IsNormal(mode)) {
if (offset > 0 && direction == LogicalDirection.Backward) return 0; if (offset > 0 && direction == LogicalDirection.Backward) return 0;
if (offset < 0 && direction == LogicalDirection.Forward) return 0; if (offset < 0 && direction == LogicalDirection.Forward) return 0;
} }
@ -256,44 +313,36 @@ namespace ICSharpCode.AvalonEdit.Document
if (nextPos < 0 || nextPos > textLength) if (nextPos < 0 || nextPos > textLength)
return -1; return -1;
// stop at every caret position? we can stop immediately.
if (mode == CaretPositioningMode.Normal)
return nextPos;
// not normal mode? we're looking for word borders...
// check if we've run against the textSource borders. // check if we've run against the textSource borders.
// a 'textSource' usually isn't the whole document, but a single VisualLineElement. // a 'textSource' usually isn't the whole document, but a single VisualLineElement.
if (nextPos == 0) { if (nextPos == 0) {
// at the document start, there's only a word border // at the document start, there's only a word border
// if the first character is not whitespace // if the first character is not whitespace
if (!char.IsWhiteSpace(textSource.GetCharAt(0))) if (IsNormal(mode) || !char.IsWhiteSpace(textSource.GetCharAt(0)))
return nextPos; return nextPos;
} else if (nextPos == textLength) { } else if (nextPos == textLength) {
// at the document end, there's never a word start // at the document end, there's never a word start
if (mode != CaretPositioningMode.WordStart && mode != CaretPositioningMode.WordStartOrSymbol) { if (mode != CaretPositioningMode.WordStart && mode != CaretPositioningMode.WordStartOrSymbol) {
// at the document end, there's only a word border // at the document end, there's only a word border
// if the last character is not whitespace // if the last character is not whitespace
if (!char.IsWhiteSpace(textSource.GetCharAt(textLength - 1))) if (IsNormal(mode) || !char.IsWhiteSpace(textSource.GetCharAt(textLength - 1)))
return nextPos; return nextPos;
} }
} else { } else {
CharacterClass charBefore = GetCharacterClass(textSource.GetCharAt(nextPos - 1)); char charBefore = textSource.GetCharAt(nextPos - 1);
CharacterClass charAfter = GetCharacterClass(textSource.GetCharAt(nextPos)); char charAfter = textSource.GetCharAt(nextPos);
if (charBefore == charAfter) { // Don't stop in the middle of a surrogate pair
if (charBefore == CharacterClass.Other && if (!char.IsSurrogatePair(charBefore, charAfter)) {
(mode == CaretPositioningMode.WordBorderOrSymbol || mode == CaretPositioningMode.WordStartOrSymbol)) CharacterClass classBefore = GetCharacterClass(charBefore);
{ CharacterClass classAfter = GetCharacterClass(charAfter);
// With the "OrSymbol" modes, there's a word border and start between any two unknown characters // get correct class for characters outside BMP:
return nextPos; if (char.IsLowSurrogate(charBefore) && nextPos >= 2) {
classBefore = GetCharacterClass(textSource.GetCharAt(nextPos - 2), charBefore);
} }
} else { if (char.IsHighSurrogate(charAfter) && nextPos + 1 < textLength) {
// this looks like a possible border classAfter = GetCharacterClass(charAfter, textSource.GetCharAt(nextPos + 1));
}
// if we're looking for word starts, check that this is a word start (and not a word end) if (StopBetweenCharacters(mode, classBefore, classAfter)) {
// if we're just checking for word borders, accept unconditionally
if (!((mode == CaretPositioningMode.WordStart || mode == CaretPositioningMode.WordStartOrSymbol)
&& (charAfter == CharacterClass.Whitespace || charAfter == CharacterClass.LineTerminator)))
{
return nextPos; return nextPos;
} }
} }
@ -302,6 +351,42 @@ namespace ICSharpCode.AvalonEdit.Document
offset = nextPos; offset = nextPos;
} }
} }
static bool IsNormal(CaretPositioningMode mode)
{
return mode == CaretPositioningMode.Normal || mode == CaretPositioningMode.EveryCodepoint;
}
static bool StopBetweenCharacters(CaretPositioningMode mode, CharacterClass charBefore, CharacterClass charAfter)
{
if (mode == CaretPositioningMode.EveryCodepoint)
return true;
// Don't stop in the middle of a grapheme
if (charAfter == CharacterClass.CombiningMark)
return false;
// Stop after every grapheme in normal mode
if (mode == CaretPositioningMode.Normal)
return true;
if (charBefore == charAfter) {
if (charBefore == CharacterClass.Other &&
(mode == CaretPositioningMode.WordBorderOrSymbol || mode == CaretPositioningMode.WordStartOrSymbol))
{
// With the "OrSymbol" modes, there's a word border and start between any two unknown characters
return true;
}
} else {
// this looks like a possible border
// if we're looking for word starts, check that this is a word start (and not a word end)
// if we're just checking for word borders, accept unconditionally
if (!((mode == CaretPositioningMode.WordStart || mode == CaretPositioningMode.WordStartOrSymbol)
&& (charAfter == CharacterClass.Whitespace || charAfter == CharacterClass.LineTerminator)))
{
return true;
}
}
return false;
}
#endregion #endregion
} }
@ -327,6 +412,11 @@ namespace ICSharpCode.AvalonEdit.Document
/// <summary> /// <summary>
/// The character is line terminator (\r or \n). /// The character is line terminator (\r or \n).
/// </summary> /// </summary>
LineTerminator LineTerminator,
/// <summary>
/// The character is a unicode combining mark that modifies the previous character.
/// Corresponds to the Unicode designations "Mn", "Mc" and "Me".
/// </summary>
CombiningMark
} }
} }

19
AvalonEdit/ICSharpCode.AvalonEdit/Document/UndoOperationGroup.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Diagnostics; using System.Diagnostics;

24
AvalonEdit/ICSharpCode.AvalonEdit/Document/UndoStack.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -193,7 +208,7 @@ namespace ICSharpCode.AvalonEdit.Document
/// </summary> /// </summary>
/// <param name="groupDescriptor">An object that is stored with the undo group. /// <param name="groupDescriptor">An object that is stored with the undo group.
/// If this is not a top-level undo group, the parameter is ignored.</param> /// If this is not a top-level undo group, the parameter is ignored.</param>
public void StartContinuedUndoGroup(object groupDescriptor) public void StartContinuedUndoGroup(object groupDescriptor = null)
{ {
if (undoGroupDepth == 0) { if (undoGroupDepth == 0) {
actionCountInUndoGroup = (allowContinue && undostack.Count > 0) ? 1 : 0; actionCountInUndoGroup = (allowContinue && undostack.Count > 0) ? 1 : 0;
@ -214,11 +229,13 @@ namespace ICSharpCode.AvalonEdit.Document
//Util.LoggingService.Debug("Close undo group (new depth=" + undoGroupDepth + ")"); //Util.LoggingService.Debug("Close undo group (new depth=" + undoGroupDepth + ")");
if (undoGroupDepth == 0) { if (undoGroupDepth == 0) {
Debug.Assert(state == StateListen || actionCountInUndoGroup == 0); Debug.Assert(state == StateListen || actionCountInUndoGroup == 0);
allowContinue = true;
if (actionCountInUndoGroup == optionalActionCount) { if (actionCountInUndoGroup == optionalActionCount) {
// only optional actions: don't store them // only optional actions: don't store them
for (int i = 0; i < optionalActionCount; i++) { for (int i = 0; i < optionalActionCount; i++) {
undostack.PopBack(); undostack.PopBack();
} }
allowContinue = false;
} else if (actionCountInUndoGroup > 1) { } else if (actionCountInUndoGroup > 1) {
// combine all actions within the group into a single grouped action // combine all actions within the group into a single grouped action
undostack.PushBack(new UndoOperationGroup(undostack, actionCountInUndoGroup)); undostack.PushBack(new UndoOperationGroup(undostack, actionCountInUndoGroup));
@ -226,7 +243,6 @@ namespace ICSharpCode.AvalonEdit.Document
} }
//if (state == StateListen) { //if (state == StateListen) {
EnforceSizeLimit(); EnforceSizeLimit();
allowContinue = true;
RecalcIsOriginalFile(); // can raise event RecalcIsOriginalFile(); // can raise event
//} //}
} }

28
AvalonEdit/ICSharpCode.AvalonEdit/Document/WeakLineTracker.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
@ -81,5 +96,14 @@ namespace ICSharpCode.AvalonEdit.Document
else else
Deregister(); Deregister();
} }
void ILineTracker.ChangeComplete(DocumentChangeEventArgs e)
{
ILineTracker targetTracker = targetObject.Target as ILineTracker;
if (targetTracker != null)
targetTracker.ChangeComplete(e);
else
Deregister();
}
} }
} }

19
AvalonEdit/ICSharpCode.AvalonEdit/Editing/AbstractMargin.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Diagnostics; using System.Diagnostics;

83
AvalonEdit/ICSharpCode.AvalonEdit/Editing/Caret.cs

@ -1,17 +1,36 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Documents; using System.Windows.Documents;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.TextFormatting; using System.Windows.Media.TextFormatting;
using System.Windows.Threading; using System.Windows.Threading;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.AvalonEdit.Utils;
#if NREFACTORY
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Editor;
#endif
namespace ICSharpCode.AvalonEdit.Editing namespace ICSharpCode.AvalonEdit.Editing
{ {
@ -31,12 +50,19 @@ namespace ICSharpCode.AvalonEdit.Editing
this.textView = textArea.TextView; this.textView = textArea.TextView;
position = new TextViewPosition(1, 1, 0); position = new TextViewPosition(1, 1, 0);
caretAdorner = new CaretLayer(textView); caretAdorner = new CaretLayer(textArea);
textView.InsertLayer(caretAdorner, KnownLayer.Caret, LayerInsertionPosition.Replace); textView.InsertLayer(caretAdorner, KnownLayer.Caret, LayerInsertionPosition.Replace);
textView.VisualLinesChanged += TextView_VisualLinesChanged; textView.VisualLinesChanged += TextView_VisualLinesChanged;
textView.ScrollOffsetChanged += TextView_ScrollOffsetChanged; textView.ScrollOffsetChanged += TextView_ScrollOffsetChanged;
} }
internal void UpdateIfVisible()
{
if (visible) {
Show();
}
}
void TextView_VisualLinesChanged(object sender, EventArgs e) void TextView_VisualLinesChanged(object sender, EventArgs e)
{ {
if (visible) { if (visible) {
@ -166,7 +192,14 @@ namespace ICSharpCode.AvalonEdit.Editing
{ {
InvalidateVisualColumn(); InvalidateVisualColumn();
if (storedCaretOffset >= 0) { if (storedCaretOffset >= 0) {
int newCaretOffset = e.GetNewOffset(storedCaretOffset, AnchorMovementType.Default); // If the caret is at the end of a selection, we don't expand the selection if something
// is inserted at the end. Thus we also need to keep the caret in front of the insertion.
AnchorMovementType caretMovementType;
if (!textArea.Selection.IsEmpty && storedCaretOffset == textArea.Selection.SurroundingSegment.EndOffset)
caretMovementType = AnchorMovementType.BeforeInsertion;
else
caretMovementType = AnchorMovementType.Default;
int newCaretOffset = e.GetNewOffset(storedCaretOffset, caretMovementType);
TextDocument document = textArea.Document; TextDocument document = textArea.Document;
if (document != null) { if (document != null) {
// keep visual column // keep visual column
@ -348,7 +381,7 @@ namespace ICSharpCode.AvalonEdit.Editing
RevalidateVisualColumn(visualLine); RevalidateVisualColumn(visualLine);
} }
TextLine textLine = visualLine.GetTextLine(position.VisualColumn); TextLine textLine = visualLine.GetTextLine(position.VisualColumn, position.IsAtEndOfLine);
double xPos = visualLine.GetTextLineVisualXPosition(textLine, position.VisualColumn); double xPos = visualLine.GetTextLineVisualXPosition(textLine, position.VisualColumn);
double lineTop = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextTop); double lineTop = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextTop);
double lineBottom = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextBottom); double lineBottom = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextBottom);
@ -359,6 +392,39 @@ namespace ICSharpCode.AvalonEdit.Editing
lineBottom - lineTop); lineBottom - lineTop);
} }
Rect CalcCaretOverstrikeRectangle(VisualLine visualLine)
{
if (!visualColumnValid) {
RevalidateVisualColumn(visualLine);
}
int currentPos = position.VisualColumn;
// The text being overwritten in overstrike mode is everything up to the next normal caret stop
int nextPos = visualLine.GetNextCaretPosition(currentPos, LogicalDirection.Forward, CaretPositioningMode.Normal, true);
TextLine textLine = visualLine.GetTextLine(currentPos);
Rect r;
if (currentPos < visualLine.VisualLength) {
// If the caret is within the text, use GetTextBounds() for the text being overwritten.
// This is necessary to ensure the rectangle is calculated correctly in bidirectional text.
var textBounds = textLine.GetTextBounds(currentPos, nextPos - currentPos)[0];
r = textBounds.Rectangle;
r.Y += visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.LineTop);
} else {
// If the caret is at the end of the line (or in virtual space),
// use the visual X position of currentPos and nextPos (one or more of which will be in virtual space)
double xPos = visualLine.GetTextLineVisualXPosition(textLine, currentPos);
double xPos2 = visualLine.GetTextLineVisualXPosition(textLine, nextPos);
double lineTop = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextTop);
double lineBottom = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextBottom);
r = new Rect(xPos, lineTop, xPos2 - xPos, lineBottom - lineTop);
}
// If the caret is too small (e.g. in front of zero-width character), ensure it's still visible
if (r.Width < SystemParameters.CaretWidth)
r.Width = SystemParameters.CaretWidth;
return r;
}
/// <summary> /// <summary>
/// Returns the caret rectangle. The coordinate system is in device-independent pixels from the top of the document. /// Returns the caret rectangle. The coordinate system is in device-independent pixels from the top of the document.
/// </summary> /// </summary>
@ -366,7 +432,7 @@ namespace ICSharpCode.AvalonEdit.Editing
{ {
if (textView != null && textView.Document != null) { if (textView != null && textView.Document != null) {
VisualLine visualLine = textView.GetOrConstructVisualLine(textView.Document.GetLineByNumber(position.Line)); VisualLine visualLine = textView.GetOrConstructVisualLine(textView.Document.GetLineByNumber(position.Line));
return CalcCaretRectangle(visualLine); return textArea.OverstrikeMode ? CalcCaretOverstrikeRectangle(visualLine) : CalcCaretRectangle(visualLine);
} else { } else {
return Rect.Empty; return Rect.Empty;
} }
@ -421,7 +487,7 @@ namespace ICSharpCode.AvalonEdit.Editing
if (caretAdorner != null && textView != null) { if (caretAdorner != null && textView != null) {
VisualLine visualLine = textView.GetVisualLine(position.Line); VisualLine visualLine = textView.GetVisualLine(position.Line);
if (visualLine != null) { if (visualLine != null) {
Rect caretRect = CalcCaretRectangle(visualLine); Rect caretRect = this.textArea.OverstrikeMode ? CalcCaretOverstrikeRectangle(visualLine) : CalcCaretRectangle(visualLine);
// Create Win32 caret so that Windows knows where our managed caret is. This is necessary for // Create Win32 caret so that Windows knows where our managed caret is. This is necessary for
// features like 'Follow text editing' in the Windows Magnifier. // features like 'Follow text editing' in the Windows Magnifier.
if (!hasWin32Caret) { if (!hasWin32Caret) {
@ -431,6 +497,7 @@ namespace ICSharpCode.AvalonEdit.Editing
Win32.SetCaretPosition(textView, caretRect.Location - textView.ScrollOffset); Win32.SetCaretPosition(textView, caretRect.Location - textView.ScrollOffset);
} }
caretAdorner.Show(caretRect); caretAdorner.Show(caretRect);
textArea.ime.UpdateCompositionWindow();
} else { } else {
caretAdorner.Hide(); caretAdorner.Hide();
} }

35
AvalonEdit/ICSharpCode.AvalonEdit/Editing/CaretLayer.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Windows; using System.Windows;
@ -15,14 +30,17 @@ namespace ICSharpCode.AvalonEdit.Editing
{ {
sealed class CaretLayer : Layer sealed class CaretLayer : Layer
{ {
TextArea textArea;
bool isVisible; bool isVisible;
Rect caretRectangle; Rect caretRectangle;
DispatcherTimer caretBlinkTimer = new DispatcherTimer(); DispatcherTimer caretBlinkTimer = new DispatcherTimer();
bool blink; bool blink;
public CaretLayer(TextView textView) : base(textView, KnownLayer.Caret) public CaretLayer(TextArea textArea) : base(textArea.TextView, KnownLayer.Caret)
{ {
this.textArea = textArea;
this.IsHitTestVisible = false; this.IsHitTestVisible = false;
caretBlinkTimer.Tick += new EventHandler(caretBlinkTimer_Tick); caretBlinkTimer.Tick += new EventHandler(caretBlinkTimer_Tick);
} }
@ -75,6 +93,17 @@ namespace ICSharpCode.AvalonEdit.Editing
Brush caretBrush = this.CaretBrush; Brush caretBrush = this.CaretBrush;
if (caretBrush == null) if (caretBrush == null)
caretBrush = (Brush)textView.GetValue(TextBlock.ForegroundProperty); caretBrush = (Brush)textView.GetValue(TextBlock.ForegroundProperty);
if (this.textArea.OverstrikeMode) {
SolidColorBrush scBrush = caretBrush as SolidColorBrush;
if (scBrush != null) {
Color brushColor = scBrush.Color;
Color newColor = Color.FromArgb(100, brushColor.R, brushColor.G, brushColor.B);
caretBrush = new SolidColorBrush(newColor);
caretBrush.Freeze();
}
}
Rect r = new Rect(caretRectangle.X - textView.HorizontalOffset, Rect r = new Rect(caretRectangle.X - textView.HorizontalOffset,
caretRectangle.Y - textView.VerticalOffset, caretRectangle.Y - textView.VerticalOffset,
caretRectangle.Width, caretRectangle.Width,

257
AvalonEdit/ICSharpCode.AvalonEdit/Editing/CaretNavigationCommandHandler.cs

@ -1,9 +1,25 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Documents; using System.Windows.Documents;
using System.Windows.Input; using System.Windows.Input;
@ -14,6 +30,24 @@ using ICSharpCode.AvalonEdit.Utils;
namespace ICSharpCode.AvalonEdit.Editing namespace ICSharpCode.AvalonEdit.Editing
{ {
enum CaretMovementType
{
None,
CharLeft,
CharRight,
Backspace,
WordLeft,
WordRight,
LineUp,
LineDown,
PageUp,
PageDown,
LineStart,
LineEnd,
DocumentStart,
DocumentEnd
}
static class CaretNavigationCommandHandler static class CaretNavigationCommandHandler
{ {
/// <summary> /// <summary>
@ -41,21 +75,28 @@ namespace ICSharpCode.AvalonEdit.Editing
const ModifierKeys None = ModifierKeys.None; const ModifierKeys None = ModifierKeys.None;
const ModifierKeys Ctrl = ModifierKeys.Control; const ModifierKeys Ctrl = ModifierKeys.Control;
const ModifierKeys Shift = ModifierKeys.Shift; const ModifierKeys Shift = ModifierKeys.Shift;
const ModifierKeys Alt = ModifierKeys.Alt;
AddBinding(EditingCommands.MoveLeftByCharacter, None, Key.Left, OnMoveCaret(CaretMovementType.CharLeft)); AddBinding(EditingCommands.MoveLeftByCharacter, None, Key.Left, OnMoveCaret(CaretMovementType.CharLeft));
AddBinding(EditingCommands.SelectLeftByCharacter, Shift, Key.Left, OnMoveCaretExtendSelection(CaretMovementType.CharLeft)); AddBinding(EditingCommands.SelectLeftByCharacter, Shift, Key.Left, OnMoveCaretExtendSelection(CaretMovementType.CharLeft));
AddBinding(RectangleSelection.BoxSelectLeftByCharacter, Alt | Shift, Key.Left, OnMoveCaretBoxSelection(CaretMovementType.CharLeft));
AddBinding(EditingCommands.MoveRightByCharacter, None, Key.Right, OnMoveCaret(CaretMovementType.CharRight)); AddBinding(EditingCommands.MoveRightByCharacter, None, Key.Right, OnMoveCaret(CaretMovementType.CharRight));
AddBinding(EditingCommands.SelectRightByCharacter, Shift, Key.Right, OnMoveCaretExtendSelection(CaretMovementType.CharRight)); AddBinding(EditingCommands.SelectRightByCharacter, Shift, Key.Right, OnMoveCaretExtendSelection(CaretMovementType.CharRight));
AddBinding(RectangleSelection.BoxSelectRightByCharacter, Alt | Shift, Key.Right, OnMoveCaretBoxSelection(CaretMovementType.CharRight));
AddBinding(EditingCommands.MoveLeftByWord, Ctrl, Key.Left, OnMoveCaret(CaretMovementType.WordLeft)); AddBinding(EditingCommands.MoveLeftByWord, Ctrl, Key.Left, OnMoveCaret(CaretMovementType.WordLeft));
AddBinding(EditingCommands.SelectLeftByWord, Ctrl | Shift, Key.Left, OnMoveCaretExtendSelection(CaretMovementType.WordLeft)); AddBinding(EditingCommands.SelectLeftByWord, Ctrl | Shift, Key.Left, OnMoveCaretExtendSelection(CaretMovementType.WordLeft));
AddBinding(RectangleSelection.BoxSelectLeftByWord, Ctrl | Alt | Shift, Key.Left, OnMoveCaretBoxSelection(CaretMovementType.WordLeft));
AddBinding(EditingCommands.MoveRightByWord, Ctrl, Key.Right, OnMoveCaret(CaretMovementType.WordRight)); AddBinding(EditingCommands.MoveRightByWord, Ctrl, Key.Right, OnMoveCaret(CaretMovementType.WordRight));
AddBinding(EditingCommands.SelectRightByWord, Ctrl | Shift, Key.Right, OnMoveCaretExtendSelection(CaretMovementType.WordRight)); AddBinding(EditingCommands.SelectRightByWord, Ctrl | Shift, Key.Right, OnMoveCaretExtendSelection(CaretMovementType.WordRight));
AddBinding(RectangleSelection.BoxSelectRightByWord, Ctrl | Alt | Shift, Key.Right, OnMoveCaretBoxSelection(CaretMovementType.WordRight));
AddBinding(EditingCommands.MoveUpByLine, None, Key.Up, OnMoveCaret(CaretMovementType.LineUp)); AddBinding(EditingCommands.MoveUpByLine, None, Key.Up, OnMoveCaret(CaretMovementType.LineUp));
AddBinding(EditingCommands.SelectUpByLine, Shift, Key.Up, OnMoveCaretExtendSelection(CaretMovementType.LineUp)); AddBinding(EditingCommands.SelectUpByLine, Shift, Key.Up, OnMoveCaretExtendSelection(CaretMovementType.LineUp));
AddBinding(RectangleSelection.BoxSelectUpByLine, Alt | Shift, Key.Up, OnMoveCaretBoxSelection(CaretMovementType.LineUp));
AddBinding(EditingCommands.MoveDownByLine, None, Key.Down, OnMoveCaret(CaretMovementType.LineDown)); AddBinding(EditingCommands.MoveDownByLine, None, Key.Down, OnMoveCaret(CaretMovementType.LineDown));
AddBinding(EditingCommands.SelectDownByLine, Shift, Key.Down, OnMoveCaretExtendSelection(CaretMovementType.LineDown)); AddBinding(EditingCommands.SelectDownByLine, Shift, Key.Down, OnMoveCaretExtendSelection(CaretMovementType.LineDown));
AddBinding(RectangleSelection.BoxSelectDownByLine, Alt | Shift, Key.Down, OnMoveCaretBoxSelection(CaretMovementType.LineDown));
AddBinding(EditingCommands.MoveDownByPage, None, Key.PageDown, OnMoveCaret(CaretMovementType.PageDown)); AddBinding(EditingCommands.MoveDownByPage, None, Key.PageDown, OnMoveCaret(CaretMovementType.PageDown));
AddBinding(EditingCommands.SelectDownByPage, Shift, Key.PageDown, OnMoveCaretExtendSelection(CaretMovementType.PageDown)); AddBinding(EditingCommands.SelectDownByPage, Shift, Key.PageDown, OnMoveCaretExtendSelection(CaretMovementType.PageDown));
@ -64,8 +105,10 @@ namespace ICSharpCode.AvalonEdit.Editing
AddBinding(EditingCommands.MoveToLineStart, None, Key.Home, OnMoveCaret(CaretMovementType.LineStart)); AddBinding(EditingCommands.MoveToLineStart, None, Key.Home, OnMoveCaret(CaretMovementType.LineStart));
AddBinding(EditingCommands.SelectToLineStart, Shift, Key.Home, OnMoveCaretExtendSelection(CaretMovementType.LineStart)); AddBinding(EditingCommands.SelectToLineStart, Shift, Key.Home, OnMoveCaretExtendSelection(CaretMovementType.LineStart));
AddBinding(RectangleSelection.BoxSelectToLineStart, Alt | Shift, Key.Home, OnMoveCaretBoxSelection(CaretMovementType.LineStart));
AddBinding(EditingCommands.MoveToLineEnd, None, Key.End, OnMoveCaret(CaretMovementType.LineEnd)); AddBinding(EditingCommands.MoveToLineEnd, None, Key.End, OnMoveCaret(CaretMovementType.LineEnd));
AddBinding(EditingCommands.SelectToLineEnd, Shift, Key.End, OnMoveCaretExtendSelection(CaretMovementType.LineEnd)); AddBinding(EditingCommands.SelectToLineEnd, Shift, Key.End, OnMoveCaretExtendSelection(CaretMovementType.LineEnd));
AddBinding(RectangleSelection.BoxSelectToLineEnd, Alt | Shift, Key.End, OnMoveCaretBoxSelection(CaretMovementType.LineEnd));
AddBinding(EditingCommands.MoveToDocumentStart, Ctrl, Key.Home, OnMoveCaret(CaretMovementType.DocumentStart)); AddBinding(EditingCommands.MoveToDocumentStart, Ctrl, Key.Home, OnMoveCaret(CaretMovementType.DocumentStart));
AddBinding(EditingCommands.SelectToDocumentStart, Ctrl | Shift, Key.Home, OnMoveCaretExtendSelection(CaretMovementType.DocumentStart)); AddBinding(EditingCommands.SelectToDocumentStart, Ctrl | Shift, Key.Home, OnMoveCaretExtendSelection(CaretMovementType.DocumentStart));
@ -92,22 +135,6 @@ namespace ICSharpCode.AvalonEdit.Editing
return target as TextArea; return target as TextArea;
} }
enum CaretMovementType
{
CharLeft,
CharRight,
WordLeft,
WordRight,
LineUp,
LineDown,
PageUp,
PageDown,
LineStart,
LineEnd,
DocumentStart,
DocumentEnd
}
static ExecutedRoutedEventHandler OnMoveCaret(CaretMovementType direction) static ExecutedRoutedEventHandler OnMoveCaret(CaretMovementType direction)
{ {
return (target, args) => { return (target, args) => {
@ -135,44 +162,81 @@ namespace ICSharpCode.AvalonEdit.Editing
}; };
} }
static ExecutedRoutedEventHandler OnMoveCaretBoxSelection(CaretMovementType direction)
{
return (target, args) => {
TextArea textArea = GetTextArea(target);
if (textArea != null && textArea.Document != null) {
args.Handled = true;
// First, convert the selection into a rectangle selection
// (this is required so that virtual space gets enabled for the caret movement)
if (textArea.Options.EnableRectangularSelection && !(textArea.Selection is RectangleSelection)) {
if (textArea.Selection.IsEmpty) {
textArea.Selection = new RectangleSelection(textArea, textArea.Caret.Position, textArea.Caret.Position);
} else {
// Convert normal selection to rectangle selection
textArea.Selection = new RectangleSelection(textArea, textArea.Selection.StartPosition, textArea.Caret.Position);
}
}
// Now move the caret and extend the selection
TextViewPosition oldPosition = textArea.Caret.Position;
MoveCaret(textArea, direction);
textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldPosition, textArea.Caret.Position);
textArea.Caret.BringCaretToView();
}
};
}
#region Caret movement #region Caret movement
static void MoveCaret(TextArea textArea, CaretMovementType direction) internal static void MoveCaret(TextArea textArea, CaretMovementType direction)
{ {
DocumentLine caretLine = textArea.Document.GetLineByNumber(textArea.Caret.Line); double desiredXPos = textArea.Caret.DesiredXPos;
VisualLine visualLine = textArea.TextView.GetOrConstructVisualLine(caretLine); textArea.Caret.Position = GetNewCaretPosition(textArea.TextView, textArea.Caret.Position, direction, textArea.Selection.EnableVirtualSpace, ref desiredXPos);
TextViewPosition caretPosition = textArea.Caret.Position; textArea.Caret.DesiredXPos = desiredXPos;
TextLine textLine = visualLine.GetTextLine(caretPosition.VisualColumn); }
internal static TextViewPosition GetNewCaretPosition(TextView textView, TextViewPosition caretPosition, CaretMovementType direction, bool enableVirtualSpace, ref double desiredXPos)
{
switch (direction) {
case CaretMovementType.None:
return caretPosition;
case CaretMovementType.DocumentStart:
desiredXPos = double.NaN;
return new TextViewPosition(0, 0);
case CaretMovementType.DocumentEnd:
desiredXPos = double.NaN;
return new TextViewPosition(textView.Document.GetLocation(textView.Document.TextLength));
}
DocumentLine caretLine = textView.Document.GetLineByNumber(caretPosition.Line);
VisualLine visualLine = textView.GetOrConstructVisualLine(caretLine);
TextLine textLine = visualLine.GetTextLine(caretPosition.VisualColumn, caretPosition.IsAtEndOfLine);
switch (direction) { switch (direction) {
case CaretMovementType.CharLeft: case CaretMovementType.CharLeft:
MoveCaretLeft(textArea, caretPosition, visualLine, CaretPositioningMode.Normal); desiredXPos = double.NaN;
break; return GetPrevCaretPosition(textView, caretPosition, visualLine, CaretPositioningMode.Normal, enableVirtualSpace);
case CaretMovementType.Backspace:
desiredXPos = double.NaN;
return GetPrevCaretPosition(textView, caretPosition, visualLine, CaretPositioningMode.EveryCodepoint, enableVirtualSpace);
case CaretMovementType.CharRight: case CaretMovementType.CharRight:
MoveCaretRight(textArea, caretPosition, visualLine, CaretPositioningMode.Normal); desiredXPos = double.NaN;
break; return GetNextCaretPosition(textView, caretPosition, visualLine, CaretPositioningMode.Normal, enableVirtualSpace);
case CaretMovementType.WordLeft: case CaretMovementType.WordLeft:
MoveCaretLeft(textArea, caretPosition, visualLine, CaretPositioningMode.WordStart); desiredXPos = double.NaN;
break; return GetPrevCaretPosition(textView, caretPosition, visualLine, CaretPositioningMode.WordStart, enableVirtualSpace);
case CaretMovementType.WordRight: case CaretMovementType.WordRight:
MoveCaretRight(textArea, caretPosition, visualLine, CaretPositioningMode.WordStart); desiredXPos = double.NaN;
break; return GetNextCaretPosition(textView, caretPosition, visualLine, CaretPositioningMode.WordStart, enableVirtualSpace);
case CaretMovementType.LineUp: case CaretMovementType.LineUp:
case CaretMovementType.LineDown: case CaretMovementType.LineDown:
case CaretMovementType.PageUp: case CaretMovementType.PageUp:
case CaretMovementType.PageDown: case CaretMovementType.PageDown:
MoveCaretUpDown(textArea, direction, visualLine, textLine, caretPosition.VisualColumn); return GetUpDownCaretPosition(textView, caretPosition, direction, visualLine, textLine, enableVirtualSpace, ref desiredXPos);
break;
case CaretMovementType.DocumentStart:
SetCaretPosition(textArea, 0, 0);
break;
case CaretMovementType.DocumentEnd:
SetCaretPosition(textArea, -1, textArea.Document.TextLength);
break;
case CaretMovementType.LineStart: case CaretMovementType.LineStart:
MoveCaretToStartOfLine(textArea, visualLine); desiredXPos = double.NaN;
break; return GetStartOfLineCaretPosition(caretPosition.VisualColumn, visualLine, textLine, enableVirtualSpace);
case CaretMovementType.LineEnd: case CaretMovementType.LineEnd:
MoveCaretToEndOfLine(textArea, visualLine); desiredXPos = double.NaN;
break; return GetEndOfLineCaretPosition(visualLine, textLine);
default: default:
throw new NotSupportedException(direction.ToString()); throw new NotSupportedException(direction.ToString());
} }
@ -180,79 +244,80 @@ namespace ICSharpCode.AvalonEdit.Editing
#endregion #endregion
#region Home/End #region Home/End
static void MoveCaretToStartOfLine(TextArea textArea, VisualLine visualLine) static TextViewPosition GetStartOfLineCaretPosition(int oldVC, VisualLine visualLine, TextLine textLine, bool enableVirtualSpace)
{ {
int newVC = visualLine.GetNextCaretPosition(-1, LogicalDirection.Forward, CaretPositioningMode.WordStart, textArea.Selection.EnableVirtualSpace); int newVC = visualLine.GetTextLineVisualStartColumn(textLine);
if (newVC == 0)
newVC = visualLine.GetNextCaretPosition(newVC - 1, LogicalDirection.Forward, CaretPositioningMode.WordStart, enableVirtualSpace);
if (newVC < 0) if (newVC < 0)
throw ThrowUtil.NoValidCaretPosition(); throw ThrowUtil.NoValidCaretPosition();
// when the caret is already at the start of the text, jump to start before whitespace // when the caret is already at the start of the text, jump to start before whitespace
if (newVC == textArea.Caret.VisualColumn) if (newVC == oldVC)
newVC = 0; newVC = 0;
int offset = visualLine.FirstDocumentLine.Offset + visualLine.GetRelativeOffset(newVC); return visualLine.GetTextViewPosition(newVC);
SetCaretPosition(textArea, newVC, offset);
} }
static void MoveCaretToEndOfLine(TextArea textArea, VisualLine visualLine) static TextViewPosition GetEndOfLineCaretPosition(VisualLine visualLine, TextLine textLine)
{ {
int newVC = visualLine.VisualLength; int newVC = visualLine.GetTextLineVisualStartColumn(textLine) + textLine.Length - textLine.TrailingWhitespaceLength;
int offset = visualLine.FirstDocumentLine.Offset + visualLine.GetRelativeOffset(newVC); TextViewPosition pos = visualLine.GetTextViewPosition(newVC);
SetCaretPosition(textArea, newVC, offset); pos.IsAtEndOfLine = true;
return pos;
} }
#endregion #endregion
#region By-character / By-word movement #region By-character / By-word movement
static void MoveCaretRight(TextArea textArea, TextViewPosition caretPosition, VisualLine visualLine, CaretPositioningMode mode) static TextViewPosition GetNextCaretPosition(TextView textView, TextViewPosition caretPosition, VisualLine visualLine, CaretPositioningMode mode, bool enableVirtualSpace)
{ {
int pos = visualLine.GetNextCaretPosition(caretPosition.VisualColumn, LogicalDirection.Forward, mode, textArea.Selection.EnableVirtualSpace); int pos = visualLine.GetNextCaretPosition(caretPosition.VisualColumn, LogicalDirection.Forward, mode, enableVirtualSpace);
if (pos >= 0) { if (pos >= 0) {
SetCaretPosition(textArea, pos, visualLine.GetRelativeOffset(pos) + visualLine.FirstDocumentLine.Offset); return visualLine.GetTextViewPosition(pos);
} else { } else {
// move to start of next line // move to start of next line
DocumentLine nextDocumentLine = visualLine.LastDocumentLine.NextLine; DocumentLine nextDocumentLine = visualLine.LastDocumentLine.NextLine;
if (nextDocumentLine != null) { if (nextDocumentLine != null) {
VisualLine nextLine = textArea.TextView.GetOrConstructVisualLine(nextDocumentLine); VisualLine nextLine = textView.GetOrConstructVisualLine(nextDocumentLine);
pos = nextLine.GetNextCaretPosition(-1, LogicalDirection.Forward, mode, textArea.Selection.EnableVirtualSpace); pos = nextLine.GetNextCaretPosition(-1, LogicalDirection.Forward, mode, enableVirtualSpace);
if (pos < 0) if (pos < 0)
throw ThrowUtil.NoValidCaretPosition(); throw ThrowUtil.NoValidCaretPosition();
SetCaretPosition(textArea, pos, nextLine.GetRelativeOffset(pos) + nextLine.FirstDocumentLine.Offset); return nextLine.GetTextViewPosition(pos);
} else { } else {
// at end of document // at end of document
Debug.Assert(visualLine.LastDocumentLine.Offset + visualLine.LastDocumentLine.TotalLength == textArea.Document.TextLength); Debug.Assert(visualLine.LastDocumentLine.Offset + visualLine.LastDocumentLine.TotalLength == textView.Document.TextLength);
SetCaretPosition(textArea, -1, textArea.Document.TextLength); return new TextViewPosition(textView.Document.GetLocation(textView.Document.TextLength));
} }
} }
} }
static void MoveCaretLeft(TextArea textArea, TextViewPosition caretPosition, VisualLine visualLine, CaretPositioningMode mode) static TextViewPosition GetPrevCaretPosition(TextView textView, TextViewPosition caretPosition, VisualLine visualLine, CaretPositioningMode mode, bool enableVirtualSpace)
{ {
int pos = visualLine.GetNextCaretPosition(caretPosition.VisualColumn, LogicalDirection.Backward, mode, textArea.Selection.EnableVirtualSpace); int pos = visualLine.GetNextCaretPosition(caretPosition.VisualColumn, LogicalDirection.Backward, mode, enableVirtualSpace);
if (pos >= 0) { if (pos >= 0) {
SetCaretPosition(textArea, pos, visualLine.GetRelativeOffset(pos) + visualLine.FirstDocumentLine.Offset); return visualLine.GetTextViewPosition(pos);
} else { } else {
// move to end of previous line // move to end of previous line
DocumentLine previousDocumentLine = visualLine.FirstDocumentLine.PreviousLine; DocumentLine previousDocumentLine = visualLine.FirstDocumentLine.PreviousLine;
if (previousDocumentLine != null) { if (previousDocumentLine != null) {
VisualLine previousLine = textArea.TextView.GetOrConstructVisualLine(previousDocumentLine); VisualLine previousLine = textView.GetOrConstructVisualLine(previousDocumentLine);
pos = previousLine.GetNextCaretPosition(previousLine.VisualLength + 1, LogicalDirection.Backward, mode, textArea.Selection.EnableVirtualSpace); pos = previousLine.GetNextCaretPosition(previousLine.VisualLength + 1, LogicalDirection.Backward, mode, enableVirtualSpace);
if (pos < 0) if (pos < 0)
throw ThrowUtil.NoValidCaretPosition(); throw ThrowUtil.NoValidCaretPosition();
SetCaretPosition(textArea, pos, previousLine.GetRelativeOffset(pos) + previousLine.FirstDocumentLine.Offset); return previousLine.GetTextViewPosition(pos);
} else { } else {
// at start of document // at start of document
Debug.Assert(visualLine.FirstDocumentLine.Offset == 0); Debug.Assert(visualLine.FirstDocumentLine.Offset == 0);
SetCaretPosition(textArea, 0, 0); return new TextViewPosition(0, 0);
} }
} }
} }
#endregion #endregion
#region Line+Page up/down #region Line+Page up/down
static void MoveCaretUpDown(TextArea textArea, CaretMovementType direction, VisualLine visualLine, TextLine textLine, int caretVisualColumn) static TextViewPosition GetUpDownCaretPosition(TextView textView, TextViewPosition caretPosition, CaretMovementType direction, VisualLine visualLine, TextLine textLine, bool enableVirtualSpace, ref double xPos)
{ {
// moving up/down happens using the desired visual X position // moving up/down happens using the desired visual X position
double xPos = textArea.Caret.DesiredXPos;
if (double.IsNaN(xPos)) if (double.IsNaN(xPos))
xPos = visualLine.GetTextLineVisualXPosition(textLine, caretVisualColumn); xPos = visualLine.GetTextLineVisualXPosition(textLine, caretPosition.VisualColumn);
// now find the TextLine+VisualLine where the caret will end up in // now find the TextLine+VisualLine where the caret will end up in
VisualLine targetVisualLine = visualLine; VisualLine targetVisualLine = visualLine;
TextLine targetLine; TextLine targetLine;
@ -266,8 +331,8 @@ namespace ICSharpCode.AvalonEdit.Editing
if (textLineIndex > 0) { if (textLineIndex > 0) {
targetLine = visualLine.TextLines[textLineIndex - 1]; targetLine = visualLine.TextLines[textLineIndex - 1];
} else if (prevLineNumber >= 1) { } else if (prevLineNumber >= 1) {
DocumentLine prevLine = textArea.Document.GetLineByNumber(prevLineNumber); DocumentLine prevLine = textView.Document.GetLineByNumber(prevLineNumber);
targetVisualLine = textArea.TextView.GetOrConstructVisualLine(prevLine); targetVisualLine = textView.GetOrConstructVisualLine(prevLine);
targetLine = targetVisualLine.TextLines[targetVisualLine.TextLines.Count - 1]; targetLine = targetVisualLine.TextLines[targetVisualLine.TextLines.Count - 1];
} else { } else {
targetLine = null; targetLine = null;
@ -281,9 +346,9 @@ namespace ICSharpCode.AvalonEdit.Editing
int nextLineNumber = visualLine.LastDocumentLine.LineNumber + 1; int nextLineNumber = visualLine.LastDocumentLine.LineNumber + 1;
if (textLineIndex < visualLine.TextLines.Count - 1) { if (textLineIndex < visualLine.TextLines.Count - 1) {
targetLine = visualLine.TextLines[textLineIndex + 1]; targetLine = visualLine.TextLines[textLineIndex + 1];
} else if (nextLineNumber <= textArea.Document.LineCount) { } else if (nextLineNumber <= textView.Document.LineCount) {
DocumentLine nextLine = textArea.Document.GetLineByNumber(nextLineNumber); DocumentLine nextLine = textView.Document.GetLineByNumber(nextLineNumber);
targetVisualLine = textArea.TextView.GetOrConstructVisualLine(nextLine); targetVisualLine = textView.GetOrConstructVisualLine(nextLine);
targetLine = targetVisualLine.TextLines[0]; targetLine = targetVisualLine.TextLines[0];
} else { } else {
targetLine = null; targetLine = null;
@ -296,11 +361,11 @@ namespace ICSharpCode.AvalonEdit.Editing
// Page up/down: find the target line using its visual position // Page up/down: find the target line using its visual position
double yPos = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.LineMiddle); double yPos = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.LineMiddle);
if (direction == CaretMovementType.PageUp) if (direction == CaretMovementType.PageUp)
yPos -= textArea.TextView.RenderSize.Height; yPos -= textView.RenderSize.Height;
else else
yPos += textArea.TextView.RenderSize.Height; yPos += textView.RenderSize.Height;
DocumentLine newLine = textArea.TextView.GetDocumentLineByVisualTop(yPos); DocumentLine newLine = textView.GetDocumentLineByVisualTop(yPos);
targetVisualLine = textArea.TextView.GetOrConstructVisualLine(newLine); targetVisualLine = textView.GetOrConstructVisualLine(newLine);
targetLine = targetVisualLine.GetTextLineByVisualYPosition(yPos); targetLine = targetVisualLine.GetTextLineByVisualYPosition(yPos);
break; break;
} }
@ -309,30 +374,18 @@ namespace ICSharpCode.AvalonEdit.Editing
} }
if (targetLine != null) { if (targetLine != null) {
double yPos = targetVisualLine.GetTextLineVisualYPosition(targetLine, VisualYPosition.LineMiddle); double yPos = targetVisualLine.GetTextLineVisualYPosition(targetLine, VisualYPosition.LineMiddle);
int newVisualColumn = targetVisualLine.GetVisualColumn(new Point(xPos, yPos), textArea.Selection.EnableVirtualSpace); int newVisualColumn = targetVisualLine.GetVisualColumn(new Point(xPos, yPos), enableVirtualSpace);
SetCaretPosition(textArea, targetVisualLine, targetLine, newVisualColumn, false);
textArea.Caret.DesiredXPos = xPos; // prevent wrapping to the next line; TODO: could 'IsAtEnd' help here?
} int targetLineStartCol = targetVisualLine.GetTextLineVisualStartColumn(targetLine);
} if (newVisualColumn >= targetLineStartCol + targetLine.Length) {
#endregion if (newVisualColumn <= targetVisualLine.VisualLength)
newVisualColumn = targetLineStartCol + targetLine.Length - 1;
#region SetCaretPosition }
static void SetCaretPosition(TextArea textArea, VisualLine targetVisualLine, TextLine targetLine, return targetVisualLine.GetTextViewPosition(newVisualColumn);
int newVisualColumn, bool allowWrapToNextLine) } else {
{ return caretPosition;
int targetLineStartCol = targetVisualLine.GetTextLineVisualStartColumn(targetLine);
if (!allowWrapToNextLine && newVisualColumn >= targetLineStartCol + targetLine.Length) {
if (newVisualColumn <= targetVisualLine.VisualLength)
newVisualColumn = targetLineStartCol + targetLine.Length - 1;
} }
int newOffset = targetVisualLine.GetRelativeOffset(newVisualColumn) + targetVisualLine.FirstDocumentLine.Offset;
SetCaretPosition(textArea, newVisualColumn, newOffset);
}
static void SetCaretPosition(TextArea textArea, int newVisualColumn, int newOffset)
{
textArea.Caret.Position = new TextViewPosition(textArea.Document.GetLocation(newOffset), newVisualColumn);
textArea.Caret.DesiredXPos = double.NaN;
} }
#endregion #endregion
} }

19
AvalonEdit/ICSharpCode.AvalonEdit/Editing/CaretWeakEventHandler.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.AvalonEdit.Utils;
using System; using System;

19
AvalonEdit/ICSharpCode.AvalonEdit/Editing/DottedLineMargin.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Windows; using System.Windows;

19
AvalonEdit/ICSharpCode.AvalonEdit/Editing/DragDropException.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Runtime.Serialization; using System.Runtime.Serialization;

172
AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -8,14 +23,15 @@ using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using System.Windows; using System.Windows;
using System.Windows.Documents; using System.Windows.Documents;
using System.Windows.Input; using System.Windows.Input;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Search;
using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.AvalonEdit.Utils;
#if NREFACTORY
using ICSharpCode.NRefactory.Editor;
#endif
namespace ICSharpCode.AvalonEdit.Editing namespace ICSharpCode.AvalonEdit.Editing
{ {
@ -47,12 +63,12 @@ namespace ICSharpCode.AvalonEdit.Editing
static EditingCommandHandler() static EditingCommandHandler()
{ {
CommandBindings.Add(new CommandBinding(ApplicationCommands.Delete, OnDelete(ApplicationCommands.NotACommand), CanDelete)); CommandBindings.Add(new CommandBinding(ApplicationCommands.Delete, OnDelete(CaretMovementType.None), CanDelete));
AddBinding(EditingCommands.Delete, ModifierKeys.None, Key.Delete, OnDelete(EditingCommands.SelectRightByCharacter)); AddBinding(EditingCommands.Delete, ModifierKeys.None, Key.Delete, OnDelete(CaretMovementType.CharRight));
AddBinding(EditingCommands.DeleteNextWord, ModifierKeys.Control, Key.Delete, OnDelete(EditingCommands.SelectRightByWord)); AddBinding(EditingCommands.DeleteNextWord, ModifierKeys.Control, Key.Delete, OnDelete(CaretMovementType.WordRight));
AddBinding(EditingCommands.Backspace, ModifierKeys.None, Key.Back, OnDelete(EditingCommands.SelectLeftByCharacter)); AddBinding(EditingCommands.Backspace, ModifierKeys.None, Key.Back, OnDelete(CaretMovementType.Backspace));
InputBindings.Add(TextAreaDefaultInputHandler.CreateFrozenKeyBinding(EditingCommands.Backspace, ModifierKeys.Shift, Key.Back)); // make Shift-Backspace do the same as plain backspace InputBindings.Add(TextAreaDefaultInputHandler.CreateFrozenKeyBinding(EditingCommands.Backspace, ModifierKeys.Shift, Key.Back)); // make Shift-Backspace do the same as plain backspace
AddBinding(EditingCommands.DeletePreviousWord, ModifierKeys.Control, Key.Back, OnDelete(EditingCommands.SelectLeftByWord)); AddBinding(EditingCommands.DeletePreviousWord, ModifierKeys.Control, Key.Back, OnDelete(CaretMovementType.WordLeft));
AddBinding(EditingCommands.EnterParagraphBreak, ModifierKeys.None, Key.Enter, OnEnter); AddBinding(EditingCommands.EnterParagraphBreak, ModifierKeys.None, Key.Enter, OnEnter);
AddBinding(EditingCommands.EnterLineBreak, ModifierKeys.Shift, Key.Enter, OnEnter); AddBinding(EditingCommands.EnterLineBreak, ModifierKeys.Shift, Key.Enter, OnEnter);
AddBinding(EditingCommands.TabForward, ModifierKeys.None, Key.Tab, OnTab); AddBinding(EditingCommands.TabForward, ModifierKeys.None, Key.Tab, OnTab);
@ -226,34 +242,28 @@ namespace ICSharpCode.AvalonEdit.Editing
#endregion #endregion
#region Delete #region Delete
static ExecutedRoutedEventHandler OnDelete(RoutedUICommand selectingCommand) static ExecutedRoutedEventHandler OnDelete(CaretMovementType caretMovement)
{ {
return (target, args) => { return (target, args) => {
TextArea textArea = GetTextArea(target); TextArea textArea = GetTextArea(target);
if (textArea != null && textArea.Document != null) { if (textArea != null && textArea.Document != null) {
// call BeginUpdate before running the 'selectingCommand' if (textArea.Selection.IsEmpty) {
// so that undoing the delete does not select the deleted character TextViewPosition startPos = textArea.Caret.Position;
using (textArea.Document.RunUpdate()) { bool enableVirtualSpace = textArea.Options.EnableVirtualSpace;
if (textArea.Selection.IsEmpty) { // When pressing delete; don't move the caret further into virtual space - instead delete the newline
TextViewPosition oldCaretPosition = textArea.Caret.Position; if (caretMovement == CaretMovementType.CharRight)
if (textArea.Caret.IsInVirtualSpace && selectingCommand == EditingCommands.SelectRightByCharacter) enableVirtualSpace = false;
EditingCommands.SelectRightByWord.Execute(args.Parameter, textArea); double desiredXPos = textArea.Caret.DesiredXPos;
else TextViewPosition endPos = CaretNavigationCommandHandler.GetNewCaretPosition(
selectingCommand.Execute(args.Parameter, textArea); textArea.TextView, startPos, caretMovement, enableVirtualSpace, ref desiredXPos);
bool hasSomethingDeletable = false; // GetNewCaretPosition may return (0,0) as new position,
foreach (ISegment s in textArea.Selection.Segments) { // thus we need to validate endPos before using it in the selection.
if (textArea.GetDeletableSegments(s).Length > 0) { if (endPos.Line < 1 || endPos.Column < 1)
hasSomethingDeletable = true; endPos = new TextViewPosition(Math.Max(endPos.Line, 1), Math.Max(endPos.Column, 1));
break; // Don't select the text to be deleted; just reuse the ReplaceSelectionWithText logic
} var sel = new SimpleSelection(textArea, startPos, endPos);
} sel.ReplaceSelectionWithText(string.Empty);
if (!hasSomethingDeletable) { } else {
// If nothing in the selection is deletable; then reset caret+selection
// to the previous value. This prevents the caret from moving through read-only sections.
textArea.Caret.Position = oldCaretPosition;
textArea.ClearSelection();
}
}
textArea.RemoveSelectedText(); textArea.RemoveSelectedText();
} }
textArea.Caret.BringCaretToView(); textArea.Caret.BringCaretToView();
@ -304,64 +314,88 @@ namespace ICSharpCode.AvalonEdit.Editing
if (textArea != null && textArea.Document != null) { if (textArea != null && textArea.Document != null) {
if (textArea.Selection.IsEmpty && textArea.Options.CutCopyWholeLine) { if (textArea.Selection.IsEmpty && textArea.Options.CutCopyWholeLine) {
DocumentLine currentLine = textArea.Document.GetLineByNumber(textArea.Caret.Line); DocumentLine currentLine = textArea.Document.GetLineByNumber(textArea.Caret.Line);
CopyWholeLine(textArea, currentLine); if (CopyWholeLine(textArea, currentLine)) {
ISegment[] segmentsToDelete = textArea.GetDeletableSegments(new SimpleSegment(currentLine.Offset, currentLine.TotalLength)); ISegment[] segmentsToDelete = textArea.GetDeletableSegments(new SimpleSegment(currentLine.Offset, currentLine.TotalLength));
for (int i = segmentsToDelete.Length - 1; i >= 0; i--) { for (int i = segmentsToDelete.Length - 1; i >= 0; i--) {
textArea.Document.Remove(segmentsToDelete[i]); textArea.Document.Remove(segmentsToDelete[i]);
}
} }
} else { } else {
CopySelectedText(textArea); if (CopySelectedText(textArea))
textArea.RemoveSelectedText(); textArea.RemoveSelectedText();
} }
textArea.Caret.BringCaretToView(); textArea.Caret.BringCaretToView();
args.Handled = true; args.Handled = true;
} }
} }
static void CopySelectedText(TextArea textArea) static bool CopySelectedText(TextArea textArea)
{ {
var data = textArea.Selection.CreateDataObject(textArea); var data = textArea.Selection.CreateDataObject(textArea);
var copyingEventArgs = new DataObjectCopyingEventArgs(data, false);
textArea.RaiseEvent(copyingEventArgs);
if (copyingEventArgs.CommandCancelled)
return false;
try { try {
Clipboard.SetDataObject(data, true); Clipboard.SetDataObject(data, true);
} catch (ExternalException) { } catch (ExternalException) {
// Apparently this exception sometimes happens randomly. // Apparently this exception sometimes happens randomly.
// The MS controls just ignore it, so we'll do the same. // The MS controls just ignore it, so we'll do the same.
return;
} }
string text = textArea.Selection.GetText(); string text = textArea.Selection.GetText();
text = TextUtilities.NormalizeNewLines(text, Environment.NewLine); text = TextUtilities.NormalizeNewLines(text, Environment.NewLine);
textArea.OnTextCopied(new TextEventArgs(text)); textArea.OnTextCopied(new TextEventArgs(text));
return true;
} }
const string LineSelectedType = "MSDEVLineSelect"; // This is the type VS 2003 and 2005 use for flagging a whole line copy const string LineSelectedType = "MSDEVLineSelect"; // This is the type VS 2003 and 2005 use for flagging a whole line copy
static void CopyWholeLine(TextArea textArea, DocumentLine line) public static bool ConfirmDataFormat(TextArea textArea, DataObject dataObject, string format)
{
var e = new DataObjectSettingDataEventArgs(dataObject, format);
textArea.RaiseEvent(e);
return !e.CommandCancelled;
}
static bool CopyWholeLine(TextArea textArea, DocumentLine line)
{ {
ISegment wholeLine = new SimpleSegment(line.Offset, line.TotalLength); ISegment wholeLine = new SimpleSegment(line.Offset, line.TotalLength);
string text = textArea.Document.GetText(wholeLine); string text = textArea.Document.GetText(wholeLine);
// Ensure we use the appropriate newline sequence for the OS // Ensure we use the appropriate newline sequence for the OS
text = TextUtilities.NormalizeNewLines(text, Environment.NewLine); text = TextUtilities.NormalizeNewLines(text, Environment.NewLine);
DataObject data = new DataObject(text); DataObject data = new DataObject();
if (ConfirmDataFormat(textArea, data, DataFormats.UnicodeText))
data.SetText(text);
// Also copy text in HTML format to clipboard - good for pasting text into Word // Also copy text in HTML format to clipboard - good for pasting text into Word
// or to the SharpDevelop forums. // or to the SharpDevelop forums.
IHighlighter highlighter = textArea.GetService(typeof(IHighlighter)) as IHighlighter; if (ConfirmDataFormat(textArea, data, DataFormats.Html)) {
HtmlClipboard.SetHtml(data, HtmlClipboard.CreateHtmlFragment(textArea.Document, highlighter, wholeLine, new HtmlOptions(textArea.Options))); IHighlighter highlighter = textArea.GetService(typeof(IHighlighter)) as IHighlighter;
HtmlClipboard.SetHtml(data, HtmlClipboard.CreateHtmlFragment(textArea.Document, highlighter, wholeLine, new HtmlOptions(textArea.Options)));
}
if (ConfirmDataFormat(textArea, data, LineSelectedType)) {
MemoryStream lineSelected = new MemoryStream(1);
lineSelected.WriteByte(1);
data.SetData(LineSelectedType, lineSelected, false);
}
MemoryStream lineSelected = new MemoryStream(1); var copyingEventArgs = new DataObjectCopyingEventArgs(data, false);
lineSelected.WriteByte(1); textArea.RaiseEvent(copyingEventArgs);
data.SetData(LineSelectedType, lineSelected, false); if (copyingEventArgs.CommandCancelled)
return false;
try { try {
Clipboard.SetDataObject(data, true); Clipboard.SetDataObject(data, true);
} catch (ExternalException) { } catch (ExternalException) {
// Apparently this exception sometimes happens randomly. // Apparently this exception sometimes happens randomly.
// The MS controls just ignore it, so we'll do the same. // The MS controls just ignore it, so we'll do the same.
return; return false;
} }
textArea.OnTextCopied(new TextEventArgs(text)); textArea.OnTextCopied(new TextEventArgs(text));
return true;
} }
static void CanPaste(object target, CanExecuteRoutedEventArgs args) static void CanPaste(object target, CanExecuteRoutedEventArgs args)
@ -388,21 +422,43 @@ namespace ICSharpCode.AvalonEdit.Editing
} }
if (dataObject == null) if (dataObject == null)
return; return;
Debug.WriteLine( dataObject.GetData(DataFormats.Html) as string );
var pastingEventArgs = new DataObjectPastingEventArgs(dataObject, false, DataFormats.UnicodeText);
textArea.RaiseEvent(pastingEventArgs);
if (pastingEventArgs.CommandCancelled)
return;
dataObject = pastingEventArgs.DataObject;
if (dataObject == null)
return;
// convert text back to correct newlines for this document // convert text back to correct newlines for this document
string newLine = TextUtilities.GetNewLineFromDocument(textArea.Document, textArea.Caret.Line); string newLine = TextUtilities.GetNewLineFromDocument(textArea.Document, textArea.Caret.Line);
string text; string text;
try { try {
text = (string)dataObject.GetData(DataFormats.UnicodeText); // Try retrieving the text as one of:
// - the FormatToApply
// - UnicodeText
// - Text
// (but don't try the same format twice)
if (pastingEventArgs.FormatToApply != null && dataObject.GetDataPresent(pastingEventArgs.FormatToApply))
text = (string)dataObject.GetData(pastingEventArgs.FormatToApply);
else if (pastingEventArgs.FormatToApply != DataFormats.UnicodeText && dataObject.GetDataPresent(DataFormats.UnicodeText))
text = (string)dataObject.GetData(DataFormats.UnicodeText);
else if (pastingEventArgs.FormatToApply != DataFormats.Text && dataObject.GetDataPresent(DataFormats.Text))
text = (string)dataObject.GetData(DataFormats.Text);
else
return; // no text data format
text = TextUtilities.NormalizeNewLines(text, newLine); text = TextUtilities.NormalizeNewLines(text, newLine);
} catch (OutOfMemoryException) { } catch (OutOfMemoryException) {
// may happen when trying to paste a huge string
return; return;
} }
if (!string.IsNullOrEmpty(text)) { if (!string.IsNullOrEmpty(text)) {
bool fullLine = textArea.Options.CutCopyWholeLine && dataObject.GetDataPresent(LineSelectedType); bool fullLine = textArea.Options.CutCopyWholeLine && dataObject.GetDataPresent(LineSelectedType);
bool rectangular = dataObject.GetDataPresent(RectangleSelection.RectangularSelectionDataType); bool rectangular = dataObject.GetDataPresent(RectangleSelection.RectangularSelectionDataType);
if (fullLine) { if (fullLine) {
DocumentLine currentLine = textArea.Document.GetLineByNumber(textArea.Caret.Line); DocumentLine currentLine = textArea.Document.GetLineByNumber(textArea.Caret.Line);
if (textArea.ReadOnlySectionProvider.CanInsert(currentLine.Offset)) { if (textArea.ReadOnlySectionProvider.CanInsert(currentLine.Offset)) {
@ -426,8 +482,18 @@ namespace ICSharpCode.AvalonEdit.Editing
{ {
TextArea textArea = GetTextArea(target); TextArea textArea = GetTextArea(target);
if (textArea != null && textArea.Document != null) { if (textArea != null && textArea.Document != null) {
DocumentLine currentLine = textArea.Document.GetLineByNumber(textArea.Caret.Line); int firstLineIndex, lastLineIndex;
textArea.Selection = Selection.Create(textArea, currentLine.Offset, currentLine.Offset + currentLine.TotalLength); if (textArea.Selection.Length == 0) {
// There is no selection, simply delete current line
firstLineIndex = lastLineIndex = textArea.Caret.Line;
} else {
// There is a selection, remove all lines affected by it (use Min/Max to be independent from selection direction)
firstLineIndex = Math.Min(textArea.Selection.StartPosition.Line, textArea.Selection.EndPosition.Line);
lastLineIndex = Math.Max(textArea.Selection.StartPosition.Line, textArea.Selection.EndPosition.Line);
}
DocumentLine startLine = textArea.Document.GetLineByNumber(firstLineIndex);
DocumentLine endLine = textArea.Document.GetLineByNumber(lastLineIndex);
textArea.Selection = Selection.Create(textArea, startLine.Offset, endLine.Offset + endLine.TotalLength);
textArea.RemoveSelectedText(); textArea.RemoveSelectedText();
args.Handled = true; args.Handled = true;
} }

32
AvalonEdit/ICSharpCode.AvalonEdit/Editing/EmptySelection.cs

@ -1,11 +1,31 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.AvalonEdit.Utils;
#if NREFACTORY
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Editor;
#endif
namespace ICSharpCode.AvalonEdit.Editing namespace ICSharpCode.AvalonEdit.Editing
{ {
@ -20,6 +40,14 @@ namespace ICSharpCode.AvalonEdit.Editing
return this; return this;
} }
public override TextViewPosition StartPosition {
get { return new TextViewPosition(TextLocation.Empty); }
}
public override TextViewPosition EndPosition {
get { return new TextViewPosition(TextLocation.Empty); }
}
public override ISegment SurroundingSegment { public override ISegment SurroundingSegment {
get { return null; } get { return null; }
} }

23
AvalonEdit/ICSharpCode.AvalonEdit/Editing/IReadOnlySectionProvider.cs

@ -1,9 +1,28 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
#if NREFACTORY
using ICSharpCode.NRefactory.Editor;
#else
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
#endif
namespace ICSharpCode.AvalonEdit.Editing namespace ICSharpCode.AvalonEdit.Editing
{ {

221
AvalonEdit/ICSharpCode.AvalonEdit/Editing/ImeNativeWrapper.cs

@ -0,0 +1,221 @@
// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.TextFormatting;
using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.AvalonEdit.Utils;
using Draw = System.Drawing;
namespace ICSharpCode.AvalonEdit.Editing
{
/// <summary>
/// Native API required for IME support.
/// </summary>
static class ImeNativeWrapper
{
[StructLayout(LayoutKind.Sequential)]
struct CompositionForm
{
public int dwStyle;
public POINT ptCurrentPos;
public RECT rcArea;
}
[StructLayout(LayoutKind.Sequential)]
struct POINT
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
struct LOGFONT
{
public int lfHeight;
public int lfWidth;
public int lfEscapement;
public int lfOrientation;
public int lfWeight;
public byte lfItalic;
public byte lfUnderline;
public byte lfStrikeOut;
public byte lfCharSet;
public byte lfOutPrecision;
public byte lfClipPrecision;
public byte lfQuality;
public byte lfPitchAndFamily;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)] public string lfFaceName;
}
const int CPS_CANCEL = 0x4;
const int NI_COMPOSITIONSTR = 0x15;
const int GCS_COMPSTR = 0x0008;
public const int WM_IME_COMPOSITION = 0x10F;
public const int WM_IME_SETCONTEXT = 0x281;
public const int WM_INPUTLANGCHANGE = 0x51;
[DllImport("imm32.dll")]
public static extern IntPtr ImmAssociateContext(IntPtr hWnd, IntPtr hIMC);
[DllImport("imm32.dll")]
internal static extern IntPtr ImmGetContext(IntPtr hWnd);
[DllImport("imm32.dll")]
internal static extern IntPtr ImmGetDefaultIMEWnd(IntPtr hWnd);
[DllImport("imm32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool ImmReleaseContext(IntPtr hWnd, IntPtr hIMC);
[DllImport("imm32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool ImmNotifyIME(IntPtr hIMC, int dwAction, int dwIndex, int dwValue = 0);
[DllImport("imm32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool ImmSetCompositionWindow(IntPtr hIMC, ref CompositionForm form);
[DllImport("imm32.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool ImmSetCompositionFont(IntPtr hIMC, ref LOGFONT font);
[DllImport("imm32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool ImmGetCompositionFont(IntPtr hIMC, out LOGFONT font);
[DllImport("msctf.dll")]
static extern int TF_CreateThreadMgr(out ITfThreadMgr threadMgr);
[ThreadStatic] static bool textFrameworkThreadMgrInitialized;
[ThreadStatic] static ITfThreadMgr textFrameworkThreadMgr;
public static ITfThreadMgr GetTextFrameworkThreadManager()
{
if (!textFrameworkThreadMgrInitialized) {
textFrameworkThreadMgrInitialized = true;
TF_CreateThreadMgr(out textFrameworkThreadMgr);
}
return textFrameworkThreadMgr;
}
public static bool NotifyIme(IntPtr hIMC)
{
return ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL);
}
public static bool SetCompositionWindow(HwndSource source, IntPtr hIMC, TextArea textArea)
{
if (textArea == null)
throw new ArgumentNullException("textArea");
Rect textViewBounds = textArea.TextView.GetBounds(source);
Rect characterBounds = textArea.TextView.GetCharacterBounds(textArea.Caret.Position, source);
CompositionForm form = new CompositionForm();
form.dwStyle = 0x0020;
form.ptCurrentPos.x = (int)Math.Max(characterBounds.Left, textViewBounds.Left);
form.ptCurrentPos.y = (int)Math.Max(characterBounds.Top, textViewBounds.Top);
form.rcArea.left = (int)textViewBounds.Left;
form.rcArea.top = (int)textViewBounds.Top;
form.rcArea.right = (int)textViewBounds.Right;
form.rcArea.bottom = (int)textViewBounds.Bottom;
return ImmSetCompositionWindow(hIMC, ref form);
}
public static bool SetCompositionFont(HwndSource source, IntPtr hIMC, TextArea textArea)
{
if (textArea == null)
throw new ArgumentNullException("textArea");
LOGFONT lf = new LOGFONT();
Rect characterBounds = textArea.TextView.GetCharacterBounds(textArea.Caret.Position, source);
lf.lfFaceName = textArea.FontFamily.Source;
lf.lfHeight = (int)characterBounds.Height;
return ImmSetCompositionFont(hIMC, ref lf);
}
static Rect GetBounds(this TextView textView, HwndSource source)
{
// this may happen during layout changes in AvalonDock, so we just return an empty rectangle
// in those cases. It should be refreshed immediately.
if (source.RootVisual == null || !source.RootVisual.IsAncestorOf(textView))
return EMPTY_RECT;
Rect displayRect = new Rect(0, 0, textView.ActualWidth, textView.ActualHeight);
return textView
.TransformToAncestor(source.RootVisual).TransformBounds(displayRect) // rect on root visual
.TransformToDevice(source.RootVisual); // rect on HWND
}
static readonly Rect EMPTY_RECT = new Rect(0, 0, 0, 0);
static Rect GetCharacterBounds(this TextView textView, TextViewPosition pos, HwndSource source)
{
VisualLine vl = textView.GetVisualLine(pos.Line);
if (vl == null)
return EMPTY_RECT;
// this may happen during layout changes in AvalonDock, so we just return an empty rectangle
// in those cases. It should be refreshed immediately.
if (source.RootVisual == null || !source.RootVisual.IsAncestorOf(textView))
return EMPTY_RECT;
TextLine line = vl.GetTextLine(pos.VisualColumn, pos.IsAtEndOfLine);
Rect displayRect;
// calculate the display rect for the current character
if (pos.VisualColumn < vl.VisualLengthWithEndOfLineMarker) {
displayRect = line.GetTextBounds(pos.VisualColumn, 1).First().Rectangle;
displayRect.Offset(0, vl.GetTextLineVisualYPosition(line, VisualYPosition.LineTop));
} else {
// if we are in virtual space, we just use one wide-space as character width
displayRect = new Rect(vl.GetVisualPosition(pos.VisualColumn, VisualYPosition.TextTop),
new Size(textView.WideSpaceWidth, textView.DefaultLineHeight));
}
// adjust to current scrolling
displayRect.Offset(-textView.ScrollOffset);
return textView
.TransformToAncestor(source.RootVisual).TransformBounds(displayRect) // rect on root visual
.TransformToDevice(source.RootVisual); // rect on HWND
}
}
[ComImport, Guid("aa80e801-2021-11d2-93e0-0060b067b86e"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface ITfThreadMgr
{
void Activate(out int clientId);
void Deactivate();
void CreateDocumentMgr(out IntPtr docMgr);
void EnumDocumentMgrs(out IntPtr enumDocMgrs);
void GetFocus(out IntPtr docMgr);
void SetFocus(IntPtr docMgr);
void AssociateFocus(IntPtr hwnd, IntPtr newDocMgr, out IntPtr prevDocMgr);
void IsThreadFocus([MarshalAs(UnmanagedType.Bool)] out bool isFocus);
void GetFunctionProvider(ref Guid classId, out IntPtr funcProvider);
void EnumFunctionProviders(out IntPtr enumProviders);
void GetGlobalCompartment(out IntPtr compartmentMgr);
}
}

165
AvalonEdit/ICSharpCode.AvalonEdit/Editing/ImeSupport.cs

@ -0,0 +1,165 @@
// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.TextFormatting;
using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Rendering;
namespace ICSharpCode.AvalonEdit.Editing
{
class ImeSupport
{
readonly TextArea textArea;
IntPtr currentContext;
IntPtr previousContext;
IntPtr defaultImeWnd;
HwndSource hwndSource;
EventHandler requerySuggestedHandler; // we need to keep the event handler instance alive because CommandManager.RequerySuggested uses weak references
bool isReadOnly;
public ImeSupport(TextArea textArea)
{
if (textArea == null)
throw new ArgumentNullException("textArea");
this.textArea = textArea;
InputMethod.SetIsInputMethodSuspended(this.textArea, textArea.Options.EnableImeSupport);
// We listen to CommandManager.RequerySuggested for both caret offset changes and changes to the set of read-only sections.
// This is because there's no dedicated event for read-only section changes; but RequerySuggested needs to be raised anyways
// to invalidate the Paste command.
requerySuggestedHandler = OnRequerySuggested;
CommandManager.RequerySuggested += requerySuggestedHandler;
textArea.OptionChanged += TextAreaOptionChanged;
}
void OnRequerySuggested(object sender, EventArgs e)
{
UpdateImeEnabled();
}
void TextAreaOptionChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "EnableImeSupport") {
InputMethod.SetIsInputMethodSuspended(this.textArea, textArea.Options.EnableImeSupport);
UpdateImeEnabled();
}
}
public void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
UpdateImeEnabled();
}
public void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
if (e.OldFocus == textArea && currentContext != IntPtr.Zero)
ImeNativeWrapper.NotifyIme(currentContext);
ClearContext();
}
void UpdateImeEnabled()
{
if (textArea.Options.EnableImeSupport && textArea.IsKeyboardFocused) {
bool newReadOnly = !textArea.ReadOnlySectionProvider.CanInsert(textArea.Caret.Offset);
if (hwndSource == null || isReadOnly != newReadOnly) {
ClearContext(); // clear existing context (on read-only change)
isReadOnly = newReadOnly;
CreateContext();
}
} else {
ClearContext();
}
}
void ClearContext()
{
if (hwndSource != null) {
ImeNativeWrapper.ImmAssociateContext(hwndSource.Handle, previousContext);
ImeNativeWrapper.ImmReleaseContext(defaultImeWnd, currentContext);
currentContext = IntPtr.Zero;
defaultImeWnd = IntPtr.Zero;
hwndSource.RemoveHook(WndProc);
hwndSource = null;
}
}
void CreateContext()
{
hwndSource = (HwndSource)PresentationSource.FromVisual(this.textArea);
if (hwndSource != null) {
if (isReadOnly) {
defaultImeWnd = IntPtr.Zero;
currentContext = IntPtr.Zero;
} else {
defaultImeWnd = ImeNativeWrapper.ImmGetDefaultIMEWnd(IntPtr.Zero);
currentContext = ImeNativeWrapper.ImmGetContext(defaultImeWnd);
}
previousContext = ImeNativeWrapper.ImmAssociateContext(hwndSource.Handle, currentContext);
hwndSource.AddHook(WndProc);
// UpdateCompositionWindow() will be called by the caret becoming visible
var threadMgr = ImeNativeWrapper.GetTextFrameworkThreadManager();
if (threadMgr != null) {
// Even though the docu says passing null is invalid, this seems to help
// activating the IME on the default input context that is shared with WPF
threadMgr.SetFocus(IntPtr.Zero);
}
}
}
IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg) {
case ImeNativeWrapper.WM_INPUTLANGCHANGE:
// Don't mark the message as handled; other windows
// might want to handle it as well.
// If we have a context, recreate it
if (hwndSource != null) {
ClearContext();
CreateContext();
}
break;
case ImeNativeWrapper.WM_IME_COMPOSITION:
UpdateCompositionWindow();
break;
}
return IntPtr.Zero;
}
public void UpdateCompositionWindow()
{
if (currentContext != IntPtr.Zero) {
ImeNativeWrapper.SetCompositionFont(hwndSource, currentContext, textArea);
ImeNativeWrapper.SetCompositionWindow(hwndSource, currentContext, textArea);
}
}
}
}

42
AvalonEdit/ICSharpCode.AvalonEdit/Editing/LineNumberMargin.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.ComponentModel; using System.ComponentModel;
@ -29,8 +44,17 @@ namespace ICSharpCode.AvalonEdit.Editing
TextArea textArea; TextArea textArea;
Typeface typeface; /// <summary>
double emSize; /// The typeface used for rendering the line number margin.
/// This field is calculated in MeasureOverride() based on the FontFamily etc. properties.
/// </summary>
protected Typeface typeface;
/// <summary>
/// The font size used for rendering the line number margin.
/// This field is calculated in MeasureOverride() based on the FontFamily etc. properties.
/// </summary>
protected double emSize;
/// <inheritdoc/> /// <inheritdoc/>
protected override Size MeasureOverride(Size availableSize) protected override Size MeasureOverride(Size availableSize)
@ -79,7 +103,7 @@ namespace ICSharpCode.AvalonEdit.Editing
newTextView.VisualLinesChanged += TextViewVisualLinesChanged; newTextView.VisualLinesChanged += TextViewVisualLinesChanged;
// find the text area belonging to the new text view // find the text area belonging to the new text view
textArea = newTextView.Services.GetService(typeof(TextArea)) as TextArea; textArea = newTextView.GetService(typeof(TextArea)) as TextArea;
} else { } else {
textArea = null; textArea = null;
} }
@ -114,7 +138,10 @@ namespace ICSharpCode.AvalonEdit.Editing
return ReceiveWeakEvent(managerType, sender, e); return ReceiveWeakEvent(managerType, sender, e);
} }
int maxLineNumberLength = 1; /// <summary>
/// Maximum length of a line number, in characters
/// </summary>
protected int maxLineNumberLength = 1;
void OnDocumentLineCountChanged() void OnDocumentLineCountChanged()
{ {
@ -164,6 +191,7 @@ namespace ICSharpCode.AvalonEdit.Editing
if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift) { if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift) {
ExtendSelection(currentSeg); ExtendSelection(currentSeg);
} }
textArea.Caret.BringCaretToView(5.0);
} }
} }
} }
@ -172,6 +200,7 @@ namespace ICSharpCode.AvalonEdit.Editing
{ {
Point pos = e.GetPosition(TextView); Point pos = e.GetPosition(TextView);
pos.X = 0; pos.X = 0;
pos.Y = pos.Y.CoerceValue(0, TextView.ActualHeight);
pos.Y += TextView.VerticalOffset; pos.Y += TextView.VerticalOffset;
VisualLine vl = TextView.GetVisualLineFromVisualTop(pos.Y); VisualLine vl = TextView.GetVisualLineFromVisualTop(pos.Y);
if (vl == null) if (vl == null)
@ -207,6 +236,7 @@ namespace ICSharpCode.AvalonEdit.Editing
if (currentSeg == SimpleSegment.Invalid) if (currentSeg == SimpleSegment.Invalid)
return; return;
ExtendSelection(currentSeg); ExtendSelection(currentSeg);
textArea.Caret.BringCaretToView(5.0);
} }
base.OnMouseMove(e); base.OnMouseMove(e);
} }

26
AvalonEdit/ICSharpCode.AvalonEdit/Editing/NoReadOnlySections.cs

@ -1,10 +1,26 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.NRefactory.Editor;
using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.AvalonEdit.Utils;
namespace ICSharpCode.AvalonEdit.Editing namespace ICSharpCode.AvalonEdit.Editing
@ -33,9 +49,9 @@ namespace ICSharpCode.AvalonEdit.Editing
/// <summary> /// <summary>
/// <see cref="IReadOnlySectionProvider"/> that completely disables editing. /// <see cref="IReadOnlySectionProvider"/> that completely disables editing.
/// </summary> /// </summary>
sealed class ReadOnlyDocument : IReadOnlySectionProvider sealed class ReadOnlySectionDocument : IReadOnlySectionProvider
{ {
public static readonly ReadOnlyDocument Instance = new ReadOnlyDocument(); public static readonly ReadOnlySectionDocument Instance = new ReadOnlySectionDocument();
public bool CanInsert(int offset) public bool CanInsert(int offset)
{ {

182
AvalonEdit/ICSharpCode.AvalonEdit/Editing/RectangleSelection.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -7,11 +22,16 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Windows; using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media.TextFormatting; using System.Windows.Media.TextFormatting;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.AvalonEdit.Utils;
#if NREFACTORY
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Editor;
#endif
namespace ICSharpCode.AvalonEdit.Editing namespace ICSharpCode.AvalonEdit.Editing
{ {
@ -20,20 +40,70 @@ namespace ICSharpCode.AvalonEdit.Editing
/// </summary> /// </summary>
public sealed class RectangleSelection : Selection public sealed class RectangleSelection : Selection
{ {
#region Commands
/// <summary>
/// Expands the selection left by one character, creating a rectangular selection.
/// Key gesture: Alt+Shift+Left
/// </summary>
public static readonly RoutedUICommand BoxSelectLeftByCharacter = Command("BoxSelectLeftByCharacter");
/// <summary>
/// Expands the selection right by one character, creating a rectangular selection.
/// Key gesture: Alt+Shift+Right
/// </summary>
public static readonly RoutedUICommand BoxSelectRightByCharacter = Command("BoxSelectRightByCharacter");
/// <summary>
/// Expands the selection left by one word, creating a rectangular selection.
/// Key gesture: Ctrl+Alt+Shift+Left
/// </summary>
public static readonly RoutedUICommand BoxSelectLeftByWord = Command("BoxSelectLeftByWord");
/// <summary>
/// Expands the selection left by one word, creating a rectangular selection.
/// Key gesture: Ctrl+Alt+Shift+Right
/// </summary>
public static readonly RoutedUICommand BoxSelectRightByWord = Command("BoxSelectRightByWord");
/// <summary>
/// Expands the selection up by one line, creating a rectangular selection.
/// Key gesture: Alt+Shift+Up
/// </summary>
public static readonly RoutedUICommand BoxSelectUpByLine = Command("BoxSelectUpByLine");
/// <summary>
/// Expands the selection up by one line, creating a rectangular selection.
/// Key gesture: Alt+Shift+Down
/// </summary>
public static readonly RoutedUICommand BoxSelectDownByLine = Command("BoxSelectDownByLine");
/// <summary>
/// Expands the selection to the start of the line, creating a rectangular selection.
/// Key gesture: Alt+Shift+Home
/// </summary>
public static readonly RoutedUICommand BoxSelectToLineStart = Command("BoxSelectToLineStart");
/// <summary>
/// Expands the selection to the end of the line, creating a rectangular selection.
/// Key gesture: Alt+Shift+End
/// </summary>
public static readonly RoutedUICommand BoxSelectToLineEnd = Command("BoxSelectToLineEnd");
static RoutedUICommand Command(string name)
{
return new RoutedUICommand(name, name, typeof(RectangleSelection));
}
#endregion
TextDocument document; TextDocument document;
readonly int startLine, endLine; readonly int startLine, endLine;
readonly double startXPos, endXPos; readonly double startXPos, endXPos;
readonly int topLeftOffset, bottomRightOffset; readonly int topLeftOffset, bottomRightOffset;
readonly TextViewPosition start, end;
readonly List<SelectionSegment> segments = new List<SelectionSegment>(); readonly List<SelectionSegment> segments = new List<SelectionSegment>();
void InitDocument() #region Constructors
{
document = textArea.Document;
if (document == null)
throw ThrowUtil.NoDocumentAssigned();
}
/// <summary> /// <summary>
/// Creates a new rectangular selection. /// Creates a new rectangular selection.
/// </summary> /// </summary>
@ -48,6 +118,9 @@ namespace ICSharpCode.AvalonEdit.Editing
CalculateSegments(); CalculateSegments();
this.topLeftOffset = this.segments.First().StartOffset; this.topLeftOffset = this.segments.First().StartOffset;
this.bottomRightOffset = this.segments.Last().EndOffset; this.bottomRightOffset = this.segments.Last().EndOffset;
this.start = start;
this.end = end;
} }
private RectangleSelection(TextArea textArea, int startLine, double startXPos, TextViewPosition end) private RectangleSelection(TextArea textArea, int startLine, double startXPos, TextViewPosition end)
@ -61,6 +134,9 @@ namespace ICSharpCode.AvalonEdit.Editing
CalculateSegments(); CalculateSegments();
this.topLeftOffset = this.segments.First().StartOffset; this.topLeftOffset = this.segments.First().StartOffset;
this.bottomRightOffset = this.segments.Last().EndOffset; this.bottomRightOffset = this.segments.Last().EndOffset;
this.start = GetStart();
this.end = end;
} }
private RectangleSelection(TextArea textArea, TextViewPosition start, int endLine, double endXPos) private RectangleSelection(TextArea textArea, TextViewPosition start, int endLine, double endXPos)
@ -74,6 +150,16 @@ namespace ICSharpCode.AvalonEdit.Editing
CalculateSegments(); CalculateSegments();
this.topLeftOffset = this.segments.First().StartOffset; this.topLeftOffset = this.segments.First().StartOffset;
this.bottomRightOffset = this.segments.Last().EndOffset; this.bottomRightOffset = this.segments.Last().EndOffset;
this.start = start;
this.end = GetEnd();
}
void InitDocument()
{
document = textArea.Document;
if (document == null)
throw ThrowUtil.NoDocumentAssigned();
} }
static double GetXPos(TextArea textArea, TextViewPosition pos) static double GetXPos(TextArea textArea, TextViewPosition pos)
@ -81,16 +167,48 @@ namespace ICSharpCode.AvalonEdit.Editing
DocumentLine documentLine = textArea.Document.GetLineByNumber(pos.Line); DocumentLine documentLine = textArea.Document.GetLineByNumber(pos.Line);
VisualLine visualLine = textArea.TextView.GetOrConstructVisualLine(documentLine); VisualLine visualLine = textArea.TextView.GetOrConstructVisualLine(documentLine);
int vc = visualLine.ValidateVisualColumn(pos, true); int vc = visualLine.ValidateVisualColumn(pos, true);
TextLine textLine = visualLine.GetTextLine(vc); TextLine textLine = visualLine.GetTextLine(vc, pos.IsAtEndOfLine);
return visualLine.GetTextLineVisualXPosition(textLine, vc); return visualLine.GetTextLineVisualXPosition(textLine, vc);
} }
int GetVisualColumnFromXPos(int line, double xPos) void CalculateSegments()
{ {
var vl = textArea.TextView.GetOrConstructVisualLine(textArea.Document.GetLineByNumber(line)); DocumentLine nextLine = document.GetLineByNumber(Math.Min(startLine, endLine));
return vl.GetVisualColumn(new Point(xPos, 0), true); do {
VisualLine vl = textArea.TextView.GetOrConstructVisualLine(nextLine);
int startVC = vl.GetVisualColumn(new Point(startXPos, 0), true);
int endVC = vl.GetVisualColumn(new Point(endXPos, 0), true);
int baseOffset = vl.FirstDocumentLine.Offset;
int startOffset = baseOffset + vl.GetRelativeOffset(startVC);
int endOffset = baseOffset + vl.GetRelativeOffset(endVC);
segments.Add(new SelectionSegment(startOffset, startVC, endOffset, endVC));
nextLine = vl.LastDocumentLine.NextLine;
} while (nextLine != null && nextLine.LineNumber <= Math.Max(startLine, endLine));
}
TextViewPosition GetStart()
{
SelectionSegment segment = (startLine < endLine ? segments.First() : segments.Last());
if (startXPos < endXPos) {
return new TextViewPosition(document.GetLocation(segment.StartOffset), segment.StartVisualColumn);
} else {
return new TextViewPosition(document.GetLocation(segment.EndOffset), segment.EndVisualColumn);
}
} }
TextViewPosition GetEnd()
{
SelectionSegment segment = (startLine < endLine ? segments.Last() : segments.First());
if (startXPos < endXPos) {
return new TextViewPosition(document.GetLocation(segment.EndOffset), segment.EndVisualColumn);
} else {
return new TextViewPosition(document.GetLocation(segment.StartOffset), segment.StartVisualColumn);
}
}
#endregion
/// <inheritdoc/> /// <inheritdoc/>
public override string GetText() public override string GetText()
{ {
@ -133,22 +251,14 @@ namespace ICSharpCode.AvalonEdit.Editing
get { return segments; } get { return segments; }
} }
/// <inheritdoc/>
public override TextViewPosition StartPosition {
get { return start; }
}
void CalculateSegments() /// <inheritdoc/>
{ public override TextViewPosition EndPosition {
DocumentLine nextLine = document.GetLineByNumber(Math.Min(startLine, endLine)); get { return end; }
do {
VisualLine vl = textArea.TextView.GetOrConstructVisualLine(nextLine);
int startVC = vl.GetVisualColumn(new Point(startXPos, 0), true);
int endVC = vl.GetVisualColumn(new Point(endXPos, 0), true);
int baseOffset = vl.FirstDocumentLine.Offset;
int startOffset = baseOffset + vl.GetRelativeOffset(startVC);
int endOffset = baseOffset + vl.GetRelativeOffset(endVC);
segments.Add(new SelectionSegment(startOffset, startVC, endOffset, endVC));
nextLine = vl.LastDocumentLine.NextLine;
} while (nextLine != null && nextLine.LineNumber <= Math.Max(startLine, endLine));
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -173,6 +283,12 @@ namespace ICSharpCode.AvalonEdit.Editing
return new RectangleSelection(textArea, startLine, startXPos, endPosition); return new RectangleSelection(textArea, startLine, startXPos, endPosition);
} }
int GetVisualColumnFromXPos(int line, double xPos)
{
var vl = textArea.TextView.GetOrConstructVisualLine(textArea.Document.GetLineByNumber(line));
return vl.GetVisualColumn(new Point(xPos, 0), true);
}
/// <inheritdoc/> /// <inheritdoc/>
public override Selection UpdateOnDocumentChange(DocumentChangeEventArgs e) public override Selection UpdateOnDocumentChange(DocumentChangeEventArgs e)
{ {
@ -282,9 +398,11 @@ namespace ICSharpCode.AvalonEdit.Editing
{ {
var data = base.CreateDataObject(textArea); var data = base.CreateDataObject(textArea);
MemoryStream isRectangle = new MemoryStream(1); if (EditingCommandHandler.ConfirmDataFormat(textArea, data, RectangularSelectionDataType)) {
isRectangle.WriteByte(1); MemoryStream isRectangle = new MemoryStream(1);
data.SetData(RectangularSelectionDataType, isRectangle, false); isRectangle.WriteByte(1);
data.SetData(RectangularSelectionDataType, isRectangle, false);
}
return data; return data;
} }

64
AvalonEdit/ICSharpCode.AvalonEdit/Editing/Selection.cs

@ -1,13 +1,32 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Windows; using System.Windows;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.AvalonEdit.Utils;
#if NREFACTORY
using ICSharpCode.NRefactory.Editor;
#endif
namespace ICSharpCode.AvalonEdit.Editing namespace ICSharpCode.AvalonEdit.Editing
{ {
@ -63,6 +82,16 @@ namespace ICSharpCode.AvalonEdit.Editing
this.textArea = textArea; this.textArea = textArea;
} }
/// <summary>
/// Gets the start position of the selection.
/// </summary>
public abstract TextViewPosition StartPosition { get; }
/// <summary>
/// Gets the end position of the selection.
/// </summary>
public abstract TextViewPosition EndPosition { get; }
/// <summary> /// <summary>
/// Gets the selected text segments. /// Gets the selected text segments.
/// </summary> /// </summary>
@ -104,7 +133,7 @@ namespace ICSharpCode.AvalonEdit.Editing
{ {
return (!string.IsNullOrEmpty(newText) || !(IsInVirtualSpace(start) && IsInVirtualSpace(end))) return (!string.IsNullOrEmpty(newText) || !(IsInVirtualSpace(start) && IsInVirtualSpace(end)))
&& newText != "\r\n" && newText != "\r\n"
&& newText != "\n" && newText != "\n"
&& newText != "\r"; && newText != "\r";
} }
@ -228,9 +257,9 @@ namespace ICSharpCode.AvalonEdit.Editing
{ {
if (this.IsEmpty) if (this.IsEmpty)
return false; return false;
if (this.SurroundingSegment.Contains(offset)) { if (this.SurroundingSegment.Contains(offset, 0)) {
foreach (ISegment s in this.Segments) { foreach (ISegment s in this.Segments) {
if (s.Contains(offset)) { if (s.Contains(offset, 0)) {
return true; return true;
} }
} }
@ -243,15 +272,30 @@ namespace ICSharpCode.AvalonEdit.Editing
/// </summary> /// </summary>
public virtual DataObject CreateDataObject(TextArea textArea) public virtual DataObject CreateDataObject(TextArea textArea)
{ {
string text = GetText(); DataObject data = new DataObject();
// Ensure we use the appropriate newline sequence for the OS // Ensure we use the appropriate newline sequence for the OS
DataObject data = new DataObject(TextUtilities.NormalizeNewLines(text, Environment.NewLine)); string text = TextUtilities.NormalizeNewLines(GetText(), Environment.NewLine);
// we cannot use DataObject.SetText - then we cannot drag to SciTe
// (but dragging to Word works in both cases) // Enable drag/drop to Word, Notepad++ and others
if (EditingCommandHandler.ConfirmDataFormat(textArea, data, DataFormats.UnicodeText)) {
data.SetText(text);
}
// Enable drag/drop to SciTe:
// We cannot use SetText, thus we need to use typeof(string).FullName as data format.
// new DataObject(object) calls SetData(object), which in turn calls SetData(Type, data),
// which then uses Type.FullName as format.
// We immitate that behavior here as well:
if (EditingCommandHandler.ConfirmDataFormat(textArea, data, typeof(string).FullName)) {
data.SetData(typeof(string).FullName, text);
}
// Also copy text in HTML format to clipboard - good for pasting text into Word // Also copy text in HTML format to clipboard - good for pasting text into Word
// or to the SharpDevelop forums. // or to the SharpDevelop forums.
HtmlClipboard.SetHtml(data, CreateHtmlFragment(new HtmlOptions(textArea.Options))); if (EditingCommandHandler.ConfirmDataFormat(textArea, data, DataFormats.Html)) {
HtmlClipboard.SetHtml(data, CreateHtmlFragment(new HtmlOptions(textArea.Options)));
}
return data; return data;
} }
} }

24
AvalonEdit/ICSharpCode.AvalonEdit/Editing/SelectionColorizer.cs

@ -1,10 +1,26 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Windows;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.AvalonEdit.Rendering;
#if NREFACTORY
using ICSharpCode.NRefactory.Editor;
#endif
namespace ICSharpCode.AvalonEdit.Editing namespace ICSharpCode.AvalonEdit.Editing
{ {

19
AvalonEdit/ICSharpCode.AvalonEdit/Editing/SelectionLayer.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Windows; using System.Windows;

77
AvalonEdit/ICSharpCode.AvalonEdit/Editing/SelectionMouseHandler.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.ComponentModel; using System.ComponentModel;
@ -14,6 +29,9 @@ using System.Windows.Threading;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.AvalonEdit.Utils;
#if NREFACTORY
using ICSharpCode.NRefactory.Editor;
#endif
namespace ICSharpCode.AvalonEdit.Editing namespace ICSharpCode.AvalonEdit.Editing
{ {
@ -166,9 +184,10 @@ namespace ICSharpCode.AvalonEdit.Editing
if (e.Data.GetDataPresent(DataFormats.UnicodeText, true)) { if (e.Data.GetDataPresent(DataFormats.UnicodeText, true)) {
e.Handled = true; e.Handled = true;
int visualColumn; int visualColumn;
int offset = GetOffsetFromMousePosition(e.GetPosition(textArea.TextView), out visualColumn); bool isAtEndOfLine;
int offset = GetOffsetFromMousePosition(e.GetPosition(textArea.TextView), out visualColumn, out isAtEndOfLine);
if (offset >= 0) { if (offset >= 0) {
textArea.Caret.Position = new TextViewPosition(textArea.Document.GetLocation(offset), visualColumn); textArea.Caret.Position = new TextViewPosition(textArea.Document.GetLocation(offset), visualColumn) { IsAtEndOfLine = isAtEndOfLine };
textArea.Caret.DesiredXPos = double.NaN; textArea.Caret.DesiredXPos = double.NaN;
if (textArea.ReadOnlySectionProvider.CanInsert(offset)) { if (textArea.ReadOnlySectionProvider.CanInsert(offset)) {
if ((e.AllowedEffects & DragDropEffects.Move) == DragDropEffects.Move if ((e.AllowedEffects & DragDropEffects.Move) == DragDropEffects.Move
@ -217,6 +236,21 @@ namespace ICSharpCode.AvalonEdit.Editing
string newLine = TextUtilities.GetNewLineFromDocument(textArea.Document, textArea.Caret.Line); string newLine = TextUtilities.GetNewLineFromDocument(textArea.Document, textArea.Caret.Line);
text = TextUtilities.NormalizeNewLines(text, newLine); text = TextUtilities.NormalizeNewLines(text, newLine);
string pasteFormat;
// fill the suggested DataFormat used for the paste action:
if (rectangular)
pasteFormat = RectangleSelection.RectangularSelectionDataType;
else
pasteFormat = DataFormats.UnicodeText;
var pastingEventArgs = new DataObjectPastingEventArgs(e.Data, true, pasteFormat);
textArea.RaiseEvent(pastingEventArgs);
if (pastingEventArgs.CommandCancelled)
return;
// DataObject.PastingEvent handlers might have changed the format to apply.
rectangular = pastingEventArgs.FormatToApply == RectangleSelection.RectangularSelectionDataType;
// Mark the undo group with the currentDragDescriptor, if the drag // Mark the undo group with the currentDragDescriptor, if the drag
// is originating from the same control. This allows combining // is originating from the same control. This allows combining
// the undo groups when text is moved. // the undo groups when text is moved.
@ -246,7 +280,7 @@ namespace ICSharpCode.AvalonEdit.Editing
// we re-throw them later to allow the application's unhandled exception handler // we re-throw them later to allow the application's unhandled exception handler
// to catch them // to catch them
textArea.Dispatcher.BeginInvoke( textArea.Dispatcher.BeginInvoke(
DispatcherPriority.Normal, DispatcherPriority.Send,
new Action(delegate { new Action(delegate {
throw new DragDropException("Exception during drag'n'drop", ex); throw new DragDropException("Exception during drag'n'drop", ex);
})); }));
@ -303,6 +337,11 @@ namespace ICSharpCode.AvalonEdit.Editing
} }
} }
var copyingEventArgs = new DataObjectCopyingEventArgs(dataObject, true);
textArea.RaiseEvent(copyingEventArgs);
if (copyingEventArgs.CommandCancelled)
return;
object dragDescriptor = new object(); object dragDescriptor = new object();
this.currentDragDescriptor = dragDescriptor; this.currentDragDescriptor = dragDescriptor;
@ -359,7 +398,8 @@ namespace ICSharpCode.AvalonEdit.Editing
Point p = e.GetPosition(textArea.TextView); Point p = e.GetPosition(textArea.TextView);
if (p.X >= 0 && p.Y >= 0 && p.X <= textArea.TextView.ActualWidth && p.Y <= textArea.TextView.ActualHeight) { if (p.X >= 0 && p.Y >= 0 && p.X <= textArea.TextView.ActualWidth && p.Y <= textArea.TextView.ActualHeight) {
int visualColumn; int visualColumn;
int offset = GetOffsetFromMousePosition(e, out visualColumn); bool isAtEndOfLine;
int offset = GetOffsetFromMousePosition(e, out visualColumn, out isAtEndOfLine);
if (textArea.Selection.Contains(offset)) if (textArea.Selection.Contains(offset))
e.Cursor = Cursors.Arrow; e.Cursor = Cursors.Arrow;
else else
@ -380,7 +420,8 @@ namespace ICSharpCode.AvalonEdit.Editing
bool shift = (modifiers & ModifierKeys.Shift) == ModifierKeys.Shift; bool shift = (modifiers & ModifierKeys.Shift) == ModifierKeys.Shift;
if (enableTextDragDrop && e.ClickCount == 1 && !shift) { if (enableTextDragDrop && e.ClickCount == 1 && !shift) {
int visualColumn; int visualColumn;
int offset = GetOffsetFromMousePosition(e, out visualColumn); bool isAtEndOfLine;
int offset = GetOffsetFromMousePosition(e, out visualColumn, out isAtEndOfLine);
if (textArea.Selection.Contains(offset)) { if (textArea.Selection.Contains(offset)) {
if (textArea.CaptureMouse()) { if (textArea.CaptureMouse()) {
mode = SelectionMode.PossibleDragStart; mode = SelectionMode.PossibleDragStart;
@ -488,12 +529,12 @@ namespace ICSharpCode.AvalonEdit.Editing
} }
} }
int GetOffsetFromMousePosition(MouseEventArgs e, out int visualColumn) int GetOffsetFromMousePosition(MouseEventArgs e, out int visualColumn, out bool isAtEndOfLine)
{ {
return GetOffsetFromMousePosition(e.GetPosition(textArea.TextView), out visualColumn); return GetOffsetFromMousePosition(e.GetPosition(textArea.TextView), out visualColumn, out isAtEndOfLine);
} }
int GetOffsetFromMousePosition(Point positionRelativeToTextView, out int visualColumn) int GetOffsetFromMousePosition(Point positionRelativeToTextView, out int visualColumn, out bool isAtEndOfLine)
{ {
visualColumn = 0; visualColumn = 0;
TextView textView = textArea.TextView; TextView textView = textArea.TextView;
@ -507,9 +548,10 @@ namespace ICSharpCode.AvalonEdit.Editing
pos.Y = textView.DocumentHeight - ExtensionMethods.Epsilon; pos.Y = textView.DocumentHeight - ExtensionMethods.Epsilon;
VisualLine line = textView.GetVisualLineFromVisualTop(pos.Y); VisualLine line = textView.GetVisualLineFromVisualTop(pos.Y);
if (line != null) { if (line != null) {
visualColumn = line.GetVisualColumn(pos, textArea.Selection.EnableVirtualSpace); visualColumn = line.GetVisualColumn(pos, textArea.Selection.EnableVirtualSpace, out isAtEndOfLine);
return line.GetRelativeOffset(visualColumn) + line.FirstDocumentLine.Offset; return line.GetRelativeOffset(visualColumn) + line.FirstDocumentLine.Offset;
} }
isAtEndOfLine = false;
return -1; return -1;
} }
@ -527,7 +569,7 @@ namespace ICSharpCode.AvalonEdit.Editing
pos.Y = textView.DocumentHeight - ExtensionMethods.Epsilon; pos.Y = textView.DocumentHeight - ExtensionMethods.Epsilon;
VisualLine line = textView.GetVisualLineFromVisualTop(pos.Y); VisualLine line = textView.GetVisualLineFromVisualTop(pos.Y);
if (line != null) { if (line != null) {
visualColumn = line.GetVisualColumn(line.TextLines.First(), positionRelativeToTextView.X, textArea.Selection.EnableVirtualSpace); visualColumn = line.GetVisualColumn(line.TextLines.First(), pos.X, textArea.Selection.EnableVirtualSpace);
return line.GetRelativeOffset(visualColumn) + line.FirstDocumentLine.Offset; return line.GetRelativeOffset(visualColumn) + line.FirstDocumentLine.Offset;
} }
return -1; return -1;
@ -568,16 +610,19 @@ namespace ICSharpCode.AvalonEdit.Editing
void SetCaretOffsetToMousePosition(MouseEventArgs e, ISegment allowedSegment) void SetCaretOffsetToMousePosition(MouseEventArgs e, ISegment allowedSegment)
{ {
int visualColumn; int visualColumn;
bool isAtEndOfLine;
int offset; int offset;
if (mode == SelectionMode.Rectangular) if (mode == SelectionMode.Rectangular) {
offset = GetOffsetFromMousePositionFirstTextLineOnly(e.GetPosition(textArea.TextView), out visualColumn); offset = GetOffsetFromMousePositionFirstTextLineOnly(e.GetPosition(textArea.TextView), out visualColumn);
else isAtEndOfLine = true;
offset = GetOffsetFromMousePosition(e, out visualColumn); } else {
offset = GetOffsetFromMousePosition(e, out visualColumn, out isAtEndOfLine);
}
if (allowedSegment != null) { if (allowedSegment != null) {
offset = offset.CoerceValue(allowedSegment.Offset, allowedSegment.EndOffset); offset = offset.CoerceValue(allowedSegment.Offset, allowedSegment.EndOffset);
} }
if (offset >= 0) { if (offset >= 0) {
textArea.Caret.Position = new TextViewPosition(textArea.Document.GetLocation(offset), visualColumn); textArea.Caret.Position = new TextViewPosition(textArea.Document.GetLocation(offset), visualColumn) { IsAtEndOfLine = isAtEndOfLine };
textArea.Caret.DesiredXPos = double.NaN; textArea.Caret.DesiredXPos = double.NaN;
} }
} }

23
AvalonEdit/ICSharpCode.AvalonEdit/Editing/SelectionSegment.cs

@ -1,8 +1,27 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
#if NREFACTORY
using ICSharpCode.NRefactory.Editor;
#else
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
#endif
namespace ICSharpCode.AvalonEdit.Editing namespace ICSharpCode.AvalonEdit.Editing
{ {

66
AvalonEdit/ICSharpCode.AvalonEdit/Editing/SimpleSelection.cs

@ -1,12 +1,28 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.AvalonEdit.Utils;
#if NREFACTORY
using ICSharpCode.NRefactory.Editor;
#endif
namespace ICSharpCode.AvalonEdit.Editing namespace ICSharpCode.AvalonEdit.Editing
{ {
@ -56,10 +72,16 @@ namespace ICSharpCode.AvalonEdit.Editing
if (segmentsToDelete[i].Offset == SurroundingSegment.Offset && segmentsToDelete[i].Length == SurroundingSegment.Length) { if (segmentsToDelete[i].Offset == SurroundingSegment.Offset && segmentsToDelete[i].Length == SurroundingSegment.Length) {
newText = AddSpacesIfRequired(newText, start, end); newText = AddSpacesIfRequired(newText, start, end);
} }
int vc = textArea.Caret.VisualColumn; if (string.IsNullOrEmpty(newText)) {
textArea.Caret.Offset = segmentsToDelete[i].EndOffset; // place caret at the beginning of the selection
if (string.IsNullOrEmpty(newText)) if (start.CompareTo(end) <= 0)
textArea.Caret.VisualColumn = vc; textArea.Caret.Position = start;
else
textArea.Caret.Position = end;
} else {
// place caret so that it ends up behind the new text
textArea.Caret.Offset = segmentsToDelete[i].EndOffset;
}
textArea.Document.Replace(segmentsToDelete[i], newText); textArea.Document.Replace(segmentsToDelete[i], newText);
} else { } else {
textArea.Document.Remove(segmentsToDelete[i]); textArea.Document.Remove(segmentsToDelete[i]);
@ -71,18 +93,12 @@ namespace ICSharpCode.AvalonEdit.Editing
} }
} }
/// <summary> public override TextViewPosition StartPosition {
/// Gets the start offset. get { return start; }
/// </summary>
public int StartOffset {
get { return startOffset; }
} }
/// <summary> public override TextViewPosition EndPosition {
/// Gets the end offset. get { return end; }
/// </summary>
public int EndOffset {
get { return endOffset; }
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -90,16 +106,24 @@ namespace ICSharpCode.AvalonEdit.Editing
{ {
if (e == null) if (e == null)
throw new ArgumentNullException("e"); throw new ArgumentNullException("e");
int newStartOffset, newEndOffset;
if (startOffset <= endOffset) {
newStartOffset = e.GetNewOffset(startOffset, AnchorMovementType.Default);
newEndOffset = Math.Max(newStartOffset, e.GetNewOffset(endOffset, AnchorMovementType.BeforeInsertion));
} else {
newEndOffset = e.GetNewOffset(endOffset, AnchorMovementType.Default);
newStartOffset = Math.Max(newEndOffset, e.GetNewOffset(startOffset, AnchorMovementType.BeforeInsertion));
}
return Selection.Create( return Selection.Create(
textArea, textArea,
new TextViewPosition(textArea.Document.GetLocation(e.GetNewOffset(startOffset, AnchorMovementType.Default)), start.VisualColumn), new TextViewPosition(textArea.Document.GetLocation(newStartOffset), start.VisualColumn),
new TextViewPosition(textArea.Document.GetLocation(e.GetNewOffset(endOffset, AnchorMovementType.Default)), end.VisualColumn) new TextViewPosition(textArea.Document.GetLocation(newEndOffset), end.VisualColumn)
); );
} }
/// <inheritdoc/> /// <inheritdoc/>
public override bool IsEmpty { public override bool IsEmpty {
get { return startOffset == endOffset; } get { return startOffset == endOffset && start.VisualColumn == end.VisualColumn; }
} }
/// <inheritdoc/> /// <inheritdoc/>

117
AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextArea.cs

@ -1,8 +1,22 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.ComponentModel; using System.ComponentModel;
@ -11,16 +25,16 @@ using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Controls.Primitives; using System.Windows.Controls.Primitives;
using System.Windows.Data; using System.Windows.Documents;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading; using System.Windows.Threading;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Indentation; using ICSharpCode.AvalonEdit.Indentation;
using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.AvalonEdit.Utils;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Editor;
namespace ICSharpCode.AvalonEdit.Editing namespace ICSharpCode.AvalonEdit.Editing
{ {
@ -29,6 +43,8 @@ namespace ICSharpCode.AvalonEdit.Editing
/// </summary> /// </summary>
public class TextArea : Control, IScrollInfo, IWeakEventListener, ITextEditorComponent, IServiceProvider public class TextArea : Control, IScrollInfo, IWeakEventListener, ITextEditorComponent, IServiceProvider
{ {
internal readonly ImeSupport ime;
#region Constructor #region Constructor
static TextArea() static TextArea()
{ {
@ -68,6 +84,9 @@ namespace ICSharpCode.AvalonEdit.Editing
caret = new Caret(this); caret = new Caret(this);
caret.PositionChanged += (sender, e) => RequestSelectionValidation(); caret.PositionChanged += (sender, e) => RequestSelectionValidation();
caret.PositionChanged += CaretPositionChanged;
AttachTypingEvents();
ime = new ImeSupport(this);
leftMargins.CollectionChanged += leftMargins_CollectionChanged; leftMargins.CollectionChanged += leftMargins_CollectionChanged;
@ -557,6 +576,14 @@ namespace ICSharpCode.AvalonEdit.Editing
get { return caret; } get { return caret; }
} }
void CaretPositionChanged(object sender, EventArgs e)
{
if (textView == null)
return;
this.textView.HighlightedLine = this.Caret.Line;
}
ObservableCollection<UIElement> leftMargins = new ObservableCollection<UIElement>(); ObservableCollection<UIElement> leftMargins = new ObservableCollection<UIElement>();
/// <summary> /// <summary>
@ -593,6 +620,7 @@ namespace ICSharpCode.AvalonEdit.Editing
if (value == null) if (value == null)
throw new ArgumentNullException("value"); throw new ArgumentNullException("value");
readOnlySectionProvider = value; readOnlySectionProvider = value;
CommandManager.InvalidateRequerySuggested(); // the read-only status effects Paste.CanExecute and the IME
} }
} }
#endregion #endregion
@ -754,6 +782,8 @@ namespace ICSharpCode.AvalonEdit.Editing
protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e) protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
{ {
base.OnGotKeyboardFocus(e); base.OnGotKeyboardFocus(e);
// First activate IME, then show caret
ime.OnGotKeyboardFocus(e);
caret.Show(); caret.Show();
} }
@ -762,6 +792,7 @@ namespace ICSharpCode.AvalonEdit.Editing
{ {
base.OnLostKeyboardFocus(e); base.OnLostKeyboardFocus(e);
caret.Hide(); caret.Hide();
ime.OnLostKeyboardFocus(e);
} }
#endregion #endregion
@ -817,6 +848,7 @@ namespace ICSharpCode.AvalonEdit.Editing
// We have to ignore those (not handle them) to keep the shortcut working. // We have to ignore those (not handle them) to keep the shortcut working.
return; return;
} }
HideMouseCursor();
PerformTextInput(e); PerformTextInput(e);
e.Handled = true; e.Handled = true;
} }
@ -850,8 +882,11 @@ namespace ICSharpCode.AvalonEdit.Editing
if (!e.Handled) { if (!e.Handled) {
if (e.Text == "\n" || e.Text == "\r" || e.Text == "\r\n") if (e.Text == "\n" || e.Text == "\r" || e.Text == "\r\n")
ReplaceSelectionWithNewLine(); ReplaceSelectionWithNewLine();
else else {
if (OverstrikeMode && Selection.IsEmpty && Document.GetLineByNumber(Caret.Line).EndOffset > Caret.Offset)
EditingCommands.SelectRightByCharacter.Execute(null, this);
ReplaceSelectionWithText(e.Text); ReplaceSelectionWithText(e.Text);
}
OnTextEntered(e); OnTextEntered(e);
caret.BringCaretToView(); caret.BringCaretToView();
} }
@ -936,6 +971,13 @@ namespace ICSharpCode.AvalonEdit.Editing
protected override void OnPreviewKeyDown(KeyEventArgs e) protected override void OnPreviewKeyDown(KeyEventArgs e)
{ {
base.OnPreviewKeyDown(e); base.OnPreviewKeyDown(e);
if (!e.Handled && e.Key == Key.Insert && this.Options.AllowToggleOverstrikeMode) {
this.OverstrikeMode = !this.OverstrikeMode;
e.Handled = true;
return;
}
foreach (TextAreaStackedInputHandler h in stackedInputHandlers) { foreach (TextAreaStackedInputHandler h in stackedInputHandlers) {
if (e.Handled) if (e.Handled)
break; break;
@ -959,15 +1001,68 @@ namespace ICSharpCode.AvalonEdit.Editing
protected override void OnKeyDown(KeyEventArgs e) protected override void OnKeyDown(KeyEventArgs e)
{ {
base.OnKeyDown(e); base.OnKeyDown(e);
TextView.InvalidateCursor(); TextView.InvalidateCursorIfMouseWithinTextView();
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnKeyUp(KeyEventArgs e) protected override void OnKeyUp(KeyEventArgs e)
{ {
base.OnKeyUp(e); base.OnKeyUp(e);
TextView.InvalidateCursor(); TextView.InvalidateCursorIfMouseWithinTextView();
}
#endregion
#region Hide Mouse Cursor While Typing
bool isMouseCursorHidden;
void AttachTypingEvents()
{
// Use the PreviewMouseMove event in case some other editor layer consumes the MouseMove event (e.g. SD's InsertionCursorLayer)
this.MouseEnter += delegate { ShowMouseCursor(); };
this.MouseLeave += delegate { ShowMouseCursor(); };
this.PreviewMouseMove += delegate { ShowMouseCursor(); };
#if DOTNET4
this.TouchEnter += delegate { ShowMouseCursor(); };
this.TouchLeave += delegate { ShowMouseCursor(); };
this.PreviewTouchMove += delegate { ShowMouseCursor(); };
#endif
}
void ShowMouseCursor()
{
if (this.isMouseCursorHidden) {
System.Windows.Forms.Cursor.Show();
this.isMouseCursorHidden = false;
}
} }
void HideMouseCursor() {
if (Options.HideCursorWhileTyping && !this.isMouseCursorHidden && this.IsMouseOver) {
this.isMouseCursorHidden = true;
System.Windows.Forms.Cursor.Hide();
}
}
#endregion
#region Overstrike mode
/// <summary>
/// The <see cref="OverstrikeMode"/> dependency property.
/// </summary>
public static readonly DependencyProperty OverstrikeModeProperty =
DependencyProperty.Register("OverstrikeMode", typeof(bool), typeof(TextArea),
new FrameworkPropertyMetadata(Boxes.False));
/// <summary>
/// Gets/Sets whether overstrike mode is active.
/// </summary>
public bool OverstrikeMode {
get { return (bool)GetValue(OverstrikeModeProperty); }
set { SetValue(OverstrikeModeProperty, value); }
}
#endregion #endregion
/// <inheritdoc/> /// <inheritdoc/>
@ -987,6 +1082,8 @@ namespace ICSharpCode.AvalonEdit.Editing
|| e.Property == SelectionCornerRadiusProperty) || e.Property == SelectionCornerRadiusProperty)
{ {
textView.Redraw(); textView.Redraw();
} else if (e.Property == OverstrikeModeProperty) {
caret.UpdateIfVisible();
} }
} }
@ -996,7 +1093,7 @@ namespace ICSharpCode.AvalonEdit.Editing
/// <returns>Returns the requested service instance, or null if the service cannot be found.</returns> /// <returns>Returns the requested service instance, or null if the service cannot be found.</returns>
public virtual object GetService(Type serviceType) public virtual object GetService(Type serviceType)
{ {
return textView.Services.GetService(serviceType); return textView.GetService(serviceType);
} }
/// <summary> /// <summary>

21
AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextAreaDefaultInputHandlers.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -59,7 +74,7 @@ namespace ICSharpCode.AvalonEdit.Editing
// Work around WPF memory leak: // Work around WPF memory leak:
// KeyBinding retains a reference to whichever UIElement it is used in first. // KeyBinding retains a reference to whichever UIElement it is used in first.
// Using a dummy element for this purpose ensures that we don't leak // Using a dummy element for this purpose ensures that we don't leak
// a real text editor (which a potentially large document). // a real text editor (with a potentially large document).
UIElement dummyElement = new UIElement(); UIElement dummyElement = new UIElement();
dummyElement.InputBindings.AddRange(inputBindings); dummyElement.InputBindings.AddRange(inputBindings);
} }

19
AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextAreaInputHandler.cs

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.AvalonEdit.Utils;
using System; using System;

27
AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextSegmentReadOnlySectionProvider.cs

@ -1,9 +1,27 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
#if NREFACTORY
using ICSharpCode.NRefactory.Editor;
#endif
namespace ICSharpCode.AvalonEdit.Editing namespace ICSharpCode.AvalonEdit.Editing
{ {
@ -60,6 +78,11 @@ namespace ICSharpCode.AvalonEdit.Editing
if (segment == null) if (segment == null)
throw new ArgumentNullException("segment"); throw new ArgumentNullException("segment");
if (segment.Length == 0 && CanInsert(segment.Offset)) {
yield return segment;
yield break;
}
int readonlyUntil = segment.Offset; int readonlyUntil = segment.Offset;
foreach (TextSegment ts in segments.FindOverlappingSegments(segment)) { foreach (TextSegment ts in segments.FindOverlappingSegments(segment)) {
int start = ts.StartOffset; int start = ts.StartOffset;

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save