Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/monodevelop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'main/src/core/Mono.Texteditor')
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor.Highlighting/ColorScheme.cs338
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor.Highlighting/SyntaxMode.cs5
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor.Theatrics/BounceFadePopupWindow.cs12
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor.Utils/CharRope.cs226
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor.Utils/ImmutableStack.cs132
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor.Utils/Rope.cs854
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor.Utils/RopeNode.cs621
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor.Utils/RopeTextReader.cs120
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor.Utils/TextBreaker.cs2
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor.Vi/NewViEditMode.cs6
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor.Vi/ViEditor.cs2
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor.Vi/ViMode.cs4
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor.Vi/ViStatusArea.cs8
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor.csproj12
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/Actions/DeleteActions.cs46
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/Actions/MiscActions.cs62
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/BookmarkMarker.cs6
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/Document/BufferedTextReader.cs93
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/Document/DiffTracker.cs2
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/Document/DocumentUpdateRequest.cs10
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/Document/GapBuffer.cs599
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/Document/IBuffer.cs131
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/Document/ImmutableLineSplitter.cs179
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/Document/StringBuffer.cs70
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/Document/TextDocument.cs133
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/EditMode.cs12
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/ActionMargin.cs4
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/CodeSegmentEditorWindow.cs4
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/CodeSegmentPreviewWindow.cs6
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/DashedLineMargin.cs4
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/FoldMarkerMargin.cs4
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/FoldingScreenbackgroundRenderer.cs4
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/GtkWorkarounds.cs2
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/GutterMargin.cs4
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/HslColor.cs2
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/ITooltipProvider.cs10
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/IconMargin.cs8
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/Margin.cs6
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/MonoTextEditor.cs (renamed from main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/TextEditor.cs)26
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/TextArea.cs57
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/TextViewMargin.cs29
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/TooltipProvider.cs14
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/HeightTree.cs4
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/HelperMethods.cs5
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/ITextEditorOptions.cs34
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/InsertionCursorEditMode.cs10
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/LineBackgroundMarker.cs4
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/MarginMarker.cs10
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/TextEditorData.cs54
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/TextEditorOptions.cs41
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/TextLineMarker.cs23
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/TextLinkEditMode.cs16
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/TextSegmentMarker.cs18
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/UnderlineMarker.cs5
-rw-r--r--main/src/core/Mono.Texteditor/Mono.TextEditor/UrlMarker.cs3
55 files changed, 2809 insertions, 1287 deletions
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor.Highlighting/ColorScheme.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor.Highlighting/ColorScheme.cs
index 79dc77fd34..b2a0451ba3 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor.Highlighting/ColorScheme.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor.Highlighting/ColorScheme.cs
@@ -36,7 +36,7 @@ using Xwt.Drawing;
namespace Mono.TextEditor.Highlighting
{
- public class ColorScheme
+ public sealed class ColorScheme
{
public string Name { get; set; }
public string Description { get; set; }
@@ -202,335 +202,437 @@ namespace Mono.TextEditor.Highlighting
#endregion
#region Text Colors
-
- [ColorDescription("Plain Text", VSSetting = "Plain Text")]
+
+ public const string PlainTextKey = "Plain Text";
+
+ [ColorDescription(PlainTextKey, VSSetting = "Plain Text")]
public ChunkStyle PlainText { get; private set; }
- [ColorDescription("Selected Text", VSSetting = "Selected Text")]
+ public const string SelectedTextKey = "Selected Text";
+ [ColorDescription(SelectedTextKey, VSSetting = "Selected Text")]
public ChunkStyle SelectedText { get; private set; }
- [ColorDescription("Selected Text(Inactive)", VSSetting = "Inactive Selected Text")]
+ public const string SelectedInactiveTextKey = "Selected Text(Inactive)";
+ [ColorDescription(SelectedInactiveTextKey, VSSetting = "Inactive Selected Text")]
public ChunkStyle SelectedInactiveText { get; private set; }
- [ColorDescription("Collapsed Text", VSSetting = "Collapsible Text")]
+ public const string CollapsedTextKey = "Collapsed Text";
+ [ColorDescription(CollapsedTextKey, VSSetting = "Collapsible Text")]
public ChunkStyle CollapsedText { get; private set; }
- [ColorDescription("Line Numbers", VSSetting = "Line Numbers")]
+ public const string LineNumbersKey = "Line Numbers";
+ [ColorDescription(LineNumbersKey, VSSetting = "Line Numbers")]
public ChunkStyle LineNumbers { get; private set; }
- [ColorDescription("Punctuation", VSSetting = "Operator")]
+ public const string PunctuationKey = "Punctuation";
+ [ColorDescription(PunctuationKey, VSSetting = "Operator")]
public ChunkStyle Punctuation { get; private set; }
- [ColorDescription("Punctuation(Brackets)", VSSetting = "Plain Text")]
+ public const string PunctuationForBracketsKey = "Punctuation(Brackets)";
+ [ColorDescription(PunctuationForBracketsKey, VSSetting = "Plain Text")]
public ChunkStyle PunctuationForBrackets { get; private set; }
- [ColorDescription("Comment(Line)", VSSetting = "Comment")]
+ public const string CommentsSingleLineKey = "Comment(Line)";
+ [ColorDescription(CommentsSingleLineKey, VSSetting = "Comment")]
public ChunkStyle CommentsSingleLine { get; private set; }
- [ColorDescription("Comment(Block)", VSSetting = "Comment")]
- public ChunkStyle CommentsMultiLine { get; private set; }
+ public const string CommentsBlockKey = "Comment(Block)";
+ [ColorDescription(CommentsBlockKey, VSSetting = "Comment")]
+ public ChunkStyle CommentsBlock { get; private set; }
- [ColorDescription("Comment(Doc)", VSSetting = "XML Doc Comment")]
+ public const string CommentsForDocumentationKey = "Comment(Doc)";
+ [ColorDescription(CommentsForDocumentationKey, VSSetting = "XML Doc Comment")]
public ChunkStyle CommentsForDocumentation { get; private set; }
- [ColorDescription("Comment(DocTag)", VSSetting = "XML Doc Tag")]
+ public const string CommentsForDocumentationTagsKey = "Comment(DocTag)";
+ [ColorDescription(CommentsForDocumentationTagsKey, VSSetting = "XML Doc Tag")]
public ChunkStyle CommentsForDocumentationTags { get; private set; }
- [ColorDescription("Comment Tag", VSSetting = "Comment")]
+ public const string CommentTagsKey = "Comment Tag";
+ [ColorDescription(CommentTagsKey, VSSetting = "Comment")]
public ChunkStyle CommentTags { get; private set; }
- [ColorDescription("Excluded Code", VSSetting = "Excluded Code")]
+ public const string ExcludedCodeKey = "Excluded Code";
+ [ColorDescription(ExcludedCodeKey, VSSetting = "Excluded Code")]
public ChunkStyle ExcludedCode { get; private set; }
- [ColorDescription("String", VSSetting = "String")]
+ public const string StringKey = "String";
+ [ColorDescription(StringKey, VSSetting = "String")]
public ChunkStyle String { get; private set; }
- [ColorDescription("String(Escape)", VSSetting = "String")]
+ public const string StringEscapeSequenceKey = "String(Escape)";
+ [ColorDescription(StringEscapeSequenceKey, VSSetting = "String")]
public ChunkStyle StringEscapeSequence { get; private set; }
- [ColorDescription("String(C# @ Verbatim)", VSSetting = "String(C# @ Verbatim)")]
+ public const string StringVerbatimKey = "String(C# @ Verbatim)";
+ [ColorDescription(StringVerbatimKey, VSSetting = "String(C# @ Verbatim)")]
public ChunkStyle StringVerbatim { get; private set; }
- [ColorDescription("Number", VSSetting = "Number")]
+ public const string NumberKey = "Number";
+ [ColorDescription(NumberKey, VSSetting = "Number")]
public ChunkStyle Number { get; private set; }
- [ColorDescription("Preprocessor", VSSetting = "Preprocessor Keyword")]
+ public const string PreprocessorKey = "Preprocessor";
+ [ColorDescription(PreprocessorKey, VSSetting = "Preprocessor Keyword")]
public ChunkStyle Preprocessor { get; private set; }
- [ColorDescription("Preprocessor(Region Name)", VSSetting = "Plain Text")]
+ public const string PreprocessorRegionNameKey = "Preprocessor(Region Name)";
+ [ColorDescription(PreprocessorRegionNameKey, VSSetting = "Plain Text")]
public ChunkStyle PreprocessorRegionName { get; private set; }
- [ColorDescription("Xml Text", VSSetting = "XML Text")]
+ public const string XmlTextKey = "Xml Text";
+ [ColorDescription(XmlTextKey, VSSetting = "XML Text")]
public ChunkStyle XmlText { get; private set; }
- [ColorDescription("Xml Delimiter", VSSetting = "XML Delimiter")]
+ public const string XmlDelimiterKey = "Xml Delimiter";
+ [ColorDescription(XmlDelimiterKey, VSSetting = "XML Delimiter")]
public ChunkStyle XmlDelimiter { get; private set; }
- [ColorDescription("Xml Name", VSSetting ="XML Name")]
+ public const string XmlNameKey = "Xml Name";
+ [ColorDescription(XmlNameKey, VSSetting ="XML Name")]
public ChunkStyle XmlName { get; private set; }
- [ColorDescription("Xml Attribute", VSSetting = "XML Attribute")]
+ public const string XmlAttributeKey = "Xml Attribute";
+ [ColorDescription(XmlAttributeKey, VSSetting = "XML Attribute")]
public ChunkStyle XmlAttribute { get; private set; }
- [ColorDescription("Xml Attribute Quotes", VSSetting = "XML Attribute Quotes")]
+ public const string XmlAttributeQuotesKey = "Xml Attribute Quotes";
+ [ColorDescription(XmlAttributeQuotesKey, VSSetting = "XML Attribute Quotes")]
public ChunkStyle XmlAttributeQuotes { get; private set; }
- [ColorDescription("Xml Attribute Value", VSSetting = "XML Attribute Value")]
+ public const string XmlAttributeValueKey = "Xml Attribute Value";
+ [ColorDescription(XmlAttributeValueKey, VSSetting = "XML Attribute Value")]
public ChunkStyle XmlAttributeValue { get; private set; }
- [ColorDescription("Xml Comment", VSSetting = "XML Comment")]
+ public const string XmlCommentKey = "Xml Comment";
+ [ColorDescription(XmlCommentKey, VSSetting = "XML Comment")]
public ChunkStyle XmlComment { get; private set; }
- [ColorDescription("Xml CData Section", VSSetting = "XML CData Section")]
+ public const string XmlCDataSectionKey = "Xml CData Section";
+ [ColorDescription(XmlCDataSectionKey, VSSetting = "XML CData Section")]
public ChunkStyle XmlCDataSection { get; private set; }
- [ColorDescription("Tooltip Text")] // not defined in vs.net
+ public const string TooltipTextKey = "Tooltip Text";
+ [ColorDescription(TooltipTextKey)] // not defined in vs.net
public ChunkStyle TooltipText { get; private set; }
- [ColorDescription("Notification Text")] // not defined in vs.net
+ public const string NotificationTextKey = "Notification Text";
+ [ColorDescription(NotificationTextKey)] // not defined in vs.net
public ChunkStyle NotificationText { get; private set; }
- [ColorDescription("Completion Text")] //not defined in vs.net
+ public const string CompletionTextKey = "Completion Text";
+ [ColorDescription(CompletionTextKey)] //not defined in vs.net
public ChunkStyle CompletionText { get; private set; }
- [ColorDescription("Completion Selected Text")] //not defined in vs.net
+ public const string CompletionSelectedTextKey = "Completion Selected Text";
+ [ColorDescription(CompletionSelectedTextKey)] //not defined in vs.net
public ChunkStyle CompletionSelectedText { get; private set; }
- [ColorDescription("Completion Selected Text(Inactive)")] //not defined in vs.net
+ public const string CompletionSelectedInactiveTextKey = "Completion Selected Text(Inactive)";
+ [ColorDescription(CompletionSelectedInactiveTextKey)] //not defined in vs.net
public ChunkStyle CompletionSelectedInactiveText { get; private set; }
- [ColorDescription("Keyword(Access)", VSSetting = "Keyword")]
+ public const string KeywordAccessorsKey = "Keyword(Access)";
+ [ColorDescription(KeywordAccessorsKey, VSSetting = "Keyword")]
public ChunkStyle KeywordAccessors { get; private set; }
- [ColorDescription("Keyword(Type)", VSSetting = "Keyword")]
+ public const string KeywordTypesKey = "Keyword(Type)";
+ [ColorDescription(KeywordTypesKey, VSSetting = "Keyword")]
public ChunkStyle KeywordTypes { get; private set; }
- [ColorDescription("Keyword(Operator)", VSSetting = "Keyword")]
+ public const string KeywordOperatorsKey = "Keyword(Operator)";
+ [ColorDescription(KeywordOperatorsKey, VSSetting = "Keyword")]
public ChunkStyle KeywordOperators { get; private set; }
- [ColorDescription("Keyword(Selection)", VSSetting = "Keyword")]
+ public const string KeywordSelectionKey = "Keyword(Selection)";
+ [ColorDescription(KeywordSelectionKey, VSSetting = "Keyword")]
public ChunkStyle KeywordSelection { get; private set; }
- [ColorDescription("Keyword(Iteration)", VSSetting = "Keyword")]
+ public const string KeywordIterationKey = "Keyword(Iteration)";
+ [ColorDescription(KeywordIterationKey, VSSetting = "Keyword")]
public ChunkStyle KeywordIteration { get; private set; }
- [ColorDescription("Keyword(Jump)", VSSetting = "Keyword")]
+ public const string KeywordJumpKey = "Keyword(Jump)";
+ [ColorDescription(KeywordJumpKey, VSSetting = "Keyword")]
public ChunkStyle KeywordJump { get; private set; }
- [ColorDescription("Keyword(Context)", VSSetting = "Keyword")]
+ public const string KeywordContextKey = "Keyword(Context)";
+ [ColorDescription(KeywordContextKey, VSSetting = "Keyword")]
public ChunkStyle KeywordContext { get; private set; }
- [ColorDescription("Keyword(Exception)", VSSetting = "Keyword")]
+ public const string KeywordExceptionKey = "Keyword(Exception)";
+ [ColorDescription(KeywordExceptionKey, VSSetting = "Keyword")]
public ChunkStyle KeywordException { get; private set; }
- [ColorDescription("Keyword(Modifiers)", VSSetting = "Keyword")]
+ public const string KeywordModifiersKey = "Keyword(Modifiers)";
+ [ColorDescription(KeywordModifiersKey, VSSetting = "Keyword")]
public ChunkStyle KeywordModifiers { get; private set; }
- [ColorDescription("Keyword(Constants)", VSSetting = "Keyword")]
+ public const string KeywordConstantsKey = "Keyword(Constants)";
+ [ColorDescription(KeywordConstantsKey, VSSetting = "Keyword")]
public ChunkStyle KeywordConstants { get; private set; }
- [ColorDescription("Keyword(Void)", VSSetting = "Keyword")]
+ public const string KeywordVoidKey = "Keyword(Void)";
+ [ColorDescription(KeywordVoidKey, VSSetting = "Keyword")]
public ChunkStyle KeywordVoid { get; private set; }
- [ColorDescription("Keyword(Namespace)", VSSetting = "Keyword")]
+ public const string KeywordNamespaceKey = "Keyword(Namespace)";
+ [ColorDescription(KeywordNamespaceKey, VSSetting = "Keyword")]
public ChunkStyle KeywordNamespace { get; private set; }
- [ColorDescription("Keyword(Property)", VSSetting = "Keyword")]
+ public const string KeywordPropertyKey = "Keyword(Property)";
+ [ColorDescription(KeywordPropertyKey, VSSetting = "Keyword")]
public ChunkStyle KeywordProperty { get; private set; }
- [ColorDescription("Keyword(Declaration)", VSSetting = "Keyword")]
+ public const string KeywordDeclarationKey = "Keyword(Declaration)";
+ [ColorDescription(KeywordDeclarationKey, VSSetting = "Keyword")]
public ChunkStyle KeywordDeclaration { get; private set; }
- [ColorDescription("Keyword(Parameter)", VSSetting = "Keyword")]
+ public const string KeywordParameterKey = "Keyword(Parameter)";
+ [ColorDescription(KeywordParameterKey, VSSetting = "Keyword")]
public ChunkStyle KeywordParameter { get; private set; }
- [ColorDescription("Keyword(Operator Declaration)", VSSetting = "Keyword")]
+ public const string KeywordOperatorDeclarationKey = "Keyword(Operator Declaration)";
+ [ColorDescription(KeywordOperatorDeclarationKey, VSSetting = "Keyword")]
public ChunkStyle KeywordOperatorDeclaration { get; private set; }
- [ColorDescription("Keyword(Other)", VSSetting = "Keyword")]
+ public const string KeywordOtherKey = "Keyword(Other)";
+ [ColorDescription(KeywordOtherKey, VSSetting = "Keyword")]
public ChunkStyle KeywordOther { get; private set; }
- [ColorDescription("User Types", VSSetting = "User Types")]
+ public const string UserTypesKey = "User Types";
+ [ColorDescription(UserTypesKey, VSSetting = "User Types")]
public ChunkStyle UserTypes { get; private set; }
- [ColorDescription("User Types(Enums)", VSSetting = "User Types(Enums)")]
+ public const string UserTypesEnumsKey = "User Types(Enums)";
+ [ColorDescription(UserTypesEnumsKey, VSSetting = "User Types(Enums)")]
public ChunkStyle UserTypesEnums { get; private set; }
- [ColorDescription("User Types(Interfaces)", VSSetting = "User Types(Interfaces)")]
+ public const string UserTypesInterfacesKey = "User Types(Interfaces)";
+ [ColorDescription(UserTypesInterfacesKey, VSSetting = "User Types(Interfaces)")]
public ChunkStyle UserTypesInterfaces { get; private set; }
- [ColorDescription("User Types(Delegates)", VSSetting = "User Types(Delegates)")]
- public ChunkStyle UserTypesDelegatess { get; private set; }
+ public const string UserTypesDelegatesKey = "User Types(Delegates)";
+ [ColorDescription(UserTypesDelegatesKey, VSSetting = "User Types(Delegates)")]
+ public ChunkStyle UserTypesDelegates { get; private set; }
- [ColorDescription("User Types(Value types)", VSSetting = "User Types(Value types)")]
+ public const string UserTypesValueTypesKey = "User Types(Value types)";
+ [ColorDescription(UserTypesValueTypesKey, VSSetting = "User Types(Value types)")]
public ChunkStyle UserTypesValueTypes { get; private set; }
- [ColorDescription("User Types(Type parameters)", VSSetting = "User Types(Type parameters)")]
+ public const string UserTypesTypeParametersKey = "User Types(Type parameters)";
+ [ColorDescription(UserTypesTypeParametersKey, VSSetting = "User Types(Type parameters)")]
public ChunkStyle UserTypesTypeParameters { get; private set; }
- [ColorDescription("User Field Usage", VSSetting = "Identifier")]
+ public const string UserFieldUsageKey = "User Field Usage";
+ [ColorDescription(UserFieldUsageKey, VSSetting = "Identifier")]
public ChunkStyle UserFieldUsage { get; private set; }
- [ColorDescription("User Field Declaration", VSSetting = "Identifier")]
+ public const string UserFieldDeclarationKey = "User Field Declaration";
+ [ColorDescription(UserFieldDeclarationKey, VSSetting = "Identifier")]
public ChunkStyle UserFieldDeclaration { get; private set; }
- [ColorDescription("User Property Usage", VSSetting = "Identifier")]
+ public const string UserPropertyUsageKey = "User Property Usage";
+ [ColorDescription(UserPropertyUsageKey, VSSetting = "Identifier")]
public ChunkStyle UserPropertyUsage { get; private set; }
- [ColorDescription("User Property Declaration", VSSetting = "Identifier")]
+ public const string UserPropertyDeclarationKey = "User Property Declaration";
+ [ColorDescription(UserPropertyDeclarationKey, VSSetting = "Identifier")]
public ChunkStyle UserPropertyDeclaration { get; private set; }
- [ColorDescription("User Event Usage", VSSetting = "Identifier")]
+ public const string UserEventUsageKey = "User Event Usage";
+ [ColorDescription(UserEventUsageKey, VSSetting = "Identifier")]
public ChunkStyle UserEventUsage { get; private set; }
- [ColorDescription("User Event Declaration", VSSetting = "Identifier")]
+ public const string UserEventDeclarationKey = "User Event Declaration";
+ [ColorDescription(UserEventDeclarationKey, VSSetting = "Identifier")]
public ChunkStyle UserEventDeclaration { get; private set; }
- [ColorDescription("User Method Usage", VSSetting = "Identifier")]
+ public const string UserMethodUsageKey = "User Method Usage";
+ [ColorDescription(UserMethodUsageKey, VSSetting = "Identifier")]
public ChunkStyle UserMethodUsage { get; private set; }
- [ColorDescription("User Method Declaration", VSSetting = "Identifier")]
+ public const string UserMethodDeclarationKey = "User Method Declaration";
+ [ColorDescription(UserMethodDeclarationKey, VSSetting = "Identifier")]
public ChunkStyle UserMethodDeclaration { get; private set; }
- [ColorDescription("User Parameter Usage", VSSetting = "Identifier")]
+ public const string UserParameterUsageKey = "User Parameter Usage";
+ [ColorDescription(UserParameterUsageKey, VSSetting = "Identifier")]
public ChunkStyle UserParameterUsage { get; private set; }
- [ColorDescription("User Parameter Declaration", VSSetting = "Identifier")]
+ public const string UserParameterDeclarationKey = "User Parameter Declaration";
+ [ColorDescription(UserParameterDeclarationKey, VSSetting = "Identifier")]
public ChunkStyle UserParameterDeclaration { get; private set; }
- [ColorDescription("User Variable Usage", VSSetting = "Identifier")]
+ public const string UserVariableUsageKey = "User Variable Usage";
+ [ColorDescription(UserVariableUsageKey, VSSetting = "Identifier")]
public ChunkStyle UserVariableUsage { get; private set; }
- [ColorDescription("User Variable Declaration", VSSetting = "Identifier")]
+ public const string UserVariableDeclarationKey = "User Variable Declaration";
+ [ColorDescription(UserVariableDeclarationKey, VSSetting = "Identifier")]
public ChunkStyle UserVariableDeclaration { get; private set; }
- [ColorDescription("Syntax Error", VSSetting = "Syntax Error")]
+ public const string SyntaxErrorKey = "Syntax Error";
+ [ColorDescription(SyntaxErrorKey, VSSetting = "Syntax Error")]
public ChunkStyle SyntaxError { get; private set; }
- [ColorDescription("String Format Items", VSSetting = "String")]
+ public const string StringFormatItemsKey = "String Format Items";
+ [ColorDescription(StringFormatItemsKey, VSSetting = "String")]
public ChunkStyle StringFormatItems { get; private set; }
- [ColorDescription("Breakpoint Text", VSSetting = "Breakpoint (Enabled)")]
+ public const string BreakpointTextKey = "Breakpoint Text";
+ [ColorDescription(BreakpointTextKey, VSSetting = "Breakpoint (Enabled)")]
public ChunkStyle BreakpointText { get; private set; }
- [ColorDescription("Debugger Current Statement", VSSetting = "Current Statement")]
+ public const string DebuggerCurrentLineKey = "Debugger Current Statement";
+ [ColorDescription(DebuggerCurrentLineKey, VSSetting = "Current Statement")]
public ChunkStyle DebuggerCurrentLine { get; private set; }
- [ColorDescription("Debugger Stack Line")] // not defined
+ public const string DebuggerStackLineKey = "Debugger Stack Line";
+ [ColorDescription(DebuggerStackLineKey)] // not defined
public ChunkStyle DebuggerStackLine { get; private set; }
- [ColorDescription("Diff Line(Added)")] //not defined
+ public const string DiffLineAddedKey = "Diff Line(Added)";
+ [ColorDescription(DiffLineAddedKey)] //not defined
public ChunkStyle DiffLineAdded { get; private set; }
- [ColorDescription("Diff Line(Removed)")] //not defined
+ public const string DiffLineRemovedKey = "Diff Line(Removed)";
+ [ColorDescription(DiffLineRemovedKey)] //not defined
public ChunkStyle DiffLineRemoved { get; private set; }
- [ColorDescription("Diff Line(Changed)")] //not defined
+ public const string DiffLineChangedKey = "Diff Line(Changed)";
+ [ColorDescription(DiffLineChangedKey)] //not defined
public ChunkStyle DiffLineChanged { get; private set; }
- [ColorDescription("Diff Header")] //not defined
+ public const string DiffHeaderKey = "Diff Header";
+ [ColorDescription(DiffHeaderKey)] //not defined
public ChunkStyle DiffHeader { get; private set; }
- [ColorDescription("Diff Header(Separator)")] //not defined
+ public const string DiffHeaderSeparatorKey = "Diff Header(Separator)";
+ [ColorDescription(DiffHeaderSeparatorKey)] //not defined
public ChunkStyle DiffHeaderSeparator { get; private set; }
- [ColorDescription("Diff Header(Old)")] //not defined
+ public const string DiffHeaderOldKey = "Diff Header(Old)";
+ [ColorDescription(DiffHeaderOldKey)] //not defined
public ChunkStyle DiffHeaderOld { get; private set; }
- [ColorDescription("Diff Header(New)")] //not defined
+ public const string DiffHeaderNewKey = "Diff Header(New)";
+ [ColorDescription(DiffHeaderNewKey)] //not defined
public ChunkStyle DiffHeaderNew { get; private set; }
- [ColorDescription("Diff Location")] //not defined
+ public const string DiffLocationKey = "Diff Location";
+ [ColorDescription(DiffLocationKey)] //not defined
public ChunkStyle DiffLocation { get; private set; }
- [ColorDescription("Html Attribute Name", VSSetting="HTML Attribute")]
+ public const string HtmlAttributeNameKey = "Html Attribute Name";
+ [ColorDescription(HtmlAttributeNameKey, VSSetting="HTML Attribute")]
public ChunkStyle HtmlAttributeName { get; private set; }
-
- [ColorDescription("Html Attribute Value", VSSetting="HTML Attribute Value")]
+
+ public const string HtmlAttributeValueKey = "Html Attribute Value";
+ [ColorDescription(HtmlAttributeValueKey, VSSetting="HTML Attribute Value")]
public ChunkStyle HtmlAttributeValue { get; private set; }
- [ColorDescription("Html Comment", VSSetting="HTML Comment")]
+ public const string HtmlCommentKey = "Html Comment";
+ [ColorDescription(HtmlCommentKey, VSSetting="HTML Comment")]
public ChunkStyle HtmlComment { get; private set; }
- [ColorDescription("Html Element Name", VSSetting="HTML Element Name")]
+ public const string HtmlElementNameKey = "Html Element Name";
+ [ColorDescription(HtmlElementNameKey, VSSetting="HTML Element Name")]
public ChunkStyle HtmlElementName { get; private set; }
- [ColorDescription("Html Entity", VSSetting="HTML Entity")]
+ public const string HtmlEntityKey = "Html Entity";
+ [ColorDescription(HtmlEntityKey, VSSetting="HTML Entity")]
public ChunkStyle HtmlEntity { get; private set; }
- [ColorDescription("Html Operator", VSSetting="HTML Operator")]
+ public const string HtmlOperatorKey = "Html Operator";
+ [ColorDescription(HtmlOperatorKey, VSSetting="HTML Operator")]
public ChunkStyle HtmlOperator { get; private set; }
- [ColorDescription("Html Server-Side Script", VSSetting="HTML Server-Side Script")]
+ public const string HtmlServerSideScriptKey = "Html Server-Side Script";
+ [ColorDescription(HtmlServerSideScriptKey, VSSetting="HTML Server-Side Script")]
public ChunkStyle HtmlServerSideScript { get; private set; }
- [ColorDescription("Html Tag Delimiter", VSSetting="HTML Tag Delimiter")]
+ public const string HtmlTagDelimiterKey = "Html Tag Delimiter";
+ [ColorDescription(HtmlTagDelimiterKey, VSSetting="HTML Tag Delimiter")]
public ChunkStyle HtmlTagDelimiter { get; private set; }
- [ColorDescription("Razor Code", VSSetting="Razor Code")]
+ public const string RazorCodeKey = "Razor Code";
+ [ColorDescription(RazorCodeKey, VSSetting="Razor Code")]
public ChunkStyle RazorCode { get; private set; }
- [ColorDescription("Css Comment", VSSetting="CSS Comment")]
+ public const string CssCommentKey = "Css Comment";
+ [ColorDescription(CssCommentKey, VSSetting="CSS Comment")]
public ChunkStyle CssComment { get; private set; }
- [ColorDescription("Css Property Name", VSSetting="CSS Property Name")]
+ public const string CssPropertyNameKey = "Css Property Name";
+ [ColorDescription(CssPropertyNameKey, VSSetting="CSS Property Name")]
public ChunkStyle CssPropertyName { get; private set; }
- [ColorDescription("Css Property Value", VSSetting="CSS Property Value")]
+ public const string CssPropertyValueKey = "Css Property Value";
+ [ColorDescription(CssPropertyValueKey, VSSetting="CSS Property Value")]
public ChunkStyle CssPropertyValue { get; private set; }
- [ColorDescription("Css Selector", VSSetting="CSS Selector")]
+ public const string CssSelectorKey = "Css Selector";
+ [ColorDescription(CssSelectorKey, VSSetting="CSS Selector")]
public ChunkStyle CssSelector { get; private set; }
- [ColorDescription("Css String Value", VSSetting="CSS String Value")]
+ public const string CssStringValueKey = "Css String Value";
+ [ColorDescription(CssStringValueKey, VSSetting="CSS String Value")]
public ChunkStyle CssStringValue { get; private set; }
- [ColorDescription("Css Keyword", VSSetting="CSS Keyword")]
+ public const string CssKeywordKey = "Css Keyword";
+ [ColorDescription(CssKeywordKey, VSSetting="CSS Keyword")]
public ChunkStyle CssKeyword { get; private set; }
- [ColorDescription("Script Comment", VSSetting="Script Comment")]
+ public const string ScriptCommentKey = "Script Comment";
+ [ColorDescription(ScriptCommentKey, VSSetting="Script Comment")]
public ChunkStyle ScriptComment { get; private set; }
- [ColorDescription("Script Identifier", VSSetting="Script Identifier")]
+ public const string ScriptIdentifierKey = "Script Identifier";
+ [ColorDescription(ScriptIdentifierKey, VSSetting="Script Identifier")]
public ChunkStyle ScriptIdentifier { get; private set; }
- [ColorDescription("Script Keyword", VSSetting="Script Keyword")]
+ public const string ScriptKeywordKey = "Script Keyword";
+ [ColorDescription(ScriptKeywordKey, VSSetting="Script Keyword")]
public ChunkStyle ScriptKeyword { get; private set; }
- [ColorDescription("Script Number", VSSetting="Script Number")]
+ public const string ScriptNumberKey = "Script Number";
+ [ColorDescription(ScriptNumberKey, VSSetting="Script Number")]
public ChunkStyle ScriptNumber { get; private set; }
- [ColorDescription("Script Operator", VSSetting="Script Operator")]
+ public const string ScriptOperatorKey = "Script Operator";
+ [ColorDescription(ScriptOperatorKey, VSSetting="Script Operator")]
public ChunkStyle ScriptOperator { get; private set; }
- [ColorDescription("Script String", VSSetting="Script String")]
+ public const string ScriptStringKey = "Script String";
+ [ColorDescription(ScriptStringKey, VSSetting="Script String")]
public ChunkStyle ScriptString { get; private set; }
#endregion
- public class PropertyDecsription
+ public sealed class PropertyDescription
{
public readonly PropertyInfo Info;
public readonly ColorDescriptionAttribute Attribute;
- public PropertyDecsription (PropertyInfo info, ColorDescriptionAttribute attribute)
+ public PropertyDescription (PropertyInfo info, ColorDescriptionAttribute attribute)
{
this.Info = info;
this.Attribute = attribute;
}
}
- static Dictionary<string, PropertyDecsription> textColors = new Dictionary<string, PropertyDecsription> ();
+ static Dictionary<string, PropertyDescription> textColors = new Dictionary<string, PropertyDescription> ();
- public static IEnumerable<PropertyDecsription> TextColors {
+ public static IEnumerable<PropertyDescription> TextColors {
get {
return textColors.Values;
}
}
- static Dictionary<string, PropertyDecsription> ambientColors = new Dictionary<string, PropertyDecsription> ();
+ static Dictionary<string, PropertyDescription> ambientColors = new Dictionary<string, PropertyDescription> ();
- public static IEnumerable<PropertyDecsription> AmbientColors {
+ public static IEnumerable<PropertyDescription> AmbientColors {
get {
return ambientColors.Values;
}
@@ -543,9 +645,9 @@ namespace Mono.TextEditor.Highlighting
if (description == null)
continue;
if (property.PropertyType == typeof (ChunkStyle)) {
- textColors.Add (description.Name, new PropertyDecsription (property, description));
+ textColors.Add (description.Name, new PropertyDescription (property, description));
} else {
- ambientColors.Add (description.Name, new PropertyDecsription (property, description));
+ ambientColors.Add (description.Name, new PropertyDescription (property, description));
}
}
}
@@ -591,7 +693,7 @@ namespace Mono.TextEditor.Highlighting
{
if (color == null)
return GetChunkStyle ("Plain Text");
- PropertyDecsription val;
+ PropertyDescription val;
if (!textColors.TryGetValue (color, out val)) {
Console.WriteLine ("Chunk style : " + color + " is undefined.");
return GetChunkStyle ("Plain Text");
@@ -652,7 +754,7 @@ namespace Mono.TextEditor.Highlighting
foreach (var colorElement in root.XPathSelectElements("//colors/*")) {
var color = AmbientColor.Create (colorElement, palette);
- PropertyDecsription info;
+ PropertyDescription info;
if (!ambientColors.TryGetValue (color.Name, out info)) {
Console.WriteLine ("Ambient color:" + color.Name + " not found.");
continue;
@@ -662,7 +764,7 @@ namespace Mono.TextEditor.Highlighting
foreach (var textColorElement in root.XPathSelectElements("//text/*")) {
var color = ChunkStyle.Create (textColorElement, palette);
- PropertyDecsription info;
+ PropertyDescription info;
if (!textColors.TryGetValue (color.Name, out info)) {
Console.WriteLine ("Text color:" + color.Name + " not found.");
continue;
@@ -805,7 +907,7 @@ namespace Mono.TextEditor.Highlighting
return HslColor.Parse (color);
}
- public class VSSettingColor
+ public sealed class VSSettingColor
{
public string Name { get; private set; }
public string Foreground { get; private set; }
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor.Highlighting/SyntaxMode.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor.Highlighting/SyntaxMode.cs
index 0d9ecc76fb..ef17e3d304 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor.Highlighting/SyntaxMode.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor.Highlighting/SyntaxMode.cs
@@ -38,7 +38,7 @@ namespace Mono.TextEditor.Highlighting
{
protected TextDocument doc;
- public TextDocument Document {
+ public virtual TextDocument Document {
get {
return doc;
}
@@ -743,6 +743,9 @@ namespace Mono.TextEditor.Highlighting
string extends = reader.GetAttribute ("extends");
if (!String.IsNullOrEmpty (extends)) {
result = (SyntaxMode)SyntaxModeService.GetSyntaxMode (null, extends).MemberwiseClone ();
+ spanList.AddRange (result.spans);
+ prevMarkerList.AddRange (result.prevMarker);
+ matches.AddRange (result.matches);
}
result.Name = reader.GetAttribute ("name");
result.MimeType = reader.GetAttribute (MimeTypesAttribute);
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor.Theatrics/BounceFadePopupWindow.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor.Theatrics/BounceFadePopupWindow.cs
index 6fc42b0782..8f74963df2 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor.Theatrics/BounceFadePopupWindow.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor.Theatrics/BounceFadePopupWindow.cs
@@ -37,12 +37,12 @@ namespace Mono.TextEditor.Theatrics
{
Stage<BounceFadePopupWindow> stage = new Stage<BounceFadePopupWindow> ();
Gdk.Pixbuf textImage = null;
- TextEditor editor;
+ MonoTextEditor editor;
protected double scale = 0.0;
protected double opacity = 1.0;
- public BounceFadePopupWindow (TextEditor editor) : base (Gtk.WindowType.Popup)
+ public BounceFadePopupWindow (MonoTextEditor editor) : base (Gtk.WindowType.Popup)
{
if (!IsComposited)
throw new InvalidOperationException ("Only works with composited screen. Check Widget.IsComposited.");
@@ -69,7 +69,7 @@ namespace Mono.TextEditor.Theatrics
stage.UpdateFrequency = 10;
}
- protected TextEditor Editor { get { return editor; } }
+ protected MonoTextEditor Editor { get { return editor; } }
/// <summary>Duration of the animation, in milliseconds.</summary>
public uint Duration { get; set; }
@@ -227,12 +227,12 @@ namespace Mono.TextEditor.Theatrics
{
Stage<BounceFadePopupWidget> stage = new Stage<BounceFadePopupWidget> ();
Gdk.Pixbuf textImage = null;
- TextEditor editor;
+ MonoTextEditor editor;
protected double scale = 0.0;
protected double opacity = 1.0;
- public BounceFadePopupWidget (TextEditor editor)
+ public BounceFadePopupWidget (MonoTextEditor editor)
{
if (!IsComposited)
throw new InvalidOperationException ("Only works with composited screen. Check Widget.IsComposited.");
@@ -256,7 +256,7 @@ namespace Mono.TextEditor.Theatrics
stage.UpdateFrequency = 10;
}
- protected TextEditor Editor { get { return editor; } }
+ protected MonoTextEditor Editor { get { return editor; } }
/// <summary>Duration of the animation, in milliseconds.</summary>
public uint Duration { get; set; }
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor.Utils/CharRope.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor.Utils/CharRope.cs
new file mode 100644
index 0000000000..df70acc8c4
--- /dev/null
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor.Utils/CharRope.cs
@@ -0,0 +1,226 @@
+// 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.Globalization;
+using System.IO;
+using System.Text;
+
+namespace Mono.TextEditor.Utils
+{
+ /// <summary>
+ /// Poor man's template specialization: extension methods for Rope&lt;char&gt;.
+ /// </summary>
+ public static class CharRope
+ {
+ /// <summary>
+ /// Creates a new rope from the specified text.
+ /// </summary>
+ public static Rope<char> Create(string text)
+ {
+ if (text == null)
+ throw new ArgumentNullException("text");
+ return new Rope<char>(InitFromString(text));
+ }
+
+ /// <summary>
+ /// Retrieves the text for a portion of the rope.
+ /// Runs in O(lg N + M), where M=<paramref name="length"/>.
+ /// </summary>
+ /// <exception cref="ArgumentOutOfRangeException">offset or length is outside the valid range.</exception>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public static string ToString(this Rope<char> rope, int startIndex, int length)
+ {
+ if (rope == null)
+ throw new ArgumentNullException("rope");
+ #if DEBUG
+ if (length < 0)
+ throw new ArgumentOutOfRangeException("length", length, "Value must be >= 0");
+ #endif
+ if (length == 0)
+ return string.Empty;
+ char[] buffer = new char[length];
+ rope.CopyTo(startIndex, buffer, 0, length);
+ return new string(buffer);
+ }
+
+ /// <summary>
+ /// Retrieves the text for a portion of the rope and writes it to the specified text writer.
+ /// Runs in O(lg N + M), where M=<paramref name="length"/>.
+ /// </summary>
+ /// <exception cref="ArgumentOutOfRangeException">offset or length is outside the valid range.</exception>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public static void WriteTo(this Rope<char> rope, TextWriter output, int startIndex, int length)
+ {
+ if (rope == null)
+ throw new ArgumentNullException("rope");
+ if (output == null)
+ throw new ArgumentNullException("output");
+ rope.VerifyRange(startIndex, length);
+ rope.root.WriteTo(startIndex, output, length);
+ }
+
+ /// <summary>
+ /// Appends text to this rope.
+ /// Runs in O(lg N + M).
+ /// </summary>
+ /// <exception cref="ArgumentNullException">newElements is null.</exception>
+ public static void AddText(this Rope<char> rope, string text)
+ {
+ InsertText(rope, rope.Length, text);
+ }
+
+ /// <summary>
+ /// Inserts text into this rope.
+ /// Runs in O(lg N + M).
+ /// </summary>
+ /// <exception cref="ArgumentNullException">newElements is null.</exception>
+ /// <exception cref="ArgumentOutOfRangeException">index or length is outside the valid range.</exception>
+ public static void InsertText(this Rope<char> rope, int index, string text)
+ {
+ if (rope == null)
+ throw new ArgumentNullException("rope");
+ rope.InsertRange(index, text.ToCharArray(), 0, text.Length);
+ /*if (index < 0 || index > rope.Length) {
+ throw new ArgumentOutOfRangeException("index", index, "0 <= index <= " + rope.Length.ToString(CultureInfo.InvariantCulture));
+ }
+ if (text == null)
+ throw new ArgumentNullException("text");
+ if (text.Length == 0)
+ return;
+ rope.root = rope.root.Insert(index, text);
+ rope.OnChanged();*/
+ }
+
+ internal static RopeNode<char> InitFromString(string text)
+ {
+ if (text.Length == 0) {
+ return RopeNode<char>.emptyRopeNode;
+ }
+ RopeNode<char> node = RopeNode<char>.CreateNodes(text.Length);
+ FillNode(node, text, 0);
+ return node;
+ }
+
+ static void FillNode(RopeNode<char> node, string text, int start)
+ {
+ if (node.contents != null) {
+ text.CopyTo(start, node.contents, 0, node.length);
+ } else {
+ FillNode(node.left, text, start);
+ FillNode(node.right, text, start + node.left.length);
+ }
+ }
+
+ internal static void WriteTo(this RopeNode<char> node, int index, TextWriter output, int count)
+ {
+ if (node.height == 0) {
+ if (node.contents == null) {
+ // function node
+ node.GetContentNode().WriteTo(index, output, count);
+ } else {
+ // leaf node: append data
+ output.Write(node.contents, index, count);
+ }
+ } else {
+ // concat node: do recursive calls
+ if (index + count <= node.left.length) {
+ node.left.WriteTo(index, output, count);
+ } else if (index >= node.left.length) {
+ node.right.WriteTo(index - node.left.length, output, count);
+ } else {
+ int amountInLeft = node.left.length - index;
+ node.left.WriteTo(index, output, amountInLeft);
+ node.right.WriteTo(0, output, count - amountInLeft);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the index of the first occurrence of any element in the specified array.
+ /// </summary>
+ /// <param name="rope">The target rope.</param>
+ /// <param name="anyOf">Array of characters being searched.</param>
+ /// <param name="startIndex">Start index of the search.</param>
+ /// <param name="length">Length of the area to search.</param>
+ /// <returns>The first index where any character was found; or -1 if no occurrence was found.</returns>
+ public static int IndexOfAny(this Rope<char> rope, char[] anyOf, int startIndex, int length)
+ {
+ if (rope == null)
+ throw new ArgumentNullException("rope");
+ if (anyOf == null)
+ throw new ArgumentNullException("anyOf");
+ rope.VerifyRange(startIndex, length);
+
+ while (length > 0) {
+ var entry = rope.FindNodeUsingCache(startIndex).PeekOrDefault();
+ char[] contents = entry.node.contents;
+ int startWithinNode = startIndex - entry.nodeStartIndex;
+ int nodeLength = System.Math.Min(entry.node.length, startWithinNode + length);
+ for (int i = startIndex - entry.nodeStartIndex; i < nodeLength; i++) {
+ char element = contents[i];
+ foreach (char needle in anyOf) {
+ if (element == needle)
+ return entry.nodeStartIndex + i;
+ }
+ }
+ length -= nodeLength - startWithinNode;
+ startIndex = entry.nodeStartIndex + nodeLength;
+ }
+ return -1;
+ }
+
+ /// <summary>
+ /// Gets the index of the first occurrence of the search text.
+ /// </summary>
+ public static int IndexOf(this Rope<char> rope, string searchText, int startIndex, int length, StringComparison comparisonType)
+ {
+ if (rope == null)
+ throw new ArgumentNullException("rope");
+ if (searchText == null)
+ throw new ArgumentNullException("searchText");
+ rope.VerifyRange(startIndex, length);
+ int pos = rope.ToString(startIndex, length).IndexOf(searchText, comparisonType);
+ if (pos < 0)
+ return -1;
+ else
+ return pos + startIndex;
+ }
+
+ /// <summary>
+ /// Gets the index of the last occurrence of the search text.
+ /// </summary>
+ public static int LastIndexOf(this Rope<char> rope, string searchText, int startIndex, int length, StringComparison comparisonType)
+ {
+ if (rope == null)
+ throw new ArgumentNullException("rope");
+ if (searchText == null)
+ throw new ArgumentNullException("searchText");
+ rope.VerifyRange(startIndex, length);
+ int pos = rope.ToString(startIndex, length).LastIndexOf(searchText, comparisonType);
+ if (pos < 0)
+ return -1;
+ else
+ return pos + startIndex;
+ }
+ }
+}
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor.Utils/ImmutableStack.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor.Utils/ImmutableStack.cs
new file mode 100644
index 0000000000..4611571c0d
--- /dev/null
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor.Utils/ImmutableStack.cs
@@ -0,0 +1,132 @@
+// 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.Text;
+
+namespace Mono.TextEditor.Utils
+{
+ /// <summary>
+ /// An immutable stack.
+ ///
+ /// Using 'foreach' on the stack will return the items from top to bottom (in the order they would be popped).
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
+ [Serializable]
+ public sealed class ImmutableStack<T> : IEnumerable<T>
+ {
+ /// <summary>
+ /// Gets the empty stack instance.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", Justification = "ImmutableStack is immutable")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")]
+ public static readonly ImmutableStack<T> Empty = new ImmutableStack<T>();
+
+ readonly T value;
+ readonly ImmutableStack<T> next;
+
+ private ImmutableStack()
+ {
+ }
+
+ private ImmutableStack(T value, ImmutableStack<T> next)
+ {
+ this.value = value;
+ this.next = next;
+ }
+
+ /// <summary>
+ /// Pushes an item on the stack. This does not modify the stack itself, but returns a new
+ /// one with the value pushed.
+ /// </summary>
+ public ImmutableStack<T> Push(T item)
+ {
+ return new ImmutableStack<T>(item, this);
+ }
+
+ /// <summary>
+ /// Gets the item on the top of the stack.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">The stack is empty.</exception>
+ public T Peek()
+ {
+ if (IsEmpty)
+ throw new InvalidOperationException("Operation not valid on empty stack.");
+ return value;
+ }
+
+ /// <summary>
+ /// Gets the item on the top of the stack.
+ /// Returns <c>default(T)</c> if the stack is empty.
+ /// </summary>
+ public T PeekOrDefault()
+ {
+ return value;
+ }
+
+ /// <summary>
+ /// Gets the stack with the top item removed.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">The stack is empty.</exception>
+ public ImmutableStack<T> Pop()
+ {
+ if (IsEmpty)
+ throw new InvalidOperationException("Operation not valid on empty stack.");
+ return next;
+ }
+
+ /// <summary>
+ /// Gets if this stack is empty.
+ /// </summary>
+ public bool IsEmpty {
+ get { return next == null; }
+ }
+
+ /// <summary>
+ /// Gets an enumerator that iterates through the stack top-to-bottom.
+ /// </summary>
+ public IEnumerator<T> GetEnumerator()
+ {
+ ImmutableStack<T> t = this;
+ while (!t.IsEmpty) {
+ yield return t.value;
+ t = t.next;
+ }
+ }
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+ {
+ return this.GetEnumerator();
+ }
+
+ /// <inheritdoc/>
+ public override string ToString()
+ {
+ StringBuilder b = new StringBuilder("[Stack");
+ foreach (T val in this) {
+ b.Append(' ');
+ b.Append(val);
+ }
+ b.Append(']');
+ return b.ToString();
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor.Utils/Rope.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor.Utils/Rope.cs
new file mode 100644
index 0000000000..0ce950d5bc
--- /dev/null
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor.Utils/Rope.cs
@@ -0,0 +1,854 @@
+// 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.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Runtime.Serialization;
+using System.Text;
+
+namespace Mono.TextEditor.Utils
+{
+ /// <summary>
+ /// A kind of List&lt;T&gt;, but more efficient for random insertions/removal.
+ /// Also has cheap Clone() and SubRope() implementations.
+ /// </summary>
+ /// <remarks>
+ /// This class is not thread-safe: multiple concurrent write operations or writes concurrent to reads have undefined behaviour.
+ /// Concurrent reads, however, are safe.
+ /// However, clones of a rope are safe to use on other threads even though they share data with the original rope.
+ /// </remarks>
+ [Serializable]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
+ public sealed class Rope<T> : IList<T>, ICloneable
+ {
+ internal RopeNode<T> root;
+
+ internal Rope(RopeNode<T> root)
+ {
+ this.root = root;
+ root.CheckInvariants();
+ }
+
+ /// <summary>
+ /// Creates a new rope representing the empty string.
+ /// </summary>
+ public Rope()
+ {
+ // we'll construct the empty rope as a clone of an imaginary static empty rope
+ this.root = RopeNode<T>.emptyRopeNode;
+ root.CheckInvariants();
+ }
+
+ /// <summary>
+ /// Creates a rope from the specified input.
+ /// This operation runs in O(N).
+ /// </summary>
+ /// <exception cref="ArgumentNullException">input is null.</exception>
+ public Rope(IEnumerable<T> input)
+ {
+ if (input == null)
+ throw new ArgumentNullException("input");
+ Rope<T> inputRope = input as Rope<T>;
+ if (inputRope != null) {
+ // clone ropes instead of copying them
+ inputRope.root.Publish();
+ this.root = inputRope.root;
+ } else {
+ string text = input as string;
+ if (text != null) {
+ // if a string is IEnumerable<T>, then T must be char
+ ((Rope<char>)(object)this).root = CharRope.InitFromString(text);
+ } else {
+ T[] arr = ToArray(input);
+ this.root = RopeNode<T>.CreateFromArray(arr, 0, arr.Length);
+ }
+ }
+ this.root.CheckInvariants();
+ }
+
+ /// <summary>
+ /// Creates a rope from a part of the array.
+ /// This operation runs in O(N).
+ /// </summary>
+ /// <exception cref="ArgumentNullException">input is null.</exception>
+ public Rope(T[] array, int arrayIndex, int count)
+ {
+ VerifyArrayWithRange(array, arrayIndex, count);
+ this.root = RopeNode<T>.CreateFromArray(array, arrayIndex, count);
+ this.root.CheckInvariants();
+ }
+
+ /// <summary>
+ /// Creates a new rope that lazily initalizes its content.
+ /// </summary>
+ /// <param name="length">The length of the rope that will be lazily loaded.</param>
+ /// <param name="initializer">
+ /// The callback that provides the content for this rope.
+ /// <paramref name="initializer"/> will be called exactly once when the content of this rope is first requested.
+ /// It must return a rope with the specified length.
+ /// Because the initializer function is not called when a rope is cloned, and such clones may be used on another threads,
+ /// it is possible for the initializer callback to occur on any thread.
+ /// </param>
+ /// <remarks>
+ /// Any modifications inside the rope will also cause the content to be initialized.
+ /// However, insertions at the beginning and the end, as well as inserting this rope into another or
+ /// using the <see cref="Concat(Rope{T},Rope{T})"/> method, allows constructions of larger ropes where parts are
+ /// lazily loaded.
+ /// However, even methods like Concat may sometimes cause the initializer function to be called, e.g. when
+ /// two short ropes are concatenated.
+ /// </remarks>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
+ public Rope(int length, Func<Rope<T>> initializer)
+ {
+ if (initializer == null)
+ throw new ArgumentNullException("initializer");
+ if (length < 0)
+ throw new ArgumentOutOfRangeException("length", length, "Length must not be negative");
+ if (length == 0) {
+ this.root = RopeNode<T>.emptyRopeNode;
+ } else {
+ this.root = new FunctionNode<T>(length, initializer);
+ }
+ this.root.CheckInvariants();
+ }
+
+ static T[] ToArray(IEnumerable<T> input)
+ {
+ T[] arr = input as T[];
+ return arr ?? input.ToArray();
+ }
+
+ /// <summary>
+ /// Clones the rope.
+ /// This operation runs in linear time to the number of rope nodes touched since the last clone was created.
+ /// If you count the per-node cost to the operation modifying the rope (doing this doesn't increase the complexity of the modification operations);
+ /// the remainder of Clone() runs in O(1).
+ /// </summary>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public Rope<T> Clone()
+ {
+ // The Publish() call actually modifies this rope instance; but this modification is thread-safe
+ // as long as the tree structure doesn't change during the operation.
+ root.Publish();
+ return new Rope<T>(root);
+ }
+
+ object ICloneable.Clone()
+ {
+ return this.Clone();
+ }
+
+ /// <summary>
+ /// Resets the rope to an empty list.
+ /// Runs in O(1).
+ /// </summary>
+ public void Clear()
+ {
+ root = RopeNode<T>.emptyRopeNode;
+ OnChanged();
+ }
+
+ /// <summary>
+ /// Gets the length of the rope.
+ /// Runs in O(1).
+ /// </summary>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public int Length {
+ get { return root.length; }
+ }
+
+ /// <summary>
+ /// Gets the length of the rope.
+ /// Runs in O(1).
+ /// </summary>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public int Count {
+ get { return root.length; }
+ }
+
+ /// <summary>
+ /// Inserts another rope into this rope.
+ /// Runs in O(lg N + lg M), plus a per-node cost as if <c>newElements.Clone()</c> was called.
+ /// </summary>
+ /// <exception cref="ArgumentNullException">newElements is null.</exception>
+ /// <exception cref="ArgumentOutOfRangeException">index or length is outside the valid range.</exception>
+ public void InsertRange(int index, Rope<T> newElements)
+ {
+ if (index < 0 || index > this.Length) {
+ throw new ArgumentOutOfRangeException("index", index, "0 <= index <= " + this.Length.ToString(CultureInfo.InvariantCulture));
+ }
+ if (newElements == null)
+ throw new ArgumentNullException("newElements");
+ newElements.root.Publish();
+ root = root.Insert(index, newElements.root);
+ OnChanged();
+ }
+
+ /// <summary>
+ /// Inserts new elemetns into this rope.
+ /// Runs in O(lg N + M), where N is the length of this rope and M is the number of new elements.
+ /// </summary>
+ /// <exception cref="ArgumentNullException">newElements is null.</exception>
+ /// <exception cref="ArgumentOutOfRangeException">index or length is outside the valid range.</exception>
+ public void InsertRange(int index, IEnumerable<T> newElements)
+ {
+ if (newElements == null)
+ throw new ArgumentNullException("newElements");
+ Rope<T> newElementsRope = newElements as Rope<T>;
+ if (newElementsRope != null) {
+ InsertRange(index, newElementsRope);
+ } else {
+ T[] arr = ToArray(newElements);
+ InsertRange(index, arr, 0, arr.Length);
+ }
+ }
+
+ /// <summary>
+ /// Inserts new elements into this rope.
+ /// Runs in O(lg N + M), where N is the length of this rope and M is the number of new elements.
+ /// </summary>
+ /// <exception cref="ArgumentNullException">newElements is null.</exception>
+ /// <exception cref="ArgumentOutOfRangeException">index or length is outside the valid range.</exception>
+ public void InsertRange(int index, T[] array, int arrayIndex, int count)
+ {
+ if (index < 0 || index > this.Length) {
+ throw new ArgumentOutOfRangeException("index", index, "0 <= index <= " + this.Length.ToString(CultureInfo.InvariantCulture));
+ }
+ VerifyArrayWithRange(array, arrayIndex, count);
+ if (count > 0) {
+ root = root.Insert(index, array, arrayIndex, count);
+ OnChanged();
+ }
+ }
+
+ /// <summary>
+ /// Appends multiple elements to the end of this rope.
+ /// Runs in O(lg N + M), where N is the length of this rope and M is the number of new elements.
+ /// </summary>
+ /// <exception cref="ArgumentNullException">newElements is null.</exception>
+ public void AddRange(IEnumerable<T> newElements)
+ {
+ InsertRange(this.Length, newElements);
+ }
+
+ /// <summary>
+ /// Appends another rope to the end of this rope.
+ /// Runs in O(lg N + lg M), plus a per-node cost as if <c>newElements.Clone()</c> was called.
+ /// </summary>
+ /// <exception cref="ArgumentNullException">newElements is null.</exception>
+ public void AddRange(Rope<T> newElements)
+ {
+ InsertRange(this.Length, newElements);
+ }
+
+ /// <summary>
+ /// Appends new elements to the end of this rope.
+ /// Runs in O(lg N + M), where N is the length of this rope and M is the number of new elements.
+ /// </summary>
+ /// <exception cref="ArgumentNullException">array is null.</exception>
+ public void AddRange(T[] array, int arrayIndex, int count)
+ {
+ InsertRange(this.Length, array, arrayIndex, count);
+ }
+
+ /// <summary>
+ /// Removes a range of elements from the rope.
+ /// Runs in O(lg N).
+ /// </summary>
+ /// <exception cref="ArgumentOutOfRangeException">offset or length is outside the valid range.</exception>
+ public void RemoveRange(int index, int count)
+ {
+ VerifyRange(index, count);
+ if (count > 0) {
+ root = root.RemoveRange(index, count);
+ OnChanged();
+ }
+ }
+
+ /// <summary>
+ /// Copies a range of the specified array into the rope, overwriting existing elements.
+ /// Runs in O(lg N + M).
+ /// </summary>
+ public void SetRange(int index, T[] array, int arrayIndex, int count)
+ {
+ VerifyRange(index, count);
+ VerifyArrayWithRange(array, arrayIndex, count);
+ if (count > 0) {
+ root = root.StoreElements(index, array, arrayIndex, count);
+ OnChanged();
+ }
+ }
+
+ /// <summary>
+ /// Creates a new rope and initializes it with a part of this rope.
+ /// Runs in O(lg N) plus a per-node cost as if <c>this.Clone()</c> was called.
+ /// </summary>
+ /// <exception cref="ArgumentOutOfRangeException">offset or length is outside the valid range.</exception>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public Rope<T> GetRange(int index, int count)
+ {
+ VerifyRange(index, count);
+ Rope<T> newRope = Clone();
+ int endIndex = index + count;
+ newRope.RemoveRange(endIndex, newRope.Length - endIndex);
+ newRope.RemoveRange(0, index);
+ return newRope;
+ }
+
+ /*
+ #region Equality
+ /// <summary>
+ /// Gets whether the two ropes have the same content.
+ /// Runs in O(N + M).
+ /// </summary>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public bool Equals(Rope other)
+ {
+ if (other == null)
+ return false;
+ // quick detection for ropes that are clones of each other:
+ if (other.root == this.root)
+ return true;
+ if (other.Length != this.Length)
+ return false;
+ using (RopeTextReader a = new RopeTextReader(this, false)) {
+ using (RopeTextReader b = new RopeTextReader(other, false)) {
+ int charA, charB;
+ do {
+ charA = a.Read();
+ charB = b.Read();
+ if (charA != charB)
+ return false;
+ } while (charA != -1);
+ return true;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets whether two ropes have the same content.
+ /// Runs in O(N + M).
+ /// </summary>
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as Rope);
+ }
+
+ /// <summary>
+ /// Calculates the hash code of the rope's content.
+ /// Runs in O(N).
+ /// </summary>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public override int GetHashCode()
+ {
+ int hashcode = 0;
+ using (RopeTextReader reader = new RopeTextReader(this, false)) {
+ unchecked {
+ int val;
+ while ((val = reader.Read()) != -1) {
+ hashcode = hashcode * 31 + val;
+ }
+ }
+ }
+ return hashcode;
+ }
+ #endregion
+ */
+
+ /// <summary>
+ /// Concatenates two ropes. The input ropes are not modified.
+ /// Runs in O(lg N + lg M).
+ /// </summary>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")]
+ public static Rope<T> Concat(Rope<T> left, Rope<T> right)
+ {
+ if (left == null)
+ throw new ArgumentNullException("left");
+ if (right == null)
+ throw new ArgumentNullException("right");
+ left.root.Publish();
+ right.root.Publish();
+ return new Rope<T>(RopeNode<T>.Concat(left.root, right.root));
+ }
+
+ /// <summary>
+ /// Concatenates multiple ropes. The input ropes are not modified.
+ /// </summary>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")]
+ public static Rope<T> Concat(params Rope<T>[] ropes)
+ {
+ if (ropes == null)
+ throw new ArgumentNullException("ropes");
+ Rope<T> result = new Rope<T>();
+ foreach (Rope<T> r in ropes)
+ result.AddRange(r);
+ return result;
+ }
+
+ #region Caches / Changed event
+ internal struct RopeCacheEntry
+ {
+ internal readonly RopeNode<T> node;
+ internal readonly int nodeStartIndex;
+
+ internal RopeCacheEntry(RopeNode<T> node, int nodeStartOffset)
+ {
+ this.node = node;
+ this.nodeStartIndex = nodeStartOffset;
+ }
+
+ internal bool IsInside(int offset)
+ {
+ return node != null && offset >= nodeStartIndex && offset < nodeStartIndex + node.length;
+ }
+ }
+
+ // cached pointer to 'last used node', used to speed up accesses by index that are close together
+ [NonSerialized]
+ volatile ImmutableStack<RopeCacheEntry> lastUsedNodeStack;
+
+ internal void OnChanged()
+ {
+ lastUsedNodeStack = null;
+
+ root.CheckInvariants();
+ }
+ #endregion
+
+ #region GetChar / SetChar
+ /// <summary>
+ /// Gets/Sets a single character.
+ /// Runs in O(lg N) for random access. Sequential read-only access benefits from a special optimization and runs in amortized O(1).
+ /// </summary>
+ /// <exception cref="ArgumentOutOfRangeException">Offset is outside the valid range (0 to Length-1).</exception>
+ /// <remarks>
+ /// The getter counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public T this[int index] {
+ get {
+ // use unsigned integers - this way negative values for index overflow and can be tested for with the same check
+ if (unchecked((uint)index >= (uint)this.Length)) {
+ throw new ArgumentOutOfRangeException("index", index, "0 <= index < " + this.Length.ToString(CultureInfo.InvariantCulture));
+ }
+ RopeCacheEntry entry = FindNodeUsingCache(index).PeekOrDefault();
+ return entry.node.contents[index - entry.nodeStartIndex];
+ }
+ set {
+ if (index < 0 || index >= this.Length) {
+ throw new ArgumentOutOfRangeException("index", index, "0 <= index < " + this.Length.ToString(CultureInfo.InvariantCulture));
+ }
+ root = root.SetElement(index, value);
+ OnChanged();
+ /* Here's a try at implementing the setter using the cached node stack (UNTESTED code!).
+ * However I don't use the code because it's complicated and doesn't integrate correctly with change notifications.
+ * Instead, I'll use the much easier to understand recursive solution.
+ * Oh, and it also doesn't work correctly with function nodes.
+ ImmutableStack<RopeCacheEntry> nodeStack = FindNodeUsingCache(offset);
+ RopeCacheEntry entry = nodeStack.Peek();
+ if (!entry.node.isShared) {
+ entry.node.contents[offset - entry.nodeStartOffset] = value;
+ // missing: clear the caches except for the node stack cache (e.g. ToString() cache?)
+ } else {
+ RopeNode oldNode = entry.node;
+ RopeNode newNode = oldNode.Clone();
+ newNode.contents[offset - entry.nodeStartOffset] = value;
+ for (nodeStack = nodeStack.Pop(); !nodeStack.IsEmpty; nodeStack = nodeStack.Pop()) {
+ RopeNode parentNode = nodeStack.Peek().node;
+ RopeNode newParentNode = parentNode.CloneIfShared();
+ if (newParentNode.left == oldNode) {
+ newParentNode.left = newNode;
+ } else {
+ Debug.Assert(newParentNode.right == oldNode);
+ newParentNode.right = newNode;
+ }
+ if (parentNode == newParentNode) {
+ // we were able to change the existing node (it was not shared);
+ // there's no reason to go further upwards
+ ClearCacheOnModification();
+ return;
+ } else {
+ oldNode = parentNode;
+ newNode = newParentNode;
+ }
+ }
+ // we reached the root of the rope.
+ Debug.Assert(root == oldNode);
+ root = newNode;
+ ClearCacheOnModification();
+ }*/
+ }
+ }
+
+ internal ImmutableStack<RopeCacheEntry> FindNodeUsingCache(int index)
+ {
+ Debug.Assert(index >= 0 && index < this.Length);
+
+ // thread safety: fetch stack into local variable
+ ImmutableStack<RopeCacheEntry> stack = lastUsedNodeStack;
+ ImmutableStack<RopeCacheEntry> oldStack = stack;
+
+ if (stack == null) {
+ stack = ImmutableStack<RopeCacheEntry>.Empty.Push(new RopeCacheEntry(root, 0));
+ }
+ while (!stack.PeekOrDefault().IsInside(index))
+ stack = stack.Pop();
+ while (true) {
+ RopeCacheEntry entry = stack.PeekOrDefault();
+ // check if we've reached a leaf or function node
+ if (entry.node.height == 0) {
+ if (entry.node.contents == null) {
+ // this is a function node - go down into its subtree
+ entry = new RopeCacheEntry(entry.node.GetContentNode(), entry.nodeStartIndex);
+ // entry is now guaranteed NOT to be another function node
+ }
+ if (entry.node.contents != null) {
+ // this is a node containing actual content, so we're done
+ break;
+ }
+ }
+ // go down towards leaves
+ if (index - entry.nodeStartIndex >= entry.node.left.length)
+ stack = stack.Push(new RopeCacheEntry(entry.node.right, entry.nodeStartIndex + entry.node.left.length));
+ else
+ stack = stack.Push(new RopeCacheEntry(entry.node.left, entry.nodeStartIndex));
+ }
+
+ // write back stack to volatile cache variable
+ // (in multithreaded access, it doesn't matter which of the threads wins - it's just a cache)
+ if (oldStack != stack) {
+ // no need to write when we the cache variable didn't change
+ lastUsedNodeStack = stack;
+ }
+
+ // this method guarantees that it finds a leaf node
+ Debug.Assert(stack.Peek().node.contents != null);
+ return stack;
+ }
+ #endregion
+
+ #region ToString / WriteTo
+ internal void VerifyRange(int startIndex, int length)
+ {
+ if (startIndex < 0 || startIndex > this.Length) {
+ throw new ArgumentOutOfRangeException("startIndex", startIndex, "0 <= startIndex <= " + this.Length.ToString(CultureInfo.InvariantCulture));
+ }
+ if (length < 0 || startIndex + length > this.Length) {
+ throw new ArgumentOutOfRangeException("length", length, "0 <= length, startIndex(" + startIndex + ")+length <= " + this.Length.ToString(CultureInfo.InvariantCulture));
+ }
+ }
+
+ internal static void VerifyArrayWithRange(T[] array, int arrayIndex, int count)
+ {
+ if (array == null)
+ throw new ArgumentNullException("array");
+ if (arrayIndex < 0 || arrayIndex > array.Length) {
+ throw new ArgumentOutOfRangeException("startIndex", arrayIndex, "0 <= arrayIndex <= " + array.Length.ToString(CultureInfo.InvariantCulture));
+ }
+ if (count < 0 || arrayIndex + count > array.Length) {
+ throw new ArgumentOutOfRangeException("count", count, "0 <= length, arrayIndex(" + arrayIndex + ")+count <= " + array.Length.ToString(CultureInfo.InvariantCulture));
+ }
+ }
+
+ /// <summary>
+ /// Creates a string from the rope. Runs in O(N).
+ /// </summary>
+ /// <returns>A string consisting of all elements in the rope as comma-separated list in {}.
+ /// As a special case, Rope&lt;char&gt; will return its contents as string without any additional separators or braces,
+ /// so it can be used like StringBuilder.ToString().</returns>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public override string ToString()
+ {
+ Rope<char> charRope = this as Rope<char>;
+ if (charRope != null) {
+ return charRope.ToString(0, this.Length);
+ } else {
+ StringBuilder b = new StringBuilder();
+ foreach (T element in this) {
+ if (b.Length == 0)
+ b.Append('{');
+ else
+ b.Append(", ");
+ b.Append(element.ToString());
+ }
+ b.Append('}');
+ return b.ToString();
+ }
+ }
+
+ internal string GetTreeAsString()
+ {
+ #if DEBUG
+ return root.GetTreeAsString();
+ #else
+ return "Not available in release build.";
+ #endif
+ }
+ #endregion
+
+ bool ICollection<T>.IsReadOnly {
+ get { return false; }
+ }
+
+ /// <summary>
+ /// Finds the first occurance of item.
+ /// Runs in O(N).
+ /// </summary>
+ /// <returns>The index of the first occurance of item, or -1 if it cannot be found.</returns>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public int IndexOf(T item)
+ {
+ return IndexOf(item, 0, this.Length);
+ }
+
+ /// <summary>
+ /// Gets the index of the first occurrence the specified item.
+ /// </summary>
+ /// <param name="item">Item to search for.</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 the item was found; or -1 if no occurrence was found.</returns>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public int IndexOf(T item, int startIndex, int count)
+ {
+ VerifyRange(startIndex, count);
+
+ while (count > 0) {
+ var entry = FindNodeUsingCache(startIndex).PeekOrDefault();
+ T[] contents = entry.node.contents;
+ int startWithinNode = startIndex - entry.nodeStartIndex;
+ int nodeLength = System.Math.Min(entry.node.length, startWithinNode + count);
+ int r = Array.IndexOf(contents, item, startWithinNode, nodeLength - startWithinNode);
+ if (r >= 0)
+ return entry.nodeStartIndex + r;
+ count -= nodeLength - startWithinNode;
+ startIndex = entry.nodeStartIndex + nodeLength;
+ }
+ return -1;
+ }
+
+ /// <summary>
+ /// Gets the index of the last occurrence of the specified item in this rope.
+ /// </summary>
+ public int LastIndexOf(T item)
+ {
+ return LastIndexOf(item, 0, this.Length);
+ }
+
+ /// <summary>
+ /// Gets the index of the last occurrence of the specified item in this rope.
+ /// </summary>
+ /// <param name="item">The search item</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 item 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 Array.LastIndexOf!</remarks>
+ public int LastIndexOf(T item, int startIndex, int count)
+ {
+ VerifyRange(startIndex, count);
+
+ var comparer = EqualityComparer<T>.Default;
+ for (int i = startIndex + count - 1; i >= startIndex; i--) {
+ if (comparer.Equals(this[i], item))
+ return i;
+ }
+ return -1;
+ }
+
+ /// <summary>
+ /// Inserts the item at the specified index in the rope.
+ /// Runs in O(lg N).
+ /// </summary>
+ public void Insert(int index, T item)
+ {
+ InsertRange(index, new[] { item }, 0, 1);
+ }
+
+ /// <summary>
+ /// Removes a single item from the rope.
+ /// Runs in O(lg N).
+ /// </summary>
+ public void RemoveAt(int index)
+ {
+ RemoveRange(index, 1);
+ }
+
+ /// <summary>
+ /// Appends the item at the end of the rope.
+ /// Runs in O(lg N).
+ /// </summary>
+ public void Add(T item)
+ {
+ InsertRange(this.Length, new[] { item }, 0, 1);
+ }
+
+ /// <summary>
+ /// Searches the item in the rope.
+ /// Runs in O(N).
+ /// </summary>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public bool Contains(T item)
+ {
+ return IndexOf(item) >= 0;
+ }
+
+ /// <summary>
+ /// Copies the whole content of the rope into the specified array.
+ /// Runs in O(N).
+ /// </summary>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public void CopyTo(T[] array, int arrayIndex)
+ {
+ CopyTo(0, array, arrayIndex, this.Length);
+ }
+
+ /// <summary>
+ /// Copies the a part of the rope into the specified array.
+ /// Runs in O(lg N + M).
+ /// </summary>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public void CopyTo(int index, T[] array, int arrayIndex, int count)
+ {
+ VerifyRange(index, count);
+ VerifyArrayWithRange(array, arrayIndex, count);
+ this.root.CopyTo(index, array, arrayIndex, count);
+ }
+
+ /// <summary>
+ /// Removes the first occurance of an item from the rope.
+ /// Runs in O(N).
+ /// </summary>
+ public bool Remove(T item)
+ {
+ int index = IndexOf(item);
+ if (index >= 0) {
+ RemoveAt(index);
+ return true;
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Retrieves an enumerator to iterate through the rope.
+ /// The enumerator will reflect the state of the rope from the GetEnumerator() call, further modifications
+ /// to the rope will not be visible to the enumerator.
+ /// </summary>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public IEnumerator<T> GetEnumerator()
+ {
+ this.root.Publish();
+ return Enumerate(root);
+ }
+
+ /// <summary>
+ /// Creates an array and copies the contents of the rope into it.
+ /// Runs in O(N).
+ /// </summary>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public T[] ToArray()
+ {
+ T[] arr = new T[this.Length];
+ this.root.CopyTo(0, arr, 0, arr.Length);
+ return arr;
+ }
+
+ /// <summary>
+ /// Creates an array and copies the contents of the rope into it.
+ /// Runs in O(N).
+ /// </summary>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public T[] ToArray(int startIndex, int count)
+ {
+ VerifyRange(startIndex, count);
+ T[] arr = new T[count];
+ CopyTo(startIndex, arr, 0, count);
+ return arr;
+ }
+
+ static IEnumerator<T> Enumerate(RopeNode<T> node)
+ {
+ Stack<RopeNode<T>> stack = new Stack<RopeNode<T>>();
+ while (node != null) {
+ // go to leftmost node, pushing the right parts that we'll have to visit later
+ while (node.contents == null) {
+ if (node.height == 0) {
+ // go down into function nodes
+ node = node.GetContentNode();
+ continue;
+ }
+ Debug.Assert(node.right != null);
+ stack.Push(node.right);
+ node = node.left;
+ }
+ // yield contents of leaf node
+ for (int i = 0; i < node.length; i++) {
+ yield return node.contents[i];
+ }
+ // go up to the next node not visited yet
+ if (stack.Count > 0)
+ node = stack.Pop();
+ else
+ node = null;
+ }
+ }
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+ {
+ return this.GetEnumerator();
+ }
+ }
+}
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor.Utils/RopeNode.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor.Utils/RopeNode.cs
new file mode 100644
index 0000000000..bc6b5bade7
--- /dev/null
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor.Utils/RopeNode.cs
@@ -0,0 +1,621 @@
+// 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.Diagnostics;
+using System.Runtime.Serialization;
+
+using System.Text;
+
+namespace Mono.TextEditor.Utils
+{
+ // Class used to represent a node in the tree.
+ // There are three types of nodes:
+ // Concat nodes: height>0, left!=null, right!=null, contents==null
+ // Leaf nodes: height==0, left==null, right==null, contents!=null
+ // Function nodes: height==0, left==null, right==null, contents==null, are of type FunctionNode<T>
+
+ [Serializable]
+ class RopeNode<T>
+ {
+ internal const int NodeSize = 256;
+
+ internal static readonly RopeNode<T> emptyRopeNode = new RopeNode<T> { isShared = true, contents = new T[RopeNode<T>.NodeSize] };
+
+ // Fields for pointers to sub-nodes. Only non-null for concat nodes (height>=1)
+ internal RopeNode<T> left, right;
+ internal volatile bool isShared; // specifies whether this node is shared between multiple ropes
+ // the total length of all text in this subtree
+ internal int length;
+ // the height of this subtree: 0 for leaf nodes; 1+max(left.height,right.height) for concat nodes
+ internal byte height;
+
+ // The character data. Only non-null for leaf nodes (height=0) that aren't function nodes.
+ internal T[] contents;
+
+ internal int Balance {
+ get { return right.height - left.height; }
+ }
+
+ [Conditional("DATACONSISTENCYTEST")]
+ internal void CheckInvariants()
+ {
+ if (height == 0) {
+ Debug.Assert(left == null && right == null);
+ if (contents == null) {
+ Debug.Assert(this is FunctionNode<T>);
+ Debug.Assert(length > 0);
+ Debug.Assert(isShared);
+ } else {
+ Debug.Assert(contents != null && contents.Length == NodeSize);
+ Debug.Assert(length >= 0 && length <= NodeSize);
+ }
+ } else {
+ Debug.Assert(left != null && right != null);
+ Debug.Assert(contents == null);
+ Debug.Assert(length == left.length + right.length);
+ Debug.Assert(height == 1 + System.Math.Max(left.height, right.height));
+ Debug.Assert(System.Math.Abs(this.Balance) <= 1);
+
+ // this is an additional invariant that forces the tree to combine small leafs to prevent excessive memory usage:
+ Debug.Assert(length > NodeSize);
+ // note that this invariant ensures that all nodes except for the empty rope's single node have at least length 1
+
+ if (isShared)
+ Debug.Assert(left.isShared && right.isShared);
+ left.CheckInvariants();
+ right.CheckInvariants();
+ }
+ }
+
+ internal RopeNode<T> Clone()
+ {
+ if (height == 0) {
+ // If a function node needs cloning, we'll evaluate it.
+ if (contents == null)
+ return GetContentNode().Clone();
+ T[] newContents = new T[NodeSize];
+ contents.CopyTo(newContents, 0);
+ return new RopeNode<T> {
+ length = this.length,
+ contents = newContents
+ };
+ } else {
+ return new RopeNode<T> {
+ left = this.left,
+ right = this.right,
+ length = this.length,
+ height = this.height
+ };
+ }
+ }
+
+ internal RopeNode<T> CloneIfShared()
+ {
+ if (isShared)
+ return Clone();
+ else
+ return this;
+ }
+
+ internal void Publish()
+ {
+ if (!isShared) {
+ if (left != null)
+ left.Publish();
+ if (right != null)
+ right.Publish();
+ // it's important that isShared=true is set at the end:
+ // Publish() must not return until the whole subtree is marked as shared, even when
+ // Publish() is called concurrently.
+ isShared = true;
+ }
+ }
+
+ internal static RopeNode<T> CreateFromArray(T[] arr, int index, int length)
+ {
+ if (length == 0) {
+ return emptyRopeNode;
+ }
+ RopeNode<T> node = CreateNodes(length);
+ return node.StoreElements(0, arr, index, length);
+ }
+
+ internal static RopeNode<T> CreateNodes(int totalLength)
+ {
+ int leafCount = (totalLength + NodeSize - 1) / NodeSize;
+ return CreateNodes(leafCount, totalLength);
+ }
+
+ static RopeNode<T> CreateNodes(int leafCount, int totalLength)
+ {
+ Debug.Assert(leafCount > 0);
+ Debug.Assert(totalLength > 0);
+ RopeNode<T> result = new RopeNode<T>();
+ result.length = totalLength;
+ if (leafCount == 1) {
+ result.contents = new T[NodeSize];
+ } else {
+ int rightSide = leafCount / 2;
+ int leftSide = leafCount - rightSide;
+ int leftLength = leftSide * NodeSize;
+ result.left = CreateNodes(leftSide, leftLength);
+ result.right = CreateNodes(rightSide, totalLength - leftLength);
+ result.height = (byte)(1 + System.Math.Max(result.left.height, result.right.height));
+ }
+ return result;
+ }
+
+ /// <summary>
+ /// Balances this node and recomputes the 'height' field.
+ /// This method assumes that the children of this node are already balanced and have an up-to-date 'height' value.
+ /// </summary>
+ internal void Rebalance()
+ {
+ // Rebalance() shouldn't be called on shared nodes - it's only called after modifications!
+ Debug.Assert(!isShared);
+ // leaf nodes are always balanced (we don't use 'height' to detect leaf nodes here
+ // because Balance is supposed to recompute the height).
+ if (left == null)
+ return;
+
+ // ensure we didn't miss a MergeIfPossible step
+ Debug.Assert(this.length > NodeSize);
+
+ // We need to loop until it's balanced. Rotations might cause two small leaves to combine to a larger one,
+ // which changes the height and might mean we need additional balancing steps.
+ while (System.Math.Abs(this.Balance) > 1) {
+ // AVL balancing
+ // note: because we don't care about the identity of concat nodes, this works a little different than usual
+ // tree rotations: in our implementation, the "this" node will stay at the top, only its children are rearranged
+ if (this.Balance > 1) {
+ if (right.Balance < 0) {
+ right = right.CloneIfShared();
+ right.RotateRight();
+ }
+ this.RotateLeft();
+ // If 'this' was unbalanced by more than 2, we've shifted some of the inbalance to the left node; so rebalance that.
+ this.left.Rebalance();
+ } else if (this.Balance < -1) {
+ if (left.Balance > 0) {
+ left = left.CloneIfShared();
+ left.RotateLeft();
+ }
+ this.RotateRight();
+ // If 'this' was unbalanced by more than 2, we've shifted some of the inbalance to the right node; so rebalance that.
+ this.right.Rebalance();
+ }
+ }
+
+ Debug.Assert(System.Math.Abs(this.Balance) <= 1);
+ this.height = (byte)(1 + System.Math.Max(left.height, right.height));
+ }
+
+ void RotateLeft()
+ {
+ Debug.Assert(!isShared);
+
+ /* Rotate tree to the left
+ *
+ * this this
+ * / \ / \
+ * A right ===> left C
+ * / \ / \
+ * B C A B
+ */
+ RopeNode<T> a = left;
+ RopeNode<T> b = right.left;
+ RopeNode<T> c = right.right;
+ // reuse right concat node, if possible
+ this.left = right.isShared ? new RopeNode<T>() : right;
+ this.left.left = a;
+ this.left.right = b;
+ this.left.length = a.length + b.length;
+ this.left.height = (byte)(1 + System.Math.Max(a.height, b.height));
+ this.right = c;
+
+ this.left.MergeIfPossible();
+ }
+
+ void RotateRight()
+ {
+ Debug.Assert(!isShared);
+
+ /* Rotate tree to the right
+ *
+ * this this
+ * / \ / \
+ * left C ===> A right
+ * / \ / \
+ * A B B C
+ */
+ RopeNode<T> a = left.left;
+ RopeNode<T> b = left.right;
+ RopeNode<T> c = right;
+ // reuse left concat node, if possible
+ this.right = left.isShared ? new RopeNode<T>() : left;
+ this.right.left = b;
+ this.right.right = c;
+ this.right.length = b.length + c.length;
+ this.right.height = (byte)(1 + System.Math.Max(b.height, c.height));
+ this.left = a;
+
+ this.right.MergeIfPossible();
+ }
+ static readonly T[] emptyArr = new T[0];
+
+ void MergeIfPossible()
+ {
+ Debug.Assert(!isShared);
+
+ if (this.length <= NodeSize) {
+ // Convert this concat node to leaf node.
+ // We know left and right cannot be concat nodes (they would have merged already),
+ // but they could be function nodes.
+ this.height = 0;
+ int lengthOnLeftSide = this.left.length;
+ if (this.left.isShared) {
+ this.contents = new T[NodeSize];
+ left.CopyTo(0, this.contents, 0, lengthOnLeftSide);
+ } else {
+ // must be a leaf node: function nodes are always marked shared
+ Debug.Assert(this.left.contents != null);
+ // steal buffer from left side
+ this.contents = this.left.contents;
+ #if DEBUG
+ // In debug builds, explicitly mark left node as 'damaged' - but no one else should be using it
+ // because it's not shared.
+ this.left.contents = emptyArr;
+ #endif
+ }
+ this.left = null;
+ right.CopyTo(0, this.contents, lengthOnLeftSide, this.right.length);
+ this.right = null;
+ }
+ }
+
+ /// <summary>
+ /// Copies from the array to this node.
+ /// </summary>
+ internal RopeNode<T> StoreElements(int index, T[] array, int arrayIndex, int count)
+ {
+ RopeNode<T> result = this.CloneIfShared();
+ // result cannot be function node after a call to Clone()
+ if (result.height == 0) {
+ // leaf node:
+ Array.Copy(array, arrayIndex, result.contents, index, count);
+ } else {
+ // concat node:
+ if (index + count <= result.left.length) {
+ result.left = result.left.StoreElements(index, array, arrayIndex, count);
+ } else if (index >= this.left.length) {
+ result.right = result.right.StoreElements(index - result.left.length, array, arrayIndex, count);
+ } else {
+ int amountInLeft = result.left.length - index;
+ result.left = result.left.StoreElements(index, array, arrayIndex, amountInLeft);
+ result.right = result.right.StoreElements(0, array, arrayIndex + amountInLeft, count - amountInLeft);
+ }
+ result.Rebalance(); // tree layout might have changed if function nodes were replaced with their content
+ }
+ return result;
+ }
+
+ /// <summary>
+ /// Copies from this node to the array.
+ /// </summary>
+ internal void CopyTo(int index, T[] array, int arrayIndex, int count)
+ {
+ if (height == 0) {
+ if (this.contents == null) {
+ // function node
+ this.GetContentNode().CopyTo(index, array, arrayIndex, count);
+ } else {
+ // leaf node
+ Array.Copy(this.contents, index, array, arrayIndex, count);
+ }
+ } else {
+ // concat node
+ if (index + count <= this.left.length) {
+ this.left.CopyTo(index, array, arrayIndex, count);
+ } else if (index >= this.left.length) {
+ this.right.CopyTo(index - this.left.length, array, arrayIndex, count);
+ } else {
+ int amountInLeft = this.left.length - index;
+ this.left.CopyTo(index, array, arrayIndex, amountInLeft);
+ this.right.CopyTo(0, array, arrayIndex + amountInLeft, count - amountInLeft);
+ }
+ }
+ }
+
+ internal RopeNode<T> SetElement(int offset, T value)
+ {
+ RopeNode<T> result = CloneIfShared();
+ // result of CloneIfShared() is leaf or concat node
+ if (result.height == 0) {
+ result.contents[offset] = value;
+ } else {
+ if (offset < result.left.length) {
+ result.left = result.left.SetElement(offset, value);
+ } else {
+ result.right = result.right.SetElement(offset - result.left.length, value);
+ }
+ result.Rebalance(); // tree layout might have changed if function nodes were replaced with their content
+ }
+ return result;
+ }
+
+ internal static RopeNode<T> Concat(RopeNode<T> left, RopeNode<T> right)
+ {
+ if (left.length == 0)
+ return right;
+ if (right.length == 0)
+ return left;
+
+ if (left.length + right.length <= NodeSize) {
+ left = left.CloneIfShared();
+ // left is guaranteed to be leaf node after cloning:
+ // - it cannot be function node (due to clone)
+ // - it cannot be concat node (too short)
+ right.CopyTo(0, left.contents, left.length, right.length);
+ left.length += right.length;
+ return left;
+ } else {
+ RopeNode<T> concatNode = new RopeNode<T>();
+ concatNode.left = left;
+ concatNode.right = right;
+ concatNode.length = left.length + right.length;
+ concatNode.Rebalance();
+ return concatNode;
+ }
+ }
+
+ /// <summary>
+ /// Splits this leaf node at offset and returns a new node with the part of the text after offset.
+ /// </summary>
+ RopeNode<T> SplitAfter(int offset)
+ {
+ Debug.Assert(!isShared && height == 0 && contents != null);
+ RopeNode<T> newPart = new RopeNode<T>();
+ newPart.contents = new T[NodeSize];
+ newPart.length = this.length - offset;
+ Array.Copy(this.contents, offset, newPart.contents, 0, newPart.length);
+ this.length = offset;
+ return newPart;
+ }
+
+ internal RopeNode<T> Insert(int offset, RopeNode<T> newElements)
+ {
+ if (offset == 0) {
+ return Concat(newElements, this);
+ } else if (offset == this.length) {
+ return Concat(this, newElements);
+ }
+
+ // first clone this node (converts function nodes to leaf or concat nodes)
+ RopeNode<T> result = CloneIfShared();
+ if (result.height == 0) {
+ // leaf node: we'll need to split this node
+ RopeNode<T> left = result;
+ RopeNode<T> right = left.SplitAfter(offset);
+ return Concat(Concat(left, newElements), right);
+ } else {
+ // concat node
+ if (offset < result.left.length) {
+ result.left = result.left.Insert(offset, newElements);
+ } else {
+ result.right = result.right.Insert(offset - result.left.length, newElements);
+ }
+ result.length += newElements.length;
+ result.Rebalance();
+ return result;
+ }
+ }
+
+ internal RopeNode<T> Insert(int offset, T[] array, int arrayIndex, int count)
+ {
+ Debug.Assert(count > 0);
+
+ if (this.length + count < RopeNode<char>.NodeSize) {
+ RopeNode<T> result = CloneIfShared();
+ // result must be leaf node (Clone never returns function nodes, too short for concat node)
+ int lengthAfterOffset = result.length - offset;
+ T[] resultContents = result.contents;
+ for (int i = lengthAfterOffset; i >= 0; i--) {
+ resultContents[i + offset + count] = resultContents[i + offset];
+ }
+ Array.Copy(array, arrayIndex, resultContents, offset, count);
+ result.length += count;
+ return result;
+ } else if (height == 0) {
+ // TODO: implement this more efficiently?
+ return Insert(offset, CreateFromArray(array, arrayIndex, count));
+ } else {
+ // this is a concat node (both leafs and function nodes are handled by the case above)
+ RopeNode<T> result = CloneIfShared();
+ if (offset < result.left.length) {
+ result.left = result.left.Insert(offset, array, arrayIndex, count);
+ } else {
+ result.right = result.right.Insert(offset - result.left.length, array, arrayIndex, count);
+ }
+ result.length += count;
+ result.Rebalance();
+ return result;
+ }
+ }
+
+ internal RopeNode<T> RemoveRange(int index, int count)
+ {
+ Debug.Assert(count > 0);
+
+ // produce empty node when one node is deleted completely
+ if (index == 0 && count == this.length)
+ return emptyRopeNode;
+
+ int endIndex = index + count;
+ RopeNode<T> result = CloneIfShared(); // convert function node to concat/leaf
+ if (result.height == 0) {
+ int remainingAfterEnd = result.length - endIndex;
+ for (int i = 0; i < remainingAfterEnd; i++) {
+ result.contents[index + i] = result.contents[endIndex + i];
+ }
+ result.length -= count;
+ } else {
+ if (endIndex <= result.left.length) {
+ // deletion is only within the left part
+ result.left = result.left.RemoveRange(index, count);
+ } else if (index >= result.left.length) {
+ // deletion is only within the right part
+ result.right = result.right.RemoveRange(index - result.left.length, count);
+ } else {
+ // deletion overlaps both parts
+ int deletionAmountOnLeftSide = result.left.length - index;
+ result.left = result.left.RemoveRange(index, deletionAmountOnLeftSide);
+ result.right = result.right.RemoveRange(0, count - deletionAmountOnLeftSide);
+ }
+ // The deletion might have introduced empty nodes. Those must be removed.
+ if (result.left.length == 0)
+ return result.right;
+ if (result.right.length == 0)
+ return result.left;
+
+ result.length -= count;
+ result.MergeIfPossible();
+ result.Rebalance();
+ }
+ return result;
+ }
+
+ #region Debug Output
+ #if DEBUG
+ internal virtual void AppendTreeToString(StringBuilder b, int indent)
+ {
+ b.AppendLine(ToString());
+ indent += 2;
+ if (left != null) {
+ b.Append(' ', indent);
+ b.Append("L: ");
+ left.AppendTreeToString(b, indent);
+ }
+ if (right != null) {
+ b.Append(' ', indent);
+ b.Append("R: ");
+ right.AppendTreeToString(b, indent);
+ }
+ }
+
+ public override string ToString()
+ {
+ if (contents != null) {
+ char[] charContents = contents as char[];
+ if (charContents != null)
+ return "[Leaf length=" + length + ", isShared=" + isShared + ", text=\"" + new string(charContents, 0, length) + "\"]";
+ else
+ return "[Leaf length=" + length + ", isShared=" + isShared + "\"]";
+ } else {
+ return "[Concat length=" + length + ", isShared=" + isShared + ", height=" + height + ", Balance=" + this.Balance + "]";
+ }
+ }
+
+ internal string GetTreeAsString()
+ {
+ StringBuilder b = new StringBuilder();
+ AppendTreeToString(b, 0);
+ return b.ToString();
+ }
+ #endif
+ #endregion
+
+ /// <summary>
+ /// Gets the root node of the subtree from a lazily evaluated function node.
+ /// Such nodes are always marked as shared.
+ /// GetContentNode() will return either a Concat or Leaf node, never another FunctionNode.
+ /// </summary>
+ internal virtual RopeNode<T> GetContentNode()
+ {
+ throw new InvalidOperationException("Called GetContentNode() on non-FunctionNode.");
+ }
+ }
+
+ sealed class FunctionNode<T> : RopeNode<T>
+ {
+ Func<Rope<T>> initializer;
+ RopeNode<T> cachedResults;
+
+ public FunctionNode(int length, Func<Rope<T>> initializer)
+ {
+ Debug.Assert(length > 0);
+ Debug.Assert(initializer != null);
+
+ this.length = length;
+ this.initializer = initializer;
+ // Function nodes are immediately shared, but cannot be cloned.
+ // This ensures we evaluate every initializer only once.
+ this.isShared = true;
+ }
+
+ internal override RopeNode<T> GetContentNode()
+ {
+ lock (this) {
+ if (this.cachedResults == null) {
+ if (this.initializer == null)
+ throw new InvalidOperationException("Trying to load this node recursively; or: a previous call to a rope initializer failed.");
+ Func<Rope<T>> initializerCopy = this.initializer;
+ this.initializer = null;
+ Rope<T> resultRope = initializerCopy();
+ if (resultRope == null)
+ throw new InvalidOperationException("Rope initializer returned null.");
+ RopeNode<T> resultNode = resultRope.root;
+ resultNode.Publish(); // result is shared between returned rope and the rope containing this function node
+ if (resultNode.length != this.length)
+ throw new InvalidOperationException("Rope initializer returned rope with incorrect length.");
+ if (resultNode.height == 0 && resultNode.contents == null) {
+ // ResultNode is another function node.
+ // We want to guarantee that GetContentNode() never returns function nodes, so we have to
+ // go down further in the tree.
+ this.cachedResults = resultNode.GetContentNode();
+ } else {
+ this.cachedResults = resultNode;
+ }
+ }
+ return this.cachedResults;
+ }
+ }
+
+ #if DEBUG
+ internal override void AppendTreeToString(StringBuilder b, int indent)
+ {
+ RopeNode<T> resultNode;
+ lock (this) {
+ b.AppendLine(ToString());
+ resultNode = cachedResults;
+ }
+ indent += 2;
+ if (resultNode != null) {
+ b.Append(' ', indent);
+ b.Append("C: ");
+ resultNode.AppendTreeToString(b, indent);
+ }
+ }
+
+ public override string ToString()
+ {
+ return "[FunctionNode length=" + length + " initializerRan=" + (initializer == null) + "]";
+ }
+ #endif
+ }
+}
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor.Utils/RopeTextReader.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor.Utils/RopeTextReader.cs
new file mode 100644
index 0000000000..b6827683db
--- /dev/null
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor.Utils/RopeTextReader.cs
@@ -0,0 +1,120 @@
+// 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.IO;
+
+namespace Mono.TextEditor.Utils
+{
+ /// <summary>
+ /// TextReader implementation that reads text from a rope.
+ /// </summary>
+ public sealed class RopeTextReader : TextReader
+ {
+ Stack<RopeNode<char>> stack = new Stack<RopeNode<char>>();
+ RopeNode<char> currentNode;
+ int indexInsideNode;
+
+ /// <summary>
+ /// Creates a new RopeTextReader.
+ /// Internally, this method creates a Clone of the rope; so the text reader will always read through the old
+ /// version of the rope if it is modified. <seealso cref="Rope{T}.Clone()"/>
+ /// </summary>
+ public RopeTextReader(Rope<char> rope)
+ {
+ if (rope == null)
+ throw new ArgumentNullException("rope");
+
+ // We force the user to iterate through a clone of the rope to keep the API contract of RopeTextReader simple
+ // (what happens when a rope is modified while iterating through it?)
+ rope.root.Publish();
+
+ // special case for the empty rope:
+ // leave currentNode initialized to null (RopeTextReader doesn't support empty nodes)
+ if (rope.Length != 0) {
+ currentNode = rope.root;
+ GoToLeftMostLeaf();
+ }
+ }
+
+ void GoToLeftMostLeaf()
+ {
+ while (currentNode.contents == null) {
+ if (currentNode.height == 0) {
+ // this is a function node - move to its contained rope
+ currentNode = currentNode.GetContentNode();
+ continue;
+ }
+ Debug.Assert(currentNode.right != null);
+ stack.Push(currentNode.right);
+ currentNode = currentNode.left;
+ }
+ Debug.Assert(currentNode.height == 0);
+ }
+
+ /// <inheritdoc/>
+ public override int Peek()
+ {
+ if (currentNode == null)
+ return -1;
+ return currentNode.contents[indexInsideNode];
+ }
+
+ /// <inheritdoc/>
+ public override int Read()
+ {
+ if (currentNode == null)
+ return -1;
+ char result = currentNode.contents[indexInsideNode++];
+ if (indexInsideNode >= currentNode.length)
+ GoToNextNode();
+ return result;
+ }
+
+ void GoToNextNode()
+ {
+ if (stack.Count == 0) {
+ currentNode = null;
+ } else {
+ indexInsideNode = 0;
+ currentNode = stack.Pop();
+ GoToLeftMostLeaf();
+ }
+ }
+
+ /// <inheritdoc/>
+ public override int Read(char[] buffer, int index, int count)
+ {
+ if (currentNode == null)
+ return 0;
+ int amountInCurrentNode = currentNode.length - indexInsideNode;
+ if (count < amountInCurrentNode) {
+ Array.Copy(currentNode.contents, indexInsideNode, buffer, index, count);
+ indexInsideNode += count;
+ return count;
+ } else {
+ // read to end of current node
+ Array.Copy(currentNode.contents, indexInsideNode, buffer, index, amountInCurrentNode);
+ GoToNextNode();
+ return amountInCurrentNode;
+ }
+ }
+ }
+}
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor.Utils/TextBreaker.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor.Utils/TextBreaker.cs
index e96caf39af..4e87370e22 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor.Utils/TextBreaker.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor.Utils/TextBreaker.cs
@@ -49,7 +49,7 @@ namespace Mono.TextEditor.Utils
/// <param name='lineCount'>
/// The number of lines to get words from
/// </param>
- public static List<TextSegment> BreakLinesIntoWords (TextEditor editor, int startLine, int lineCount, bool includeDelimiter = true)
+ public static List<TextSegment> BreakLinesIntoWords (MonoTextEditor editor, int startLine, int lineCount, bool includeDelimiter = true)
{
return BreakLinesIntoWords (editor.Document, startLine, lineCount, includeDelimiter);
}
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor.Vi/NewViEditMode.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor.Vi/NewViEditMode.cs
index 2527e953bd..e83d383e32 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor.Vi/NewViEditMode.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor.Vi/NewViEditMode.cs
@@ -35,7 +35,7 @@ namespace Mono.TextEditor.Vi
public class NewViEditMode : EditMode
{
ViStatusArea statusArea;
- TextEditor viTextEditor;
+ MonoTextEditor viTextEditor;
protected ViEditor ViEditor { get ; private set ;}
@@ -75,7 +75,7 @@ namespace Mono.TextEditor.Vi
}
}
- public override void AllocateTextArea (TextEditor textEditor, TextArea textArea, Gdk.Rectangle allocation)
+ public override void AllocateTextArea (MonoTextEditor textEditor, TextArea textArea, Gdk.Rectangle allocation)
{
statusArea.AllocateArea (textArea, allocation);
}
@@ -85,7 +85,7 @@ namespace Mono.TextEditor.Vi
ViEditor.ProcessKey (modifier, key, (char)unicodeKey);
}
- public new TextEditor Editor { get { return base.Editor; } }
+ public new MonoTextEditor Editor { get { return base.Editor; } }
public new TextEditorData Data { get { return base.Data; } }
public override bool WantsToPreemptIM {
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor.Vi/ViEditor.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor.Vi/ViEditor.cs
index 83110bd41e..9cd39fdd76 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor.Vi/ViEditor.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor.Vi/ViEditor.cs
@@ -55,7 +55,7 @@ namespace Mono.TextEditor.Vi
Dictionary<char,string> registers = new Dictionary<char,string> ();
Dictionary<char,ViMark> marks = new Dictionary<char, ViMark> ();
- public TextEditor Editor { get { return editMode.Editor; } }
+ public MonoTextEditor Editor { get { return editMode.Editor; } }
public TextEditorData Data { get { return editMode.Data; } }
public TextDocument Document { get { return Data.Document; } }
ViBuilderContext Context { get; set; }
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor.Vi/ViMode.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor.Vi/ViMode.cs
index 9eb1a3164c..83d8caee06 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor.Vi/ViMode.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor.Vi/ViMode.cs
@@ -187,7 +187,7 @@ namespace Mono.TextEditor.Vi
}
ViStatusArea statusArea;
- TextEditor viTextEditor;
+ MonoTextEditor viTextEditor;
void CheckVisualMode ()
{
@@ -243,7 +243,7 @@ namespace Mono.TextEditor.Vi
}
}
- public override void AllocateTextArea (TextEditor textEditor, TextArea textArea, Gdk.Rectangle allocation)
+ public override void AllocateTextArea (MonoTextEditor textEditor, TextArea textArea, Gdk.Rectangle allocation)
{
statusArea.AllocateArea (textArea, allocation);
}
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor.Vi/ViStatusArea.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor.Vi/ViStatusArea.cs
index 6c768d7935..3b26ada8e7 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor.Vi/ViStatusArea.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor.Vi/ViStatusArea.cs
@@ -31,18 +31,18 @@ namespace Mono.TextEditor.Vi
{
class ViStatusArea : Gtk.DrawingArea
{
- TextEditor editor;
+ MonoTextEditor editor;
bool showCaret;
string statusText;
- public ViStatusArea (TextEditor editor)
+ public ViStatusArea (MonoTextEditor editor)
{
this.editor = editor;
editor.TextViewMargin.CaretBlink += HandleCaretBlink;
editor.Caret.PositionChanged += HandlePositionChanged;
editor.AddTopLevelWidget (this, 0, 0);
- ((TextEditor.EditorContainerChild)editor[this]).FixedPosition = true;
+ ((MonoTextEditor.EditorContainerChild)editor[this]).FixedPosition = true;
Show ();
}
@@ -83,7 +83,7 @@ namespace Mono.TextEditor.Vi
if (textArea.Allocation != allocation) {
textArea.SizeAllocate (allocation);
SetSizeRequest (allocation.Width, (int)editor.LineHeight);
- var pos = ((TextEditor.EditorContainerChild)editor [this]);
+ var pos = ((MonoTextEditor.EditorContainerChild)editor [this]);
if (pos.X != 0 || pos.Y != allocation.Height)
editor.MoveTopLevelWidget (this, 0, allocation.Height);
}
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor.csproj b/main/src/core/Mono.Texteditor/Mono.TextEditor.csproj
index fde0ebbec9..0f798d61d4 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor.csproj
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor.csproj
@@ -168,9 +168,7 @@
<Compile Include="Mono.TextEditor\Document\DocumentLocation.cs" />
<Compile Include="Mono.TextEditor\Document\DocumentRegion.cs" />
<Compile Include="Mono.TextEditor\Document\DocumentUpdateRequest.cs" />
- <Compile Include="Mono.TextEditor\Document\IBuffer.cs" />
<Compile Include="Mono.TextEditor\Gui\ITooltipProvider.cs" />
- <Compile Include="Mono.TextEditor\Document\GapBuffer.cs" />
<Compile Include="Mono.TextEditor\Document\FoldSegment.cs" />
<Compile Include="Mono.TextEditor\Document\LineEventArgs.cs" />
<Compile Include="Mono.TextEditor\Document\LineSplitter.cs" />
@@ -183,12 +181,10 @@
<Compile Include="Mono.TextEditor\Document\PrimitiveLineSplitter.cs" />
<Compile Include="Mono.TextEditor\Document\IWordFindStrategy.cs" />
<Compile Include="Mono.TextEditor\Actions\SelectionActions.cs" />
- <Compile Include="Mono.TextEditor\Document\StringBuffer.cs" />
<Compile Include="Mono.TextEditor\Document\TextSegment.cs" />
<Compile Include="Mono.TextEditor\Gui\TextViewMargin.cs" />
<Compile Include="Mono.TextEditor\Document\SharpDevelopWordFindStrategy.cs" />
<Compile Include="Mono.TextEditor\Document\EmacsWordFindStrategy.cs" />
- <Compile Include="Mono.TextEditor\Document\BufferedTextReader.cs" />
<Compile Include="Mono.TextEditor\Document\SyntaxModeChangeEventArgs.cs" />
<Compile Include="Mono.TextEditor\Document\DocumentLine.cs" />
<Compile Include="Mono.TextEditor\Standalone\TextSourceVersionProvider.cs" />
@@ -207,7 +203,6 @@
<Compile Include="Mono.TextEditor\TextSegmentMarker.cs" />
<Compile Include="Mono.TextEditor\Gui\FoldMarkerMargin.cs" />
<Compile Include="Mono.TextEditor\Gui\TextArea.cs" />
- <Compile Include="Mono.TextEditor\Gui\TextEditor.cs" />
<Compile Include="Mono.TextEditor\Standalone\StringTextSource.cs" />
<Compile Include="Mono.TextEditor.Utils\HtmlWriter.cs" />
<Compile Include="Mono.TextEditor.PopupWindow\WindowTransparencyDecorator.cs" />
@@ -236,6 +231,13 @@
<Compile Include="Mono.TextEditor\Gui\LayoutCache.cs" />
<Compile Include="Mono.TextEditor\Gui\GtkUtil.cs" />
<Compile Include="Mono.TextEditor\EditModeChangedEventArgs.cs" />
+ <Compile Include="Mono.TextEditor.Utils\Rope.cs" />
+ <Compile Include="Mono.TextEditor.Utils\RopeNode.cs" />
+ <Compile Include="Mono.TextEditor.Utils\ImmutableStack.cs" />
+ <Compile Include="Mono.TextEditor.Utils\CharRope.cs" />
+ <Compile Include="Mono.TextEditor.Utils\RopeTextReader.cs" />
+ <Compile Include="Mono.TextEditor\Document\ImmutableLineSplitter.cs" />
+ <Compile Include="Mono.TextEditor\Gui\MonoTextEditor.cs" />
</ItemGroup>
<ItemGroup>
<None Include="Mono.TextEditor.dll.config">
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/Actions/DeleteActions.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/Actions/DeleteActions.cs
index ce3e4c3b98..1c1cb81ad4 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/Actions/DeleteActions.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/Actions/DeleteActions.cs
@@ -220,7 +220,35 @@ namespace Mono.TextEditor
// Virtual indentation needs to be fixed before to have the same behavior
// if it's there or not (otherwise user has to press multiple backspaces in some cases)
data.EnsureCaretIsNotVirtual ();
- DocumentLine line = data.Document.GetLine (data.Caret.Line);
+
+ var line = data.Document.GetLine (data.Caret.Line);
+ // smart backspace (delete indentation)
+ if (data.Options.IndentStyle == IndentStyle.Smart || data.Options.IndentStyle == IndentStyle.Virtual) {
+ if (data.Caret.Column == data.GetVirtualIndentationColumn (data.Caret.Location)) {
+ bool isAllIndent = line.GetIndentation (data.Document).Length == data.Caret.Column - 1;
+
+ if (isAllIndent) {
+ var prevLine = line.PreviousLine;
+ var prevLineIsEmpty = prevLine != null && prevLine.Length == 0;
+
+ var startOffset = prevLine != null ? prevLine.EndOffset : 0;
+ data.Remove (startOffset, data.Caret.Offset - startOffset);
+ if (prevLine != null) {
+ if (prevLineIsEmpty) {
+ if (line.Length - data.Caret.Column - 1 > 0 && data.HasIndentationTracker) {
+ data.InsertAtCaret (data.IndentationTracker.GetIndentationString (data.Caret.Offset));
+ } else {
+ data.Caret.Column = data.GetVirtualIndentationColumn (prevLine.Offset);
+ }
+ }
+ data.FixVirtualIndentation ();
+ }
+ return;
+ }
+ }
+ }
+
+ // normal backspace.
if (data.Caret.Column > line.Length + 1) {
data.Caret.Column = line.Length + 1;
} else if (data.Caret.Offset == line.Offset) {
@@ -287,7 +315,21 @@ namespace Mono.TextEditor
DocumentLine line = data.Document.GetLine (data.Caret.Line);
if (data.Caret.Column == line.Length + 1) {
if (data.Caret.Line < data.Document.LineCount) {
- data.Remove (line.EndOffsetIncludingDelimiter - line.DelimiterLength, line.DelimiterLength);
+ var deletionLength = line.DelimiterLength;
+ // smart backspace (delete indentation)
+ if (data.Options.IndentStyle == IndentStyle.Smart || data.Options.IndentStyle == IndentStyle.Virtual) {
+ var next = line.NextLine;
+ if (next != null) {
+ if (data.HasIndentationTracker) {
+ var lineIndentation = next.GetIndentation (data.Document);
+ if (lineIndentation.StartsWith (data.IndentationTracker.GetIndentationString (next.Offset))) {
+ deletionLength += lineIndentation.Length;
+ }
+ }
+ }
+ }
+
+ data.Remove (line.EndOffsetIncludingDelimiter - line.DelimiterLength, deletionLength);
if (line.EndOffsetIncludingDelimiter == data.Document.TextLength)
line.UnicodeNewline = UnicodeNewline.Unknown;
}
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/Actions/MiscActions.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/Actions/MiscActions.cs
index 8a42f8b0a0..37b5dd2c81 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/Actions/MiscActions.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/Actions/MiscActions.cs
@@ -272,9 +272,11 @@ namespace Mono.TextEditor
break;
case IndentStyle.Auto:
data.EnsureCaretIsNotVirtual ();
- var sb = new StringBuilder (data.EolMarker);
- sb.Append (data.Document.GetLineIndent (data.Caret.Line));
- data.InsertAtCaret (sb.ToString ());
+ var indent = data.Document.GetLineIndent (data.Caret.Line);
+ data.InsertAtCaret (data.EolMarker);
+ data.EnsureCaretIsNotVirtual ();
+ if (data.GetLine (data.Caret.Line).Length == 0)
+ data.InsertAtCaret (indent);
break;
case IndentStyle.Smart:
if (!data.HasIndentationTracker)
@@ -511,59 +513,5 @@ namespace Mono.TextEditor
}
}
- public static void SortSelectedLines (TextEditorData data)
- {
- var start = data.MainSelection.Start;
- var end = data.MainSelection.End;
- var caret = data.Caret.Location;
-
- int startLine = start.Line;
- int endLine = end.Line;
- if (startLine == endLine)
- return;
-
- int length = 0;
- var lines = new string[endLine - startLine + 1];
- for (int i = startLine; i <= endLine; i++) {
- //get lines *with* line endings
- var lineText = data.GetLineText (i, true);
- lines [i - startLine] = lineText;
- length += lineText.Length;
- }
-
- var linesUnsorted = new string[lines.Length];
-
- Array.Sort (lines, StringComparer.Ordinal);
-
- bool changed = false;
- for (int i = 0; i <= lines.Length; i++) {
- //can't simply use reference comparison as Array.Sort is not stable
- if (string.Equals (lines [i], linesUnsorted [i], StringComparison.Ordinal)) {
- continue;
- }
- changed = true;
- break;
- }
- if (!changed) {
- return;
- }
-
-
- var sb = new StringBuilder ();
- for (int i = 0; i < lines.Length; i++) {
- sb.Append (lines [i]);
- }
-
- var startOffset = data.Document.LocationToOffset (new TextLocation (startLine, 0));
- data.Replace (startOffset, length, sb.ToString ());
-
- data.Caret.Location = LimitColumn (data, caret);
- data.SetSelection (LimitColumn (data, start), LimitColumn (data, end));
- }
-
- static DocumentLocation LimitColumn (TextEditorData data, DocumentLocation loc)
- {
- return new DocumentLocation (loc.Line, System.Math.Min (loc.Column, data.GetLine (loc.Line).Length + 1));
- }
}
} \ No newline at end of file
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/BookmarkMarker.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/BookmarkMarker.cs
index c40ea22edf..63e525d6d5 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/BookmarkMarker.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/BookmarkMarker.cs
@@ -49,14 +49,14 @@ namespace Mono.TextEditor
return margin is IconMargin;
}
- public override void DrawForeground (TextEditor editor, Cairo.Context cr, MarginDrawMetrics metrics)
+ public override void DrawForeground (MonoTextEditor editor, Cairo.Context cr, MarginDrawMetrics metrics)
{
DrawBookmarkFunc (editor, cr, LineSegment, metrics.X, metrics.Y, metrics.Width, metrics.Height);
}
- public static Action<TextEditor, Cairo.Context, DocumentLine, double, double, double, double> DrawBookmarkFunc = DrawIcon;
+ public static Action<MonoTextEditor, Cairo.Context, DocumentLine, double, double, double, double> DrawBookmarkFunc = DrawIcon;
- static void DrawIcon (TextEditor editor, Cairo.Context cr, DocumentLine lineSegment, double x, double y, double width, double height)
+ static void DrawIcon (MonoTextEditor editor, Cairo.Context cr, DocumentLine lineSegment, double x, double y, double width, double height)
{
if (lineSegment.IsBookmarked) {
var color1 = editor.ColorStyle.Bookmarks.Color;
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/BufferedTextReader.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/BufferedTextReader.cs
deleted file mode 100644
index 77340951be..0000000000
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/BufferedTextReader.cs
+++ /dev/null
@@ -1,93 +0,0 @@
-// BufferedTextReader.cs
-//
-// Author:
-// Mike Krüger <mkrueger@novell.com>
-//
-// Copyright (c) 2008 Novell, Inc (http://www.novell.com)
-//
-// 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;
-
-namespace Mono.TextEditor
-{
- /// <summary>
- /// Wraps the IBuffer interface to a System.IO.TextReader model.
- /// </summary>
- class BufferedTextReader : System.IO.TextReader
- {
- int position = 0;
- IBuffer buffer;
-
- public BufferedTextReader (IBuffer buffer)
- {
- this.buffer = buffer;
- }
-
- protected override void Dispose (bool disposing)
- {
- if (disposing)
- buffer = null;
- }
-
- public override void Close ()
- {
- Dispose ();
- }
-
- public override int Peek ()
- {
- if (position < 0 || position >= buffer.TextLength)
- return -1;
- return buffer.GetCharAt (position);
- }
-
- public override int Read ()
- {
- if (position < 0 || position >= buffer.TextLength)
- return -1;
- return buffer.GetCharAt (position++);
- }
-
- public override int Read (char[] buffer, int index, int count)
- {
- if (buffer == null)
- throw new ArgumentNullException ();
- int lastOffset = System.Math.Min (this.buffer.TextLength, position + count);
- int length = lastOffset - position;
- if (length <= 0)
- return 0;
- string text = this.buffer.GetTextAt (position, length);
- text.CopyTo (0, buffer, index, length);
- position += length;
- return length;
- }
-
- public override string ReadToEnd ()
- {
- if (position < 0 || position >= buffer.TextLength)
- return "";
- string result = this.buffer.GetTextAt (position, buffer.TextLength - position);
- position = buffer.TextLength;
- return result;
- }
- }
-}
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/DiffTracker.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/DiffTracker.cs
index 1b56c0130f..1c8a1e190a 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/DiffTracker.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/DiffTracker.cs
@@ -46,7 +46,7 @@ namespace Mono.TextEditor
public Mono.TextEditor.TextDocument.LineState GetLineState (DocumentLine line)
{
- if (line != null) {
+ if (line != null && lineStates != null) {
try {
var info = lineStates [line.LineNumber];
if (info != null) {
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/DocumentUpdateRequest.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/DocumentUpdateRequest.cs
index bf4ce8f02d..baa5551ec1 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/DocumentUpdateRequest.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/DocumentUpdateRequest.cs
@@ -29,7 +29,7 @@ namespace Mono.TextEditor
{
public abstract class DocumentUpdateRequest
{
- public abstract void Update (TextEditor editor);
+ public abstract void Update (MonoTextEditor editor);
}
public class SinglePositionUpdate : DocumentUpdateRequest
@@ -42,7 +42,7 @@ namespace Mono.TextEditor
this.column = column;
}
- public override void Update (TextEditor editor)
+ public override void Update (MonoTextEditor editor)
{
editor.RedrawPosition (line, column);
}
@@ -50,7 +50,7 @@ namespace Mono.TextEditor
public class UpdateAll : DocumentUpdateRequest
{
- public override void Update (TextEditor editor)
+ public override void Update (MonoTextEditor editor)
{
editor.QueueDraw ();
}
@@ -65,7 +65,7 @@ namespace Mono.TextEditor
this.line = line;
}
- public override void Update (TextEditor editor)
+ public override void Update (MonoTextEditor editor)
{
editor.RedrawLine (line);
}
@@ -81,7 +81,7 @@ namespace Mono.TextEditor
this.end = end;
}
- public override void Update (TextEditor editor)
+ public override void Update (MonoTextEditor editor)
{
editor.TextViewMargin.PurgeLayoutCache ();
editor.RedrawLines (start, end);
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/GapBuffer.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/GapBuffer.cs
deleted file mode 100644
index 7ba81771aa..0000000000
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/GapBuffer.cs
+++ /dev/null
@@ -1,599 +0,0 @@
-// GapBuffer.cs
-//
-// Author:
-// Mike Krüger <mkrueger@novell.com>
-//
-// Copyright (c) 2007 Novell, Inc (http://www.novell.com)
-//
-// 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.Text;
-using System.Collections.Generic;
-
-namespace Mono.TextEditor
-{
- public sealed class GapBuffer : IBuffer
- {
- char[] buffer = new char[0];
-
- int gapBegin = 0;
- int gapEnd = 0;
- int gapLength = 0;
-
- const int minGapLength = 4 * 1024;
- const int maxGapLength = 32 * 1024;
-
- public int TextLength {
- get {
- return buffer.Length - gapLength;
- }
- }
-
- public void Insert (int offset, string text)
- {
- Replace (offset, 0, text);
- }
-
- public void Remove (int offset, int count)
- {
- Replace (offset, count, null);
- }
-
- public void Remove (TextSegment segment)
- {
- Remove (segment.Offset, segment.Length);
- }
-
- public string GetTextAt (TextSegment segment)
- {
- return GetTextAt (segment.Offset, segment.Length);
- }
-
- public string Text {
- get {
- return GetTextAt (0, TextLength);
- }
- set {
- buffer = value != null ? value.ToCharArray () : new char[0];
- gapBegin = gapEnd = gapLength = 0;
- }
- }
-
- public char GetCharAt (int offset)
- {
- return buffer[offset < gapBegin ? offset : offset + gapLength];
- }
-
- public string GetTextAt (int offset, int count)
- {
- int end = offset + count;
- if (end < gapBegin)
- return new string (buffer, offset, count);
- if (offset > gapBegin)
- return new string (buffer, offset + gapLength, count);
-
- int leftBlockSize = gapBegin - offset;
- int rightBlockSize = end - gapBegin;
- char[] result = new char [leftBlockSize + rightBlockSize];
- Array.Copy (buffer, offset, result, 0, leftBlockSize);
- Array.Copy (buffer, gapEnd, result, leftBlockSize, rightBlockSize);
- return new string (result);
- }
-
- public void Replace (int offset, int count, string text)
- {
- if (!string.IsNullOrEmpty (text)) {
- PlaceGap (offset, text.Length - count);
- text.CopyTo (0, buffer, offset, text.Length);
- gapBegin += text.Length;
- } else {
- PlaceGap (offset, 0);
- }
- gapEnd += count;
- gapLength = gapEnd - gapBegin;
- if (gapLength > maxGapLength)
- CreateBuffer (gapBegin, minGapLength);
- }
-
- void PlaceGap (int newOffset, int minLength)
- {
- if (gapLength < minLength) {
- if (minLength < maxGapLength) {
- CreateBuffer (newOffset, minLength + (maxGapLength - minLength) / 2);
- } else {
- CreateBuffer (newOffset, minLength + minGapLength);
- }
- return;
- }
-
- int delta = gapBegin - newOffset;
- if (delta > 0) {
- Array.Copy (buffer, newOffset, buffer, gapEnd - delta, delta);
- } else {
- Array.Copy (buffer, gapEnd, buffer, gapBegin, -delta);
- }
- gapBegin -= delta;
- gapEnd -= delta;
- }
-
- void CreateBuffer (int gapOffset, int gapLength)
- {
- gapLength = System.Math.Max (minGapLength, gapLength);
-
- char[] newBuffer = new char[TextLength + gapLength];
- if (gapOffset < gapBegin) {
- Array.Copy (buffer, 0, newBuffer, 0, gapOffset);
- Array.Copy (buffer, gapOffset, newBuffer, gapOffset + gapLength, gapBegin - gapOffset);
- Array.Copy (buffer, gapEnd, newBuffer, newBuffer.Length - (buffer.Length - gapEnd), buffer.Length - gapEnd);
- } else {
- Array.Copy (buffer, 0, newBuffer, 0, gapBegin);
- Array.Copy (buffer, gapEnd, newBuffer, gapBegin, gapOffset - gapBegin);
- int lastPartLength = newBuffer.Length - (gapOffset + gapLength);
- Array.Copy (buffer, buffer.Length - lastPartLength, newBuffer, gapOffset + gapLength, lastPartLength);
- }
-
- gapBegin = gapOffset;
- gapEnd = gapOffset + gapLength;
- this.gapLength = gapLength;
- buffer = newBuffer;
- }
-
-
- // TODO: Optimize!
- int IBuffer.IndexOf (char c, int startIndex, int count)
- {
- return Text.IndexOf (c, startIndex, count);
- }
-
- int IBuffer.IndexOfAny (char[] anyOf, int startIndex, int count)
- {
- return Text.IndexOfAny (anyOf, startIndex, count);
- }
-
- public int IndexOf (string searchText, int startIndex, int count, StringComparison comparisonType)
- {
- return Text.IndexOf (searchText, startIndex, count, comparisonType);
- }
-
- int IBuffer.LastIndexOf (char c, int startIndex, int count)
- {
- return Text.LastIndexOf (c, startIndex, count);
- }
-
- public int LastIndexOf (string searchText, int startIndex, int count, StringComparison comparisonType)
- {
- return Text.LastIndexOf (searchText, startIndex, count, comparisonType);
- }
-
-// #region Search
-// unsafe int SearchForwardInternal (string pattern, int startIndex)
-// {
-// if (startIndex > gapBegin)
-// startIndex += gapLength;
-//
-// int valueLen = pattern.Length;
-// if (startIndex >= buffer.Length - valueLen + 1)
-// return -1;
-//
-// fixed (char* bufferPtr = buffer, patternPtr = pattern) {
-// char* ap = bufferPtr + startIndex;
-// char* bufferPhysEnd = bufferPtr + buffer.Length;
-// char* bufferEnd = bufferPhysEnd - valueLen + 1;
-// char* gapBeginPtr = bufferPtr + gapBegin;
-// char* gapEndPtr = bufferPtr + gapEnd;
-// char* stopGap = gapBeginPtr - valueLen + 1;
-// char* patternPos1Ptr = patternPtr + 1;
-// char* patternEndPtr = patternPtr + valueLen;
-// char p0 = *patternPtr;
-//
-// if (ap < gapBeginPtr) {
-// if (stopGap > bufferPtr) {
-// while (ap < stopGap) {
-// if (*ap == p0) {
-// char* p = ap + 1;
-// char* v = patternPos1Ptr;
-// while (v < patternEndPtr) {
-// if (*p != *v)
-// goto NextVal;
-// v++;
-// p++;
-// }
-// return (int)(ap - bufferPtr);
-// }
-// NextVal:
-// ap++;
-// }
-// }
-//
-// while (ap != gapBeginPtr) {
-// if (*ap == p0) {
-// char* p = ap + 1;
-// char* v = patternPos1Ptr;
-// while (v < patternEndPtr) {
-// if (*p != *v)
-// goto NextVal;
-// v++;
-// p++;
-// if (p == gapBeginPtr)
-// p = gapEndPtr;
-// }
-// return (int)(ap - bufferPtr);
-// }
-// NextVal:
-// ap++;
-// }
-// }
-//
-// if (ap < gapEndPtr)
-// ap = gapEndPtr;
-// if (ap < bufferEnd) {
-// while (ap != bufferEnd) {
-// if (*ap == p0) {
-// char* p = ap + 1;
-// char* v = patternPos1Ptr;
-// while (v < patternEndPtr) {
-// if (*p != *v)
-// goto NextVal;
-// v++;
-// p++;
-// }
-// return (int)(ap - gapLength - bufferPtr);
-// }
-// NextVal:
-// ap++;
-// }
-// }
-// }
-// return -1;
-// }
-//
-// unsafe int SearchForwardInternalIgnoreCase (string pattern, int startIndex)
-// {
-// if (startIndex > gapBegin)
-// startIndex += gapLength;
-//
-// int valueLen = pattern.Length;
-// if (startIndex >= buffer.Length - valueLen + 1)
-// return -1;
-//
-// fixed (char* bufferPtr = buffer, patternPtr = pattern) {
-// char* ap = bufferPtr + startIndex;
-// char* bufferPhysEnd = bufferPtr + buffer.Length;
-// char* bufferEnd = bufferPhysEnd - valueLen + 1;
-// char* gapBeginPtr = bufferPtr + gapBegin;
-// char* gapEndPtr = bufferPtr + gapEnd;
-// char* stopGap = gapBeginPtr - valueLen + 1;
-// char* patternPos1Ptr = patternPtr + 1;
-// char* patternEndPtr = patternPtr + valueLen;
-// char p0 = *patternPtr;
-//
-// if (ap < gapBeginPtr) {
-// if (stopGap > bufferPtr) {
-// while (ap < stopGap) {
-// if (char.ToUpper (*ap) == p0) {
-// char* p = ap + 1;
-// char* v = patternPos1Ptr;
-// while (v < patternEndPtr) {
-// if (char.ToUpper (*p) != *v)
-// goto NextVal;
-// v++;
-// p++;
-// }
-// return (int)(ap - bufferPtr);
-// }
-// NextVal:
-// ap++;
-// }
-// }
-//
-// while (ap != gapBeginPtr) {
-// if (char.ToUpper (*ap) == p0) {
-// char* p = ap + 1;
-// char* v = patternPos1Ptr;
-// while (v < patternEndPtr) {
-// if (char.ToUpper (*p) != *v)
-// goto NextVal;
-// v++;
-// p++;
-// if (p == gapBeginPtr)
-// p = gapEndPtr;
-// }
-// return (int)(ap - bufferPtr);
-// }
-// NextVal:
-// ap++;
-// }
-// }
-//
-// if (ap < gapEndPtr)
-// ap = gapEndPtr;
-// if (ap < bufferEnd) {
-// while (ap != bufferEnd) {
-// if (char.ToUpper (*ap) == p0) {
-// char* p = ap + 1;
-// char* v = patternPos1Ptr;
-// while (v < patternEndPtr) {
-// if (char.ToUpper (*p) != *v)
-// goto NextVal;
-// v++;
-// p++;
-// }
-// return (int)(ap - gapLength - bufferPtr);
-// }
-// NextVal:
-// ap++;
-// }
-// }
-// }
-// return -1;
-// }
-//
-// public IEnumerable<int> SearchForward (string pattern, int startIndex)
-// {
-// int idx = startIndex;
-// while ((idx = SearchForwardInternal (pattern, idx)) != -1) {
-// yield return idx;
-// idx += pattern.Length;
-// }
-// }
-//
-// public IEnumerable<int> SearchForwardIgnoreCase (string pattern, int startIndex)
-// {
-// pattern = pattern.ToUpper ();
-// int idx = startIndex;
-// while ((idx = SearchForwardInternalIgnoreCase (pattern, idx)) != -1) {
-// yield return idx;
-// idx += pattern.Length;
-// }
-// }
-//
-// unsafe int SearchBackwardInternal (string pattern, int startIndex)
-// {
-// int valueLen = pattern.Length;
-// if (startIndex < valueLen - 1)
-// return -1;
-// if (startIndex > gapBegin)
-// startIndex += gapLength;
-// fixed (char* bufferPtr = buffer, patternPtr = pattern) {
-// char* ap = bufferPtr + startIndex;
-// char* bufferEnd = bufferPtr + valueLen - 1;
-//
-// char* bufferPhysEnd = bufferPtr + buffer.Length;
-// char* gapBeginPtr = bufferPtr + gapBegin;
-// char* gapEndPtr = bufferPtr + gapEnd;
-// char* stopGap = gapEndPtr + valueLen - 1;
-// char* patternPos1Ptr = patternPtr + valueLen - 2;
-// char* patternEndPtr = patternPtr - 1;
-// char p0 = *(patternPtr + valueLen - 1);
-//
-// if (ap >= gapEndPtr) {
-// if (stopGap < bufferPhysEnd) {
-// while (ap >= stopGap) {
-// if (*ap == p0) {
-// char* p = ap - 1;
-// char* v = patternPos1Ptr;
-// while (v > patternEndPtr) {
-// if (*p != *v)
-// goto NextVal;
-// v--;
-// p--;
-// }
-// return (int)(p - gapLength - bufferPtr + 1);
-// }
-// NextVal:
-// ap--;
-// }
-// }
-//
-// while (ap >= gapEndPtr) {
-// if (*ap == p0) {
-// char* p = ap - 1;
-// char* v = patternPos1Ptr;
-// while (v > patternEndPtr) {
-// if (*p != *v)
-// goto NextVal;
-// v--;
-// if (p == gapEndPtr) {
-// p = gapBeginPtr - 1;
-// } else {
-// p--;
-// }
-// }
-// if (p >= gapEndPtr)
-// return (int)(p - gapLength - bufferPtr + 1);
-// return (int)(p - bufferPtr + 1);
-// }
-// NextVal:
-// ap--;
-// }
-// }
-//
-// while (ap >= bufferEnd) {
-// if (*ap == p0) {
-// char* p = ap - 1;
-// char* v = patternPos1Ptr;
-// while (v > patternEndPtr) {
-// if (*p != *v)
-// goto NextVal;
-// v--;
-// p--;
-// }
-// return (int)(p - bufferPtr + 1);
-// }
-// NextVal:
-// ap--;
-// }
-// }
-// return -1;
-// }
-//
-// unsafe int SearchBackwardInternalIgnoreCase (string pattern, int startIndex)
-// {
-// int valueLen = pattern.Length;
-// if (startIndex < valueLen - 1)
-// return -1;
-// if (startIndex > gapBegin)
-// startIndex += gapLength;
-//
-// fixed (char* bufferPtr = buffer, patternPtr = pattern) {
-// char* ap = bufferPtr + startIndex;
-// char* bufferEnd = bufferPtr + valueLen - 1;
-//
-// char* bufferPhysEnd = bufferPtr + buffer.Length;
-// char* gapBeginPtr = bufferPtr + gapBegin;
-// char* gapEndPtr = bufferPtr + gapEnd;
-// char* stopGap = gapEndPtr + valueLen - 1;
-// char* patternPos1Ptr = patternPtr + valueLen - 2;
-// char* patternEndPtr = patternPtr - 1;
-// char p0 = *(patternPtr + valueLen - 1);
-//
-// if (ap >= gapEndPtr) {
-// if (stopGap < bufferPhysEnd) {
-// while (ap >= stopGap) {
-// if (char.ToUpper (*ap) == p0) {
-// char* p = ap - 1;
-// char* v = patternPos1Ptr;
-// while (v > patternEndPtr) {
-// if (char.ToUpper (*p) != *v)
-// goto NextVal;
-// v--;
-// p--;
-// }
-// return (int)(p - gapLength - bufferPtr + 1);
-// }
-// NextVal:
-// ap--;
-// }
-// }
-//
-// while (ap >= gapEndPtr) {
-// if (char.ToUpper (*ap) == p0) {
-// char* p = ap - 1;
-// char* v = patternPos1Ptr;
-// while (v > patternEndPtr) {
-// if (char.ToUpper (*p) != *v)
-// goto NextVal;
-// v--;
-// if (p == gapEndPtr) {
-// p = gapBeginPtr - 1;
-// } else {
-// p--;
-// }
-// }
-// if (p >= gapEndPtr)
-// return (int)(p - gapLength - bufferPtr + 1);
-// return (int)(p - bufferPtr + 1);
-// }
-// NextVal:
-// ap--;
-// }
-// }
-// while (ap >= bufferEnd) {
-// if (char.ToUpper (*ap) == p0) {
-// char* p = ap - 1;
-// char* v = patternPos1Ptr;
-// while (v > patternEndPtr) {
-// if (char.ToUpper (*p) != *v)
-// goto NextVal;
-// v--;
-// p--;
-// }
-// return (int)(p - bufferPtr + 1);
-// }
-// NextVal:
-// ap--;
-// }
-// }
-// return -1;
-// }
-//
-// public IEnumerable<int> SearchBackward (string pattern, int startIndex)
-// {
-// int idx = startIndex;
-// while ((idx = SearchBackwardInternal (pattern, idx)) != -1) {
-// yield return idx;
-// idx -= pattern.Length;
-// }
-// }
-//
-// public IEnumerable<int> SearchBackwardIgnoreCase (string pattern, int startIndex)
-// {
-// pattern = pattern.ToUpper ();
-// int idx = startIndex;
-// while ((idx = SearchBackwardInternalIgnoreCase (pattern, idx)) != -1) {
-// yield return idx;
-// idx -= pattern.Length;
-// }
-// }
-//
-// /* Boyer-Moore-Horspool-Raita implementation: (but on Intel Core i brute force outpeforms it.
-//
-// static int[] ProcessString (string pattern)
-// {
-// var result = new int[char.MaxValue];
-// for (int i = 0; i < result.Length; i++)
-// result[i] = pattern.Length - 1;
-// for (int i = 0; i < pattern.Length - 1; ++i) {
-// result[pattern[i]] = pattern.Length - i - 1;
-// }
-// return result;
-// }
-// unsafe static int bmhrSearchBytes (string text, string pattern, int textStart, int[] b)
-// {
-// int lastIndex = text.Length + pattern.Length - 1;
-// if (textStart >= lastIndex)
-// return -1;
-//
-// int m = pattern.Length - 1;
-// int mMinusOne = pattern.Length - 2;
-//
-// var last = pattern[pattern.Length - 1];
-// var first = pattern[0];
-//
-// fixed (char* textPtr = text, pattenrPtr = pattern) {
-// char* i = textPtr + textStart + pattern.Length - 1;
-// char* endText = textPtr + lastIndex;
-// while (i < endText) {
-// if (*i == last) {
-// //if (*(i - m) == first) {
-// char* k = i - 1;
-// char* pp = pattenrPtr + mMinusOne;
-//
-// while (pp >= pattenrPtr && *k == *pp) {
-// --k;
-// --pp;
-// }
-//
-// if (pp < pattenrPtr)
-// return (int)(k - textPtr) + 1;
-// // }
-// }
-// i += b[*i];
-// }
-// }
-// return -1;
-// }
-// * */
-// #endregion
-
- }
-} \ No newline at end of file
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/IBuffer.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/IBuffer.cs
deleted file mode 100644
index 0cfffef74e..0000000000
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/IBuffer.cs
+++ /dev/null
@@ -1,131 +0,0 @@
-// IBuffer.cs
-//
-// Author:
-// Mike Krüger <mkrueger@novell.com>
-//
-// Copyright (c) 2007 Novell, Inc (http://www.novell.com)
-//
-// 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.Text;
-
-namespace Mono.TextEditor
-{
- public interface IBuffer
- {
- /// <summary>
- /// Gets the total text length.
- /// </summary>
- /// <returns>The length of the text, in characters.</returns>
- /// <remarks>This is the same as Text.Length, but is more efficient because
- /// it doesn't require creating a String object.</remarks>
- int TextLength {
- get;
- }
-
- /// <summary>
- /// Gets the whole text as string.
- /// </summary>
- string Text {
- get;
- set;
- }
-
- /// <summary>
- /// Replaces text.
- /// </summary>
- /// <param name="offset">The starting offset of the text to be replaced.</param>
- /// <param name="count">The length of the text to be replaced.</param>
- /// <param name="value">The new text.</param>
- void Replace (int offset, int count, string value);
-
- /// <summary>
- /// Retrieves the text for a portion of the document.
- /// </summary>
- /// <exception cref="ArgumentOutOfRangeException">offset or length is outside the valid range.</exception>
- /// <remarks>This is the same as Text.Substring, but is more efficient because
- /// it doesn't require creating a String object for the whole document.</remarks>
- string GetTextAt (int offset, int count);
-
- /// <summary>
- /// Gets a character at the specified position in the document.
- /// </summary>
- /// <paramref name="offset">The index of the character to get.</paramref>
- /// <exception cref="ArgumentOutOfRangeException">Offset is outside the valid range (0 to TextLength-1).</exception>
- /// <returns>The character at the specified position.</returns>
- /// <remarks>This is the same as Text[offset], but is more efficient because
- /// it doesn't require creating a String object.</remarks>
- char GetCharAt (int offset);
-
- /// <summary>
- /// Gets the index of the first occurrence of the character in the specified array.
- /// </summary>
- /// <param name="c">Character 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 the character was found; or -1 if no occurrence was found.</returns>
- int IndexOf (char c, int startIndex, int count);
-
- /// <summary>
- /// Gets the index of the first occurrence of any character in the specified array.
- /// </summary>
- /// <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);
-
- /// <summary>
- /// Gets the index of the first occurrence of the specified search text in this text source.
- /// </summary>
- /// <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);
-
- /// <summary>
- /// Gets the index of the last occurrence of the specified character in this text source.
- /// </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);
-
- /// <summary>
- /// Gets the index of the last occurrence of the specified search text in this text source.
- /// </summary>
- /// <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);
- }
-}
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/ImmutableLineSplitter.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/ImmutableLineSplitter.cs
new file mode 100644
index 0000000000..a2d65da072
--- /dev/null
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/ImmutableLineSplitter.cs
@@ -0,0 +1,179 @@
+//
+// ImmutableLineSplitter.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2014 Xamarin Inc. (http://xamarin.com)
+//
+// 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 ICSharpCode.NRefactory;
+using System.Collections.Generic;
+
+namespace Mono.TextEditor
+{
+ class ImmutableLineSplitter : ILineSplitter
+ {
+ readonly LineSegment[] lines;
+
+ sealed class LineSegment : DocumentLine
+ {
+ readonly ImmutableLineSplitter splitter;
+ readonly int lineNumber;
+
+ public override int Offset { get; set; }
+
+ public override int LineNumber {
+ get {
+ return lineNumber + 1;
+ }
+ }
+
+ public override DocumentLine NextLine {
+ get {
+ return splitter.Get (lineNumber + 1);
+ }
+ }
+
+ public override DocumentLine PreviousLine {
+ get {
+ return splitter.Get (lineNumber - 1);
+ }
+ }
+
+ public LineSegment (ImmutableLineSplitter splitter, int lineNumber, int offset, int length, UnicodeNewline newLine) : base(length, newLine)
+ {
+ this.splitter = splitter;
+ this.lineNumber = lineNumber;
+ Offset = offset;
+ }
+ }
+
+ public ImmutableLineSplitter (ILineSplitter src)
+ {
+ if (src == null)
+ throw new ArgumentNullException ("src");
+ lines = new LineSegment[src.Count];
+ int cur = 0;
+ foreach (var line in src.Lines) {
+ lines [cur] = new LineSegment (this, cur, line.Offset, line.LengthIncludingDelimiter, line.UnicodeNewline);
+ cur++;
+ }
+ }
+
+ #region ILineSplitter implementation
+
+ public event EventHandler<LineEventArgs> LineChanged;
+
+ public event EventHandler<LineEventArgs> LineInserted;
+
+ public event EventHandler<LineEventArgs> LineRemoved;
+
+ public void Clear ()
+ {
+ }
+
+ public void Initalize (string text, out DocumentLine longestLine)
+ {
+ longestLine = null;
+ }
+
+ public DocumentLine Get (int number)
+ {
+ return lines [number - 1];
+ }
+
+ public DocumentLine GetLineByOffset (int offset)
+ {
+ return lines [OffsetToLineNumber (offset) - 1];
+ }
+
+ public int OffsetToLineNumber (int offset)
+ {
+ int min = 0;
+ int max = lines.Length - 1;
+ while (min <= max) {
+ int mid = (min + max) / 2;
+ var middleLine = lines [mid];
+ if (offset < middleLine.Offset) {
+ max = mid - 1;
+ } else if (offset > middleLine.EndOffset) {
+ min = mid + 1;
+ } else {
+ return mid + 1;
+ }
+ }
+ return lines.Length;
+ }
+
+ public void TextReplaced (object sender, DocumentChangeEventArgs args)
+ {
+ }
+
+ public void TextRemove (int offset, int length)
+ {
+ }
+
+ public void TextInsert (int offset, string text)
+ {
+ }
+
+ public IEnumerable<DocumentLine> GetLinesBetween (int startLine, int endLine)
+ {
+ for (int i = startLine; i <= endLine; i++)
+ yield return Get (i);
+ }
+
+ public IEnumerable<DocumentLine> GetLinesStartingAt (int startLine)
+ {
+ for (int i = startLine; i <= Count; i++)
+ yield return Get (i);
+ }
+
+ public IEnumerable<DocumentLine> GetLinesReverseStartingAt (int startLine)
+ {
+ for (int i = startLine; i-- > DocumentLocation.MinLine;)
+ yield return Get (i);
+ }
+
+ public int Count {
+ get {
+ return lines.Length;
+ }
+ }
+
+ public bool LineEndingMismatch {
+ get;
+ set;
+ }
+
+ public System.Collections.Generic.IEnumerable<DocumentLine> Lines {
+ get {
+ return lines;
+ }
+ }
+
+ #endregion
+
+
+ }
+}
+
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/StringBuffer.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/StringBuffer.cs
deleted file mode 100644
index ba504a179a..0000000000
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/StringBuffer.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-
-using System;
-using System.Collections.Generic;
-using System.Text;
-namespace Mono.TextEditor
-{
- /// <summary>
- /// Simple implementation of the buffer interface to support fast read-only documents.
- /// </summary>
- public class StringBuffer : IBuffer
- {
- string buffer;
-
- public StringBuffer (string buffer)
- {
- this.buffer = buffer;
- }
-
- #region IBuffer Members
- int IBuffer.TextLength {
- get { return buffer.Length; }
- }
-
- string IBuffer.Text {
- get { return buffer; }
- set { buffer = value; }
- }
-
- void IBuffer.Replace (int offset, int count, string value)
- {
- throw new NotSupportedException ("Operation not supported on this buffer.");
- }
-
- string IBuffer.GetTextAt (int offset, int count)
- {
- return buffer.Substring (offset, count);
- }
-
- char IBuffer.GetCharAt (int offset)
- {
- return buffer[offset];
- }
-
- int IBuffer.IndexOf (char c, int startIndex, int count)
- {
- return buffer.IndexOf (c, startIndex, count);
- }
-
- int IBuffer.IndexOfAny (char[] anyOf, int startIndex, int count)
- {
- return buffer.IndexOfAny (anyOf, startIndex, count);
- }
-
- public int IndexOf (string searchText, int startIndex, int count, StringComparison comparisonType)
- {
- return buffer.IndexOf (searchText, startIndex, count, comparisonType);
- }
-
- int IBuffer.LastIndexOf (char c, int startIndex, int count)
- {
- return buffer.LastIndexOf (c, startIndex, count);
- }
-
- public int LastIndexOf (string searchText, int startIndex, int count, StringComparison comparisonType)
- {
- return buffer.LastIndexOf (searchText, startIndex, count, comparisonType);
- }
- #endregion
- }
-}
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/TextDocument.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/TextDocument.cs
index f69c94d427..1b958ef287 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/TextDocument.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/Document/TextDocument.cs
@@ -40,7 +40,7 @@ namespace Mono.TextEditor
{
public class TextDocument : ICSharpCode.NRefactory.AbstractAnnotatable, ICSharpCode.NRefactory.Editor.IDocument
{
- readonly IBuffer buffer;
+ readonly Rope<char> buffer;
readonly ILineSplitter splitter;
ISyntaxMode syntaxMode = null;
@@ -61,11 +61,21 @@ namespace Mono.TextEditor
lock (this) {
mimeType = value;
SyntaxMode = SyntaxModeService.GetSyntaxMode (this, value);
+ OnMimeTypeChanged (EventArgs.Empty);
}
}
}
}
-
+
+ public event EventHandler MimeTypeChanged;
+
+ protected virtual void OnMimeTypeChanged (EventArgs e)
+ {
+ EventHandler handler = this.MimeTypeChanged;
+ if (handler != null)
+ handler (this, e);
+ }
+
string fileName;
public string FileName {
get {
@@ -91,6 +101,16 @@ namespace Mono.TextEditor
set;
}
+ public bool UseBom {
+ get;
+ set;
+ }
+
+ public System.Text.Encoding Encoding {
+ get;
+ set;
+ }
+
internal ILineSplitter Splitter {
get {
return splitter;
@@ -135,11 +155,10 @@ namespace Mono.TextEditor
}
}
- protected TextDocument (IBuffer buffer,ILineSplitter splitter)
+ protected TextDocument (Rope<char> buffer,ILineSplitter splitter)
{
this.buffer = buffer;
this.splitter = splitter;
- splitter.LineChanged += SplitterLineSegmentTreeLineChanged;
splitter.LineRemoved += HandleSplitterLineSegmentTreeLineRemoved;
foldSegmentTree.tree.NodeRemoved += HandleFoldSegmentTreetreeNodeRemoved;
textSegmentMarkerTree.InstallListener (this);
@@ -152,7 +171,7 @@ namespace Mono.TextEditor
foldedSegments.Remove (e.Node);
}
- public TextDocument () : this(new GapBuffer (), new LineSplitter ())
+ public TextDocument () : this(new Rope<char> (), new LineSplitter ())
{
}
@@ -163,41 +182,56 @@ namespace Mono.TextEditor
public static TextDocument CreateImmutableDocument (string text, bool suppressHighlighting = true)
{
- return new TextDocument (new StringBuffer (text), new PrimitiveLineSplitter ()) {
+ return new TextDocument (CharRope.Create (text), new PrimitiveLineSplitter ()) {
SuppressHighlightUpdate = suppressHighlighting,
Text = text,
ReadOnly = true
};
}
- void SplitterLineSegmentTreeLineChanged (object sender, LineEventArgs e)
- {
- if (LineChanged != null)
- LineChanged (this, e);
+ public event EventHandler<LineEventArgs> LineChanged {
+ add { splitter.LineChanged += value; }
+ remove { splitter.LineChanged -= value; }
}
-
- public event EventHandler<LineEventArgs> LineChanged;
- // public event EventHandler<LineEventArgs> LineInserted;
-
+
+ public event EventHandler<LineEventArgs> LineInserted {
+ add { splitter.LineInserted += value; }
+ remove { splitter.LineInserted -= value; }
+ }
+
+ public event EventHandler<LineEventArgs> LineRemoved {
+ add { splitter.LineRemoved += value; }
+ remove { splitter.LineRemoved -= value; }
+ }
+
#region Buffer implementation
+
public int TextLength {
get {
- return buffer.TextLength;
+ return buffer.Length;
}
}
public bool SuppressHighlightUpdate { get; set; }
internal DocumentLine longestLineAtTextSet;
+ WeakReference cachedText;
public string Text {
get {
- return buffer.Text;
+ string completeText = cachedText != null ? (cachedText.Target as string) : null;
+ if (completeText == null) {
+ completeText = buffer.ToString();
+ cachedText = new WeakReference(completeText);
+ }
+ return completeText;
}
set {
var args = new DocumentChangeEventArgs (0, Text, value);
textSegmentMarkerTree.Clear ();
OnTextReplacing (args);
- buffer.Text = value;
+ cachedText = null;
+ buffer.Clear ();
+ buffer.InsertText (0, value);
extendingTextMarkers = new List<TextLineMarker> ();
splitter.Initalize (value, out longestLineAtTextSet);
ClearFoldSegments ();
@@ -255,8 +289,10 @@ namespace Mono.TextEditor
}
redoStack.Clear ();
}
-
- buffer.Replace (offset, count, value);
+ cachedText = null;
+ buffer.RemoveRange(offset, count);
+ if (!string.IsNullOrEmpty (value))
+ buffer.InsertText (offset, value);
foldSegmentTree.UpdateOnTextReplace (this, args);
splitter.TextReplaced (this, args);
versionProvider.AppendChange (args);
@@ -274,7 +310,7 @@ namespace Mono.TextEditor
if (endOffset > TextLength)
throw new ArgumentException ("endOffset > Length");
- return buffer.GetTextAt (startOffset, endOffset - startOffset);
+ return buffer.ToString (startOffset, endOffset - startOffset);
}
public string GetTextBetween (DocumentLocation start, DocumentLocation end)
@@ -297,7 +333,7 @@ namespace Mono.TextEditor
throw new ArgumentException ("count < 0");
if (offset + count > TextLength)
throw new ArgumentException ("offset + count is beyond EOF");
- return buffer.GetTextAt (offset, count);
+ return buffer.ToString (offset, count);
}
public string GetTextAt (DocumentRegion region)
@@ -337,17 +373,17 @@ namespace Mono.TextEditor
throw new ArgumentException ("offset < 0");
if (offset >= TextLength)
throw new ArgumentException ("offset >= TextLength");
- return buffer.GetCharAt (offset);
+ return buffer [offset];
}
public char GetCharAt (DocumentLocation location)
{
- return buffer.GetCharAt (LocationToOffset (location));
+ return buffer [LocationToOffset (location)];
}
public char GetCharAt (int line, int column)
{
- return buffer.GetCharAt (LocationToOffset (line, column));
+ return buffer [LocationToOffset (line, column)];
}
/// <summary>
@@ -1176,6 +1212,11 @@ namespace Mono.TextEditor
return foldSegmentTree.GetSegmentsOverlapping (line.Offset, line.Length).Cast<FoldSegment> ();
}
+ public IEnumerable<FoldSegment> GetFoldingContaining (int offset, int length)
+ {
+ return foldSegmentTree.GetSegmentsOverlapping (offset, length).Cast<FoldSegment> ();
+ }
+
public IEnumerable<FoldSegment> GetStartFoldings (int lineNumber)
{
return GetStartFoldings (this.GetLine (lineNumber));
@@ -1188,6 +1229,11 @@ namespace Mono.TextEditor
return GetFoldingContaining (line).Where (fold => fold.StartLine == line);
}
+ public IEnumerable<FoldSegment> GetStartFoldings (int offset, int length)
+ {
+ return GetFoldingContaining (offset, length).Where (fold => offset <= fold.StartLine.Offset && fold.StartLine.Offset < offset + length);
+ }
+
public IEnumerable<FoldSegment> GetEndFoldings (int lineNumber)
{
return GetStartFoldings (this.GetLine (lineNumber));
@@ -1200,7 +1246,12 @@ namespace Mono.TextEditor
yield return segment;
}
}
-
+
+ public IEnumerable<FoldSegment> GetEndFoldings (int offset, int length)
+ {
+ return GetFoldingContaining (offset, length).Where (fold => offset <= fold.EndLine.Offset && fold.EndLine.Offset < offset + length);
+ }
+
public int GetLineCount (FoldSegment segment)
{
return segment.EndLine.LineNumber - segment.StartLine.LineNumber;
@@ -1656,12 +1707,14 @@ namespace Mono.TextEditor
#region Diff
+
+
int[] GetDiffCodes (ref int codeCounter, Dictionary<string, int> codeDictionary, bool includeEol)
{
int i = 0;
var result = new int[LineCount];
foreach (DocumentLine line in Lines) {
- string lineText = buffer.GetTextAt (line.Offset, includeEol ? line.LengthIncludingDelimiter : line.Length);
+ string lineText = buffer.ToString (line.Offset, includeEol ? line.LengthIncludingDelimiter : line.Length);
int curCode;
if (!codeDictionary.TryGetValue (lineText, out curCode)) {
codeDictionary[lineText] = curCode = ++codeCounter;
@@ -1854,12 +1907,12 @@ namespace Mono.TextEditor
public System.IO.TextReader CreateReader ()
{
- return new BufferedTextReader (buffer);
+ return new RopeTextReader (buffer);
}
public System.IO.TextReader CreateReader (int offset, int length)
{
- throw new NotImplementedException ();
+ return new RopeTextReader(buffer.GetRange(offset, length));
}
string ICSharpCode.NRefactory.Editor.ITextSource.GetText (int offset, int length)
@@ -1893,25 +1946,37 @@ namespace Mono.TextEditor
}
}
- public SnapshotDocument (string text, ITextSourceVersion version) : base (new StringBuffer (text), new PrimitiveLineSplitter ())
+ public SnapshotDocument (TextDocument doc) : base (doc.buffer.Clone(), new ImmutableLineSplitter (doc.splitter))
{
- this.version = version;
- Text = text;
+ this.version = doc.Version;
+ fileName = doc.fileName;
+ Encoding = doc.Encoding;
+ UseBom = doc.UseBom;
+ mimeType = doc.mimeType;
+
ReadOnly = true;
}
}
public TextDocument CreateDocumentSnapshot ()
{
- return new SnapshotDocument (Text, Version);
+ return new SnapshotDocument (this);
}
- ICSharpCode.NRefactory.Editor.IDocument ICSharpCode.NRefactory.Editor.IDocument.CreateDocumentSnapshot ()
+ public Mono.TextEditor.Utils.Rope<char> CloneRope ()
{
- return new SnapshotDocument (Text, Version);
+ return buffer.Clone ();
}
+ public Mono.TextEditor.Utils.Rope<char> CloneRope (int offset, int count)
+ {
+ return buffer.GetRange (offset, count);
+ }
+ ICSharpCode.NRefactory.Editor.IDocument ICSharpCode.NRefactory.Editor.IDocument.CreateDocumentSnapshot ()
+ {
+ return new SnapshotDocument (this);
+ }
#endregion
}
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/EditMode.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/EditMode.cs
index a6f2acded4..90fcd4a2e8 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/EditMode.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/EditMode.cs
@@ -37,10 +37,10 @@ namespace Mono.TextEditor
//NOTE: the behaviour of this class is actually stateless; these variables are used to make the API
// friendlier for subclassers of this class
protected TextEditorData textEditorData;
- protected TextEditor editor;
+ protected MonoTextEditor editor;
// string status;
- public void InternalHandleKeypress (TextEditor editor, TextEditorData data, Gdk.Key key,
+ public void InternalHandleKeypress (MonoTextEditor editor, TextEditorData data, Gdk.Key key,
uint unicodeChar, Gdk.ModifierType modifier)
{
this.editor = editor;
@@ -53,7 +53,7 @@ namespace Mono.TextEditor
this.editor = null;
}
- internal virtual void InternalSelectionChanged (TextEditor editor, TextEditorData data)
+ internal virtual void InternalSelectionChanged (MonoTextEditor editor, TextEditorData data)
{
// only trigger SelectionChanged when event is a result of external stimuli, i.e. when
// not already running HandleKeypress
@@ -66,7 +66,7 @@ namespace Mono.TextEditor
}
}
- internal void InternalCaretPositionChanged (TextEditor editor, TextEditorData data)
+ internal void InternalCaretPositionChanged (MonoTextEditor editor, TextEditorData data)
{
// only trigger CaretPositionChanged when event is a result of external stimuli, i.e. when
// not already running HandleKeypress
@@ -95,7 +95,7 @@ namespace Mono.TextEditor
protected Caret Caret { get { return textEditorData.Caret; } }
protected TextDocument Document { get { return textEditorData.Document; } }
- protected TextEditor Editor { get { return editor; } }
+ protected MonoTextEditor Editor { get { return editor; } }
protected TextEditorData Data { get { return textEditorData; } }
protected abstract void HandleKeypress (Gdk.Key key, uint unicodeKey, Gdk.ModifierType modifier);
@@ -266,7 +266,7 @@ namespace Mono.TextEditor
}
#region TextAreaControl
- public virtual void AllocateTextArea (TextEditor textEditor, TextArea textArea, Rectangle allocation)
+ public virtual void AllocateTextArea (MonoTextEditor textEditor, TextArea textArea, Rectangle allocation)
{
if (textArea.Allocation != allocation)
textArea.SizeAllocate (allocation);
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/ActionMargin.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/ActionMargin.cs
index ba069dfc6e..ea054fb966 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/ActionMargin.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/ActionMargin.cs
@@ -30,7 +30,7 @@ namespace Mono.TextEditor
{
public class ActionMargin : Margin
{
- readonly TextEditor editor;
+ readonly MonoTextEditor editor;
double marginWidth;
public override double Width {
@@ -39,7 +39,7 @@ namespace Mono.TextEditor
}
}
- public ActionMargin (TextEditor editor)
+ public ActionMargin (MonoTextEditor editor)
{
if (editor == null)
throw new ArgumentNullException ("editor");
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/CodeSegmentEditorWindow.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/CodeSegmentEditorWindow.cs
index b461bc6b7a..93115383ac 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/CodeSegmentEditorWindow.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/CodeSegmentEditorWindow.cs
@@ -29,7 +29,7 @@ namespace Mono.TextEditor
{
public class CodeSegmentEditorWindow : Gtk.Window
{
- TextEditor codeSegmentEditor = new TextEditor ();
+ MonoTextEditor codeSegmentEditor = new MonoTextEditor ();
public ISyntaxMode SyntaxMode {
get {
@@ -49,7 +49,7 @@ namespace Mono.TextEditor
}
}
- public CodeSegmentEditorWindow (TextEditor editor) : base (Gtk.WindowType.Toplevel)
+ public CodeSegmentEditorWindow (MonoTextEditor editor) : base (Gtk.WindowType.Toplevel)
{
Gtk.ScrolledWindow scrolledWindow = new Gtk.ScrolledWindow ();
scrolledWindow.Child = codeSegmentEditor;
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/CodeSegmentPreviewWindow.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/CodeSegmentPreviewWindow.cs
index 29a546cb20..424ba054cc 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/CodeSegmentPreviewWindow.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/CodeSegmentPreviewWindow.cs
@@ -38,7 +38,7 @@ namespace Mono.TextEditor
{
const int DefaultPreviewWindowWidth = 320;
const int DefaultPreviewWindowHeight = 200;
- TextEditor editor;
+ MonoTextEditor editor;
Pango.FontDescription fontDescription;
Pango.Layout layout;
Pango.Layout informLayout;
@@ -64,11 +64,11 @@ namespace Mono.TextEditor
}
}
- public CodeSegmentPreviewWindow (TextEditor editor, bool hideCodeSegmentPreviewInformString, TextSegment segment, bool removeIndent = true) : this(editor, hideCodeSegmentPreviewInformString, segment, DefaultPreviewWindowWidth, DefaultPreviewWindowHeight, removeIndent)
+ public CodeSegmentPreviewWindow (MonoTextEditor editor, bool hideCodeSegmentPreviewInformString, TextSegment segment, bool removeIndent = true) : this(editor, hideCodeSegmentPreviewInformString, segment, DefaultPreviewWindowWidth, DefaultPreviewWindowHeight, removeIndent)
{
}
- public CodeSegmentPreviewWindow (TextEditor editor, bool hideCodeSegmentPreviewInformString, TextSegment segment, int width, int height, bool removeIndent = true) : base (Gtk.WindowType.Popup)
+ public CodeSegmentPreviewWindow (MonoTextEditor editor, bool hideCodeSegmentPreviewInformString, TextSegment segment, int width, int height, bool removeIndent = true) : base (Gtk.WindowType.Popup)
{
this.HideCodeSegmentPreviewInformString = hideCodeSegmentPreviewInformString;
this.Segment = segment;
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/DashedLineMargin.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/DashedLineMargin.cs
index 154111bc09..db52b966d9 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/DashedLineMargin.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/DashedLineMargin.cs
@@ -30,7 +30,7 @@ namespace Mono.TextEditor
{
public class DashedLineMargin : Margin
{
- TextEditor editor;
+ MonoTextEditor editor;
Cairo.Color color;
public override double Width {
@@ -39,7 +39,7 @@ namespace Mono.TextEditor
}
}
- public DashedLineMargin (TextEditor editor)
+ public DashedLineMargin (MonoTextEditor editor)
{
this.editor = editor;
}
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/FoldMarkerMargin.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/FoldMarkerMargin.cs
index 7a87bfe63a..68aa69fb9f 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/FoldMarkerMargin.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/FoldMarkerMargin.cs
@@ -35,7 +35,7 @@ namespace Mono.TextEditor
{
public class FoldMarkerMargin : Margin
{
- TextEditor editor;
+ MonoTextEditor editor;
DocumentLine lineHover;
Pango.Layout layout;
@@ -63,7 +63,7 @@ namespace Mono.TextEditor
}
}
- public FoldMarkerMargin (TextEditor editor)
+ public FoldMarkerMargin (MonoTextEditor editor)
{
this.editor = editor;
layout = PangoUtil.CreateLayout (editor);
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/FoldingScreenbackgroundRenderer.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/FoldingScreenbackgroundRenderer.cs
index 6c392f8ae7..c1803dbdaf 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/FoldingScreenbackgroundRenderer.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/FoldingScreenbackgroundRenderer.cs
@@ -31,7 +31,7 @@ namespace Mono.TextEditor
{
public class FoldingScreenbackgroundRenderer : IBackgroundRenderer, IDisposable
{
- TextEditor editor;
+ MonoTextEditor editor;
List<FoldSegment> foldSegments;
[Flags]
enum Roles
@@ -54,7 +54,7 @@ namespace Mono.TextEditor
}
}
- public FoldingScreenbackgroundRenderer (TextEditor editor, IEnumerable<FoldSegment> foldSegments)
+ public FoldingScreenbackgroundRenderer (MonoTextEditor editor, IEnumerable<FoldSegment> foldSegments)
{
this.editor = editor;
this.foldSegments = new List<FoldSegment> (foldSegments);
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/GtkWorkarounds.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/GtkWorkarounds.cs
index 01d2665829..b3dcc07170 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/GtkWorkarounds.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/GtkWorkarounds.cs
@@ -34,7 +34,7 @@ using Gtk;
namespace Mono.TextEditor
{
- public static class GtkWorkarounds
+ static class GtkWorkarounds
{
const string LIBOBJC ="/usr/lib/libobjc.dylib";
const string USER32DLL = "User32.dll";
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/GutterMargin.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/GutterMargin.cs
index 1428c3fc0c..8c69267e8b 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/GutterMargin.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/GutterMargin.cs
@@ -34,13 +34,13 @@ namespace Mono.TextEditor
{
public class GutterMargin : Margin
{
- TextEditor editor;
+ MonoTextEditor editor;
int width;
int oldLineCountLog10 = -1;
double fontHeight;
- public GutterMargin (TextEditor editor)
+ public GutterMargin (MonoTextEditor editor)
{
this.editor = editor;
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/HslColor.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/HslColor.cs
index 6f7f446845..f8e525f939 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/HslColor.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/HslColor.cs
@@ -30,7 +30,7 @@ using System.Collections.Generic;
namespace Mono.TextEditor
{
- public struct HslColor
+ struct HslColor
{
public double H {
get;
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/ITooltipProvider.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/ITooltipProvider.cs
index cd0cb8d027..6cdc53eccb 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/ITooltipProvider.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/ITooltipProvider.cs
@@ -50,5 +50,15 @@ namespace Mono.TextEditor
this.ItemSegment = new TextSegment (offset, length);
this.Item = item;
}
+
+ public override bool Equals (object obj)
+ {
+ return ItemSegment.Equals (((TooltipItem)obj).ItemSegment);
+ }
+
+ public override int GetHashCode ()
+ {
+ return ItemSegment.GetHashCode ();
+ }
}
}
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/IconMargin.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/IconMargin.cs
index 89f264056d..f3ab923632 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/IconMargin.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/IconMargin.cs
@@ -33,11 +33,11 @@ namespace Mono.TextEditor
{
public class IconMargin : Margin
{
- TextEditor editor;
+ MonoTextEditor editor;
Cairo.Color backgroundColor, separatorColor;
const int marginWidth = 22;
- public IconMargin (TextEditor editor)
+ public IconMargin (MonoTextEditor editor)
{
this.editor = editor;
}
@@ -136,7 +136,7 @@ namespace Mono.TextEditor
public class BookmarkMarginDrawEventArgs : EventArgs
{
- public TextEditor Editor {
+ public MonoTextEditor Editor {
get;
private set;
}
@@ -166,7 +166,7 @@ namespace Mono.TextEditor
private set;
}
- public BookmarkMarginDrawEventArgs (TextEditor editor, Cairo.Context context, DocumentLine line, int lineNumber, double xPos, double yPos)
+ public BookmarkMarginDrawEventArgs (MonoTextEditor editor, Cairo.Context context, DocumentLine line, int lineNumber, double xPos, double yPos)
{
this.Editor = editor;
this.Context = context;
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/Margin.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/Margin.cs
index 9a132f34ee..fdd570e2c3 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/Margin.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/Margin.cs
@@ -182,18 +182,18 @@ namespace Mono.TextEditor
}
}
- public TextEditor Editor {
+ public MonoTextEditor Editor {
get;
private set;
}
- public MarginMouseEventArgs (TextEditor editor, Gdk.Event raw, uint button, double x, double y, Gdk.ModifierType modifierState)
+ public MarginMouseEventArgs (MonoTextEditor editor, Gdk.Event raw, uint button, double x, double y, Gdk.ModifierType modifierState)
: this (editor, raw.Type, button, x, y, modifierState)
{
this.RawEvent = raw;
}
- public MarginMouseEventArgs (TextEditor editor, Gdk.EventType type, uint button, double x, double y, Gdk.ModifierType modifierState)
+ public MarginMouseEventArgs (MonoTextEditor editor, Gdk.EventType type, uint button, double x, double y, Gdk.ModifierType modifierState)
{
this.Editor = editor;
this.Type = type;
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/TextEditor.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/MonoTextEditor.cs
index 9c6542011c..29c4bde505 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/TextEditor.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/MonoTextEditor.cs
@@ -41,7 +41,7 @@ namespace Mono.TextEditor
{
[System.ComponentModel.Category("Mono.TextEditor")]
[System.ComponentModel.ToolboxItem(true)]
- public class TextEditor : Container
+ public class MonoTextEditor : Container
{
readonly TextArea textArea;
@@ -60,21 +60,21 @@ namespace Mono.TextEditor
}
}
- public TextEditor () : this(new TextDocument ())
+ public MonoTextEditor () : this(new TextDocument ())
{
}
- public TextEditor (TextDocument doc)
+ public MonoTextEditor (TextDocument doc)
: this (doc, null)
{
}
- public TextEditor (TextDocument doc, ITextEditorOptions options)
+ public MonoTextEditor (TextDocument doc, ITextEditorOptions options)
: this (doc, options, new SimpleEditMode ())
{
}
- public TextEditor (TextDocument doc, ITextEditorOptions options, EditMode initialMode)
+ public MonoTextEditor (TextDocument doc, ITextEditorOptions options, EditMode initialMode)
{
GtkWorkarounds.FixContainerLeak (this);
WidgetFlags |= WidgetFlags.NoWindow;
@@ -376,6 +376,9 @@ namespace Mono.TextEditor
get {
return textArea.Document;
}
+ set {
+ textArea.Document = value;
+ }
}
public bool IsDisposed {
@@ -723,7 +726,16 @@ namespace Mono.TextEditor
textArea.SelectionAnchor = value;
}
}
-
+
+ public int SelectionLead {
+ get {
+ return textArea.SelectionLead;
+ }
+ set {
+ textArea.SelectionLead = value;
+ }
+ }
+
public IEnumerable<DocumentLine> SelectedLines {
get {
return textArea.SelectedLines;
@@ -1203,6 +1215,8 @@ namespace Mono.TextEditor
public double ColumnToX (DocumentLine line, int column)
{
+ if (line == null)
+ throw new ArgumentNullException ("line");
return TextViewMargin.ColumnToX (line, column);
}
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/TextArea.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/TextArea.cs
index ebb0580971..4a2415bfa4 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/TextArea.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/TextArea.cs
@@ -76,6 +76,9 @@ namespace Mono.TextEditor
get {
return textEditorData.Document;
}
+ set {
+ textEditorData.Document = value;
+ }
}
public bool IsDisposed {
@@ -296,8 +299,8 @@ namespace Mono.TextEditor
ResizeMode = ResizeMode.Queue;
}
- TextEditor editor;
- internal void Initialize (TextEditor editor, TextDocument doc, ITextEditorOptions options, EditMode initialMode)
+ MonoTextEditor editor;
+ internal void Initialize (MonoTextEditor editor, TextDocument doc, ITextEditorOptions options, EditMode initialMode)
{
if (doc == null)
throw new ArgumentNullException ("doc");
@@ -1479,8 +1482,10 @@ namespace Mono.TextEditor
} else {
double caretX = ColumnToX (Document.GetLine (p.Line), p.Column);
double textWith = Allocation.Width - textViewMargin.XOffset;
- if (this.textEditorData.HAdjustment.Value > caretX) {
- this.textEditorData.HAdjustment.Value = caretX;
+ if (caretX < this.textEditorData.HAdjustment.Upper) {
+ this.textEditorData.HAdjustment.Value = 0;
+ } else if (this.textEditorData.HAdjustment.Value > caretX) {
+ this.textEditorData.HAdjustment.Value = System.Math.Max (0, caretX - this.textEditorData.HAdjustment.Upper / 2);
} else if (this.textEditorData.HAdjustment.Value + textWith < caretX + TextViewMargin.CharWidth) {
double adjustment = System.Math.Max (0, caretX - textWith + TextViewMargin.CharWidth);
this.textEditorData.HAdjustment.Value = adjustment;
@@ -1953,7 +1958,16 @@ namespace Mono.TextEditor
this.textEditorData.SelectionAnchor = value;
}
}
-
+
+ public int SelectionLead {
+ get {
+ return this.textEditorData.SelectionLead;
+ }
+ set {
+ this.textEditorData.SelectionLead = value;
+ }
+ }
+
public IEnumerable<DocumentLine> SelectedLines {
get {
return this.textEditorData.SelectedLines;
@@ -2277,7 +2291,7 @@ namespace Mono.TextEditor
class CaretPulseAnimation : IAnimationDrawer
{
- TextEditor editor;
+ MonoTextEditor editor;
public double Percent { get; set; }
@@ -2294,7 +2308,7 @@ namespace Mono.TextEditor
}
}
- public CaretPulseAnimation (TextEditor editor)
+ public CaretPulseAnimation (MonoTextEditor editor)
{
this.editor = editor;
}
@@ -2331,7 +2345,7 @@ namespace Mono.TextEditor
public class RegionPulseAnimation : IAnimationDrawer
{
- TextEditor editor;
+ MonoTextEditor editor;
public PulseKind Kind { get; set; }
public double Percent { get; set; }
@@ -2352,10 +2366,10 @@ namespace Mono.TextEditor
}
}
- public RegionPulseAnimation (TextEditor editor, Gdk.Point position, Gdk.Size size)
+ public RegionPulseAnimation (MonoTextEditor editor, Gdk.Point position, Gdk.Size size)
: this (editor, new Gdk.Rectangle (position, size)) {}
- public RegionPulseAnimation (TextEditor editor, Gdk.Rectangle region)
+ public RegionPulseAnimation (MonoTextEditor editor, Gdk.Rectangle region)
{
if (region.X < 0 || region.Y < 0 || region.Width < 0 || region.Height < 0)
throw new ArgumentException ("region is invalid");
@@ -2478,7 +2492,7 @@ namespace Mono.TextEditor
set;
}
- public SearchHighlightPopupWindow (TextEditor editor) : base (editor)
+ public SearchHighlightPopupWindow (MonoTextEditor editor) : base (editor)
{
}
@@ -2753,9 +2767,11 @@ namespace Mono.TextEditor
tipItem = item;
Gtk.Window tw = null;
try {
- tw = provider.ShowTooltipWindow (editor, nextTipOffset, nextTipModifierState, tipX + (int) TextViewMargin.XOffset, tipY, item);
+ tw = provider.CreateTooltipWindow (editor, nextTipOffset, nextTipModifierState, item);
+ if (tw != null)
+ provider.ShowTooltipWindow (editor, tw, nextTipOffset, nextTipModifierState, tipX + (int) TextViewMargin.XOffset, tipY, item);
} catch (Exception e) {
- Console.WriteLine ("-------- Exception while creating tooltip:");
+ Console.WriteLine ("-------- Exception while creating tooltip: " + provider);
Console.WriteLine (e);
}
if (tw == tipWindow)
@@ -2821,9 +2837,12 @@ namespace Mono.TextEditor
nextTipOffset = -1;
}
- void OnDocumentStateChanged (object s, EventArgs a)
+ void OnDocumentStateChanged (object s, DocumentChangeEventArgs args)
{
HideTooltip ();
+ var start = editor.Document.OffsetToLineNumber (args.Offset);
+ var end = editor.Document.OffsetToLineNumber (args.Offset + args.InsertionLength);
+ editor.Document.CommitMultipleLineUpdate (start, end);
}
void OnTextSet (object sender, EventArgs e)
@@ -3047,12 +3066,12 @@ namespace Mono.TextEditor
class SetCaret
{
- TextEditor view;
+ MonoTextEditor view;
int line, column;
bool highlightCaretLine;
bool centerCaret;
- public SetCaret (TextEditor view, int line, int column, bool highlightCaretLine, bool centerCaret)
+ public SetCaret (MonoTextEditor view, int line, int column, bool highlightCaretLine, bool centerCaret)
{
this.view = view;
this.line = line;
@@ -3122,12 +3141,12 @@ namespace Mono.TextEditor
return Gtk.Widget.GType;
}
- internal List<TextEditor.EditorContainerChild> containerChildren = new List<TextEditor.EditorContainerChild> ();
+ internal List<MonoTextEditor.EditorContainerChild> containerChildren = new List<MonoTextEditor.EditorContainerChild> ();
public void AddTopLevelWidget (Gtk.Widget widget, int x, int y)
{
widget.Parent = this;
- TextEditor.EditorContainerChild info = new TextEditor.EditorContainerChild (this, widget);
+ MonoTextEditor.EditorContainerChild info = new MonoTextEditor.EditorContainerChild (this, widget);
info.X = x;
info.Y = y;
containerChildren.Add (info);
@@ -3218,7 +3237,7 @@ namespace Mono.TextEditor
containerChildren.ForEach (child => child.Child.Unmap ());
}
- void ResizeChild (Rectangle allocation, TextEditor.EditorContainerChild child)
+ void ResizeChild (Rectangle allocation, MonoTextEditor.EditorContainerChild child)
{
Requisition req = child.Child.SizeRequest ();
var childRectangle = new Gdk.Rectangle (child.X, child.Y, req.Width, req.Height);
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/TextViewMargin.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/TextViewMargin.cs
index c055338beb..68441c45cd 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/TextViewMargin.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/TextViewMargin.cs
@@ -42,7 +42,7 @@ namespace Mono.TextEditor
{
public class TextViewMargin : Margin
{
- readonly TextEditor textEditor;
+ readonly MonoTextEditor textEditor;
Pango.TabArray tabArray;
Pango.Layout markerLayout, defaultLayout;
Pango.Layout[] eolMarkerLayout;
@@ -120,7 +120,7 @@ namespace Mono.TextEditor
}
- public TextViewMargin (TextEditor textEditor)
+ public TextViewMargin (MonoTextEditor textEditor)
{
if (textEditor == null)
throw new ArgumentNullException ("textEditor");
@@ -1630,18 +1630,26 @@ namespace Mono.TextEditor
TextRenderEndPosition = xPos + width,
LineHeight = _lineHeight,
- WholeLineWidth = textEditor.Allocation.Width - xPos
+ WholeLineWidth = textEditor.Allocation.Width - xPos,
+
+ LineYRenderStartPosition = y
};
foreach (TextLineMarker marker in line.Markers) {
if (!marker.IsVisible)
continue;
- if (marker.DrawBackground (textEditor, cr, y, metrics)) {
+ if (marker.DrawBackground (textEditor, cr, metrics)) {
isSelectionDrawn |= (marker.Flags & TextLineMarkerFlags.DrawsSelection) == TextLineMarkerFlags.DrawsSelection;
}
}
+ foreach (var marker in Document.GetTextSegmentMarkersAt (line).Where (m => m.IsVisible)) {
+ if (layout.Layout != null)
+ marker.DrawBackground (textEditor, cr, metrics, offset, offset + length);
+ }
+
+
if (DecorateLineBg != null)
DecorateLineBg (cr, layout, offset, length, xPos, y, selectionStartOffset, selectionEndOffset);
@@ -1819,12 +1827,12 @@ namespace Mono.TextEditor
}
foreach (TextLineMarker marker in line.Markers.Where (m => m.IsVisible)) {
if (layout.Layout != null)
- marker.Draw (textEditor, cr, y, metrics);
+ marker.Draw (textEditor, cr, metrics);
}
foreach (var marker in Document.GetTextSegmentMarkersAt (line).Where (m => m.IsVisible)) {
if (layout.Layout != null)
- marker.Draw (textEditor, cr, layout.Layout, false, /*selected*/offset, offset + length, y, xPos, xPos + width);
+ marker.Draw (textEditor, cr, metrics, offset, offset + length);
}
position += System.Math.Floor (layout.LastLineWidth);
@@ -2037,7 +2045,7 @@ namespace Mono.TextEditor
textEditor.ClearSelection ();
Caret.Location = clickLocation;
InSelectionDrag = true;
- textEditor.SetSelection (clickLocation, clickLocation);
+ textEditor.MainSelection = new Selection (clickLocation, clickLocation);
}
textEditor.RequestResetCaretBlink ();
}
@@ -2417,6 +2425,8 @@ namespace Mono.TextEditor
public static int GetNextTabstop (TextEditorData textEditor, int currentColumn, int tabSize)
{
+ if (tabSize == 0)
+ return currentColumn;
int result = currentColumn - 1 + tabSize;
return 1 + (result / tabSize) * tabSize;
}
@@ -2878,10 +2888,11 @@ namespace Mono.TextEditor
var metrics = new EndOfLineMetrics {
LineSegment = line,
TextRenderEndPosition = TextStartPosition + position,
- LineHeight = _lineHeight
+ LineHeight = _lineHeight,
+ LineYRenderStartPosition = y
};
foreach (var marker in line.Markers) {
- marker.DrawAfterEol (textEditor, cr, y, metrics);
+ marker.DrawAfterEol (textEditor, cr, metrics);
}
}
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/TooltipProvider.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/TooltipProvider.cs
index 20ea1309e9..c3467dcb75 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/TooltipProvider.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/Gui/TooltipProvider.cs
@@ -29,30 +29,26 @@ namespace Mono.TextEditor
{
public abstract class TooltipProvider
{
- public abstract TooltipItem GetItem (TextEditor editor, int offset);
+ public abstract TooltipItem GetItem (MonoTextEditor editor, int offset);
- public virtual bool IsInteractive (TextEditor editor, Gtk.Window tipWindow)
+ public virtual bool IsInteractive (MonoTextEditor editor, Gtk.Window tipWindow)
{
return false;
}
- protected virtual void GetRequiredPosition (TextEditor editor, Gtk.Window tipWindow, out int requiredWidth, out double xalign)
+ protected virtual void GetRequiredPosition (MonoTextEditor editor, Gtk.Window tipWindow, out int requiredWidth, out double xalign)
{
requiredWidth = tipWindow.SizeRequest ().Width;
xalign = 0.5;
}
- protected virtual Gtk.Window CreateTooltipWindow (TextEditor editor, int offset, Gdk.ModifierType modifierState, TooltipItem item)
+ public virtual Gtk.Window CreateTooltipWindow (MonoTextEditor editor, int offset, Gdk.ModifierType modifierState, TooltipItem item)
{
return null;
}
- public virtual Gtk.Window ShowTooltipWindow (TextEditor editor, int offset, Gdk.ModifierType modifierState, int mouseX, int mouseY, TooltipItem item)
+ public virtual Gtk.Window ShowTooltipWindow (MonoTextEditor editor, Gtk.Window tipWindow, int offset, Gdk.ModifierType modifierState, int mouseX, int mouseY, TooltipItem item)
{
- Gtk.Window tipWindow = CreateTooltipWindow (editor, offset, modifierState, item);
- if (tipWindow == null)
- return null;
-
int ox = 0, oy = 0;
if (editor.GdkWindow != null)
editor.GdkWindow.GetOrigin (out ox, out oy);
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/HeightTree.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/HeightTree.cs
index 7ea191115a..768a4c1a68 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/HeightTree.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/HeightTree.cs
@@ -70,7 +70,9 @@ namespace Mono.TextEditor
void HandleLineRemoved (object sender, LineEventArgs e)
{
- RemoveLine (e.Line.LineNumber);
+ Rebuild ();
+ OnLineUpdateFrom (new HeightChangedEventArgs (e.Line.LineNumber - 1));
+ //RemoveLine (e.Line.LineNumber);
}
public void Dispose ()
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/HelperMethods.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/HelperMethods.cs
index 1b74818031..29c68acd36 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/HelperMethods.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/HelperMethods.cs
@@ -34,7 +34,7 @@ using System.Collections.Generic;
namespace Mono.TextEditor
{
- public static class HelperMethods
+ public static class PublicHelperMethods
{
public static TextSegment AdjustSegment (this TextSegment segment, DocumentChangeEventArgs args)
{
@@ -50,7 +50,10 @@ namespace Mono.TextEditor
yield return segment.AdjustSegment (args);
}
}
+ }
+ static class HelperMethods
+ {
public static T Kill<T>(this T gc) where T : IDisposable
{
if (gc != null)
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/ITextEditorOptions.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/ITextEditorOptions.cs
index 3c5e8551e3..732f4d2302 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/ITextEditorOptions.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/ITextEditorOptions.cs
@@ -29,21 +29,23 @@ using Mono.TextEditor.Highlighting;
namespace Mono.TextEditor
{
- public enum ShowWhitespaces {
+ public enum ShowWhitespaces
+ {
Never,
Selection,
Always
}
[Flags]
- public enum IncludeWhitespaces {
- None = 0,
- Space = 1,
- Tab = 2,
+ public enum IncludeWhitespaces
+ {
+ None = 0,
+ Space = 1,
+ Tab = 2,
LineEndings = 4,
- All = Space | Tab | LineEndings
+ All = Space | Tab | LineEndings
}
-
+
public interface ITextEditorOptions : IDisposable
{
double Zoom { get; set; }
@@ -53,11 +55,11 @@ namespace Mono.TextEditor
void ZoomIn ();
void ZoomOut ();
void ZoomReset ();
-
+
string IndentationString { get; }
-
+
IWordFindStrategy WordFindStrategy { get; set; }
-
+
bool AllowTabsAfterNonTabs { get; set; }
bool HighlightMatchingBracket { get; set; }
bool TabsToSpaces { get; set; }
@@ -80,21 +82,23 @@ namespace Mono.TextEditor
bool DrawIndentationMarkers { get; set; }
bool WrapLines { get; set; }
- string FontName { get; set; }
- Pango.FontDescription Font { get; }
+ string FontName { get; set; }
+ Pango.FontDescription Font { get; }
string GutterFontName { get; set; }
Pango.FontDescription GutterFont { get; }
-
- string ColorScheme { get; set; }
+
+ string ColorScheme { get; set; }
string DefaultEolMarker { get; set; }
ShowWhitespaces ShowWhitespaces { get; set; }
IncludeWhitespaces IncludeWhitespaces { get; set; }
bool GenerateFormattingUndoStep { get; set; }
+ event EventHandler ZoomChanged;
+
ColorScheme GetColorStyle ();
-
+
event EventHandler Changed;
}
}
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/InsertionCursorEditMode.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/InsertionCursorEditMode.cs
index b23ade4f51..f403816ab1 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/InsertionCursorEditMode.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/InsertionCursorEditMode.cs
@@ -97,9 +97,9 @@ namespace Mono.TextEditor
public class HelpWindowEditMode : SimpleEditMode
{
- protected new TextEditor editor;
+ protected new MonoTextEditor editor;
- public new TextEditor Editor {
+ public new MonoTextEditor Editor {
get {
return editor;
}
@@ -199,7 +199,7 @@ namespace Mono.TextEditor
get { return insertionPoints; }
}
- public InsertionCursorEditMode (TextEditor editor, List<InsertionPoint> insertionPoints)
+ public InsertionCursorEditMode (MonoTextEditor editor, List<InsertionPoint> insertionPoints)
{
this.editor = editor;
this.insertionPoints = insertionPoints;
@@ -324,7 +324,7 @@ namespace Mono.TextEditor
public double GetLineIndentationStart ()
{
- TextEditor editor = mode.editor;
+ MonoTextEditor editor = mode.editor;
var lineAbove = editor.Document.GetLine (mode.CurrentInsertionPoint.Line - 1);
var lineBelow = editor.Document.GetLine (mode.CurrentInsertionPoint.Line);
@@ -356,7 +356,7 @@ namespace Mono.TextEditor
public override void Draw (Cairo.Context cr, Cairo.Rectangle erea)
{
- TextEditor editor = mode.editor;
+ MonoTextEditor editor = mode.editor;
double y = editor.LineToY (mode.CurrentInsertionPoint.Line) - editor.VAdjustment.Value;
double x = GetLineIndentationStart ();
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/LineBackgroundMarker.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/LineBackgroundMarker.cs
index 3fead25185..f74f08ec90 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/LineBackgroundMarker.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/LineBackgroundMarker.cs
@@ -40,12 +40,12 @@ namespace Mono.TextEditor
this.color = color;
}
- public override bool DrawBackground (TextEditor editor, Cairo.Context cr, double y, LineMetrics metrics)
+ public override bool DrawBackground (MonoTextEditor editor, Cairo.Context cr, LineMetrics metrics)
{
if (metrics.SelectionStart > 0)
return true;
cr.SetSourceColor (color);
- cr.Rectangle (metrics.TextRenderStartPosition, y, metrics.TextRenderEndPosition - metrics.TextRenderStartPosition, editor.LineHeight);
+ cr.Rectangle (metrics.TextRenderStartPosition, metrics.LineYRenderStartPosition, metrics.TextRenderEndPosition - metrics.TextRenderStartPosition, editor.LineHeight);
cr.Fill ();
return true;
}
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/MarginMarker.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/MarginMarker.cs
index c8cd377311..26638df368 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/MarginMarker.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/MarginMarker.cs
@@ -128,7 +128,7 @@ namespace Mono.TextEditor
/// <summary>
/// Draws the foreground of the specified margin.
/// </summary>
- public virtual void DrawForeground (TextEditor editor, Cairo.Context cr, MarginDrawMetrics metrics)
+ public virtual void DrawForeground (MonoTextEditor editor, Cairo.Context cr, MarginDrawMetrics metrics)
{
}
@@ -136,7 +136,7 @@ namespace Mono.TextEditor
/// Draws the background of the specified margin.
/// </summary>
/// <returns>true, if the background is drawn. false if the margin should fallback to the default background renderer. </returns>
- public virtual bool DrawBackground (TextEditor editor, Cairo.Context cr, MarginDrawMetrics metrics)
+ public virtual bool DrawBackground (MonoTextEditor editor, Cairo.Context cr, MarginDrawMetrics metrics)
{
return false;
}
@@ -147,7 +147,7 @@ namespace Mono.TextEditor
/// <param name="editor">The text editor in which the event press occured.</param>
/// <param name="margin">The margin in which the event occured.</param>
/// <param name="args">The event arguments.</param>
- public virtual void InformMousePress (TextEditor editor, Margin margin, MarginMouseEventArgs args)
+ public virtual void InformMousePress (MonoTextEditor editor, Margin margin, MarginMouseEventArgs args)
{
}
@@ -157,7 +157,7 @@ namespace Mono.TextEditor
/// <param name="editor">The text editor in which the event press occured.</param>
/// <param name="margin">The margin in which the event occured.</param>
/// <param name="args">The event arguments.</param>
- public virtual void InformMouseRelease (TextEditor editor, Margin margin, MarginMouseEventArgs args)
+ public virtual void InformMouseRelease (MonoTextEditor editor, Margin margin, MarginMouseEventArgs args)
{
}
@@ -167,7 +167,7 @@ namespace Mono.TextEditor
/// <param name="editor">The text editor in which the event press occured.</param>
/// <param name="margin">The margin in which the event occured.</param>
/// <param name="args">The event arguments.</param>
- public virtual void InformMouseHover (TextEditor editor, Margin margin, MarginMouseEventArgs args)
+ public virtual void InformMouseHover (MonoTextEditor editor, Margin margin, MarginMouseEventArgs args)
{
}
}
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/TextEditorData.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/TextEditorData.cs
index a9c9e75adc..599ba88014 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/TextEditorData.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/TextEditorData.cs
@@ -95,7 +95,7 @@ namespace Mono.TextEditor
/// </summary>
public event EventHandler<EditModeChangedEventArgs> EditModeChanged;
- public TextEditor Parent {
+ public MonoTextEditor Parent {
get;
set;
}
@@ -207,24 +207,28 @@ namespace Mono.TextEditor
caret.PositionChanged += CaretPositionChanged;
options = TextEditorOptions.DefaultOptions;
-
document = doc;
+ AttachDocument ();
+ SearchEngine = new BasicSearchEngine ();
+
+ HeightTree = new HeightTree (this);
+ HeightTree.Rebuild ();
+ IndentationTracker = new DefaultIndentationTracker (document);
+ }
+
+ void AttachDocument ()
+ {
+ if (document == null)
+ return;
document.BeginUndo += OnBeginUndo;
document.EndUndo += OnEndUndo;
-
document.Undone += DocumentHandleUndone;
document.Redone += DocumentHandleRedone;
document.LineChanged += HandleDocLineChanged;
document.TextReplaced += HandleTextReplaced;
-
document.TextSet += HandleDocTextSet;
document.Folded += HandleTextEditorDataDocumentFolded;
document.FoldTreeUpdated += HandleFoldTreeUpdated;
- SearchEngine = new BasicSearchEngine ();
-
- HeightTree = new HeightTree (this);
- HeightTree.Rebuild ();
- IndentationTracker = new DefaultIndentationTracker (document);
}
void HandleFoldTreeUpdated (object sender, EventArgs e)
@@ -267,6 +271,12 @@ namespace Mono.TextEditor
get {
return document;
}
+ set {
+ DetachDocument ();
+ document = value;
+ this.caret.SetDocument (document);
+ AttachDocument ();
+ }
}
void HandleTextReplaced (object sender, DocumentChangeEventArgs e)
@@ -805,6 +815,26 @@ namespace Mono.TextEditor
}
}
+ public int SelectionLead {
+ get {
+ if (MainSelection.IsEmpty)
+ return -1;
+ return MainSelection.GetLeadOffset (this);
+ }
+ set {
+ DocumentLocation location = Document.OffsetToLocation (value);
+ if (mainSelection.IsEmpty) {
+ MainSelection = new Selection (location, location);
+ } else {
+ if (MainSelection.Anchor == location) {
+ MainSelection = MainSelection.WithAnchor (MainSelection.Lead);
+ } else {
+ MainSelection = MainSelection.WithLead (location);
+ }
+ }
+ }
+ }
+
/// <summary>
/// Gets or sets the selection range. If nothing is selected (Caret.Offset, 0) is returned.
/// </summary>
@@ -888,6 +918,10 @@ namespace Mono.TextEditor
public void SetSelection (int anchorOffset, int leadOffset)
{
+ if (anchorOffset == leadOffset) {
+ MainSelection = Selection.Empty;
+ return;
+ }
var anchor = document.OffsetToLocation (anchorOffset);
var lead = document.OffsetToLocation (leadOffset);
MainSelection = new Selection (anchor, lead);
@@ -895,7 +929,7 @@ namespace Mono.TextEditor
public void SetSelection (DocumentLocation anchor, DocumentLocation lead)
{
- MainSelection = new Selection (anchor, lead);
+ MainSelection = anchor == lead ? Selection.Empty : new Selection (anchor, lead);
}
public void SetSelection (int anchorLine, int anchorColumn, int leadLine, int leadColumn)
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/TextEditorOptions.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/TextEditorOptions.cs
index 3028e70c6d..2721756861 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/TextEditorOptions.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/TextEditorOptions.cs
@@ -65,10 +65,11 @@ namespace Mono.TextEditor
string colorStyle = DefaultColorStyle;
Pango.FontDescription font, gutterFont;
- double zoom = 1d;
IWordFindStrategy wordFindStrategy = new EmacsWordFindStrategy (true);
#region Zoom
+ static double zoom = 1d;
+ static event EventHandler StaticZoomChanged;
const double ZOOM_FACTOR = 1.1f;
const int ZOOM_MIN_POW = -4;
@@ -78,7 +79,7 @@ namespace Mono.TextEditor
public double Zoom {
get {
- return zoom;
+ return zoom;
}
set {
value = System.Math.Min (ZOOM_MAX, System.Math.Max (ZOOM_MIN, value));
@@ -90,39 +91,39 @@ namespace Mono.TextEditor
}
if (zoom != value) {
zoom = value;
- DisposeFont ();
- OnChanged (EventArgs.Empty);
+ StaticZoomChanged?.Invoke (this, EventArgs.Empty);
}
}
}
-
+ public event EventHandler ZoomChanged { add { StaticZoomChanged += value; } remove { StaticZoomChanged -= value; } }
+
public bool CanZoomIn {
get {
- return zoom < ZOOM_MAX - 0.000001d;
+ return Zoom < ZOOM_MAX - 0.000001d;
}
}
public bool CanZoomOut {
get {
- return zoom > ZOOM_MIN + 0.000001d;
+ return Zoom > ZOOM_MIN + 0.000001d;
}
}
public bool CanResetZoom {
get {
- return zoom != 1d;
+ return Zoom != 1d;
}
}
public void ZoomIn ()
{
- int oldPow = (int)System.Math.Round (System.Math.Log (zoom) / System.Math.Log (ZOOM_FACTOR));
+ int oldPow = (int)System.Math.Round (System.Math.Log (Zoom) / System.Math.Log (ZOOM_FACTOR));
Zoom = System.Math.Pow (ZOOM_FACTOR, oldPow + 1);
}
public void ZoomOut ()
{
- int oldPow = (int)System.Math.Round (System.Math.Log (zoom) / System.Math.Log (ZOOM_FACTOR));
+ int oldPow = (int)System.Math.Round (System.Math.Log (Zoom) / System.Math.Log (ZOOM_FACTOR));
Zoom = System.Math.Pow (ZOOM_FACTOR, oldPow - 1);
}
@@ -345,7 +346,7 @@ namespace Mono.TextEditor
}
}
- void DisposeFont ()
+ protected void DisposeFont ()
{
if (font != null) {
font.Dispose ();
@@ -543,7 +544,7 @@ namespace Mono.TextEditor
public virtual void CopyFrom (TextEditorOptions other)
{
- zoom = other.zoom;
+ Zoom = other.Zoom;
highlightMatchingBracket = other.highlightMatchingBracket;
tabsToSpaces = other.tabsToSpaces;
indentationSize = other.indentationSize;
@@ -568,11 +569,23 @@ namespace Mono.TextEditor
DisposeFont ();
OnChanged (EventArgs.Empty);
}
-
+
+ public TextEditorOptions ()
+ {
+ StaticZoomChanged += HandleStaticZoomChanged;
+ }
+
public virtual void Dispose ()
{
+ StaticZoomChanged -= HandleStaticZoomChanged;
}
-
+
+ void HandleStaticZoomChanged (object sender, EventArgs e)
+ {
+ DisposeFont ();
+ OnChanged (EventArgs.Empty);
+ }
+
protected void OnChanged (EventArgs args)
{
if (Changed != null)
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/TextLineMarker.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/TextLineMarker.cs
index 90f5d5fc52..a8eb35b235 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/TextLineMarker.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/TextLineMarker.cs
@@ -31,8 +31,8 @@ namespace Mono.TextEditor
{
public interface IExtendingTextLineMarker
{
- double GetLineHeight (TextEditor editor);
- void Draw (TextEditor editor, Cairo.Context cr, int lineNr, Cairo.Rectangle lineArea);
+ double GetLineHeight (MonoTextEditor editor);
+ void Draw (MonoTextEditor editor, Cairo.Context cr, int lineNr, Cairo.Rectangle lineArea);
}
public interface IActionTextLineMarker
@@ -40,9 +40,9 @@ namespace Mono.TextEditor
/// <returns>
/// true, if the mouse press was handled - false otherwise.
/// </returns>
- bool MousePressed (TextEditor editor, MarginMouseEventArgs args);
+ bool MousePressed (MonoTextEditor editor, MarginMouseEventArgs args);
- void MouseHover (TextEditor editor, MarginMouseEventArgs args, TextLineMarkerHoverResult result);
+ void MouseHover (MonoTextEditor editor, MarginMouseEventArgs args, TextLineMarkerHoverResult result);
}
public class TextLineMarkerHoverResult
@@ -90,6 +90,8 @@ namespace Mono.TextEditor
public double LineHeight { get; internal set; }
public double WholeLineWidth { get; internal set; }
+
+ public double LineYRenderStartPosition { get; internal set; }
}
public class EndOfLineMetrics
@@ -97,6 +99,7 @@ namespace Mono.TextEditor
public DocumentLine LineSegment { get; internal set; }
public double TextRenderEndPosition { get; internal set; }
public double LineHeight { get; internal set; }
+ public double LineYRenderStartPosition { get; internal set; }
}
public class TextLineMarker
@@ -116,7 +119,11 @@ namespace Mono.TextEditor
get;
set;
}
-
+
+ public object Tag {
+ get;
+ set;
+ }
bool isVisible = true;
public virtual bool IsVisible {
@@ -128,7 +135,7 @@ namespace Mono.TextEditor
{
}
- public virtual void Draw (TextEditor editor, Cairo.Context cr, double y, LineMetrics metrics)
+ public virtual void Draw (MonoTextEditor editor, Cairo.Context cr, LineMetrics metrics)
{
}
@@ -145,7 +152,7 @@ namespace Mono.TextEditor
/// <param name="cr">The cairo context.</param>
/// <param name="y">The y coordinate.</param>
/// <param name="metrics">The line metrics.</param>
- public virtual bool DrawBackground (TextEditor editor, Cairo.Context cr, double y, LineMetrics metrics)
+ public virtual bool DrawBackground (MonoTextEditor editor, Cairo.Context cr, LineMetrics metrics)
{
return false;
}
@@ -153,7 +160,7 @@ namespace Mono.TextEditor
/// <summary>
/// Is used to draw in the area after the visible text.
/// </summary>
- public virtual void DrawAfterEol (TextEditor textEditor, Cairo.Context cr, double y, EndOfLineMetrics lineHeight)
+ public virtual void DrawAfterEol (MonoTextEditor textEditor, Cairo.Context cr, EndOfLineMetrics metrics)
{
}
}
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/TextLinkEditMode.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/TextLinkEditMode.cs
index 16415127e2..017517c7ef 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/TextLinkEditMode.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/TextLinkEditMode.cs
@@ -188,7 +188,7 @@ namespace Mono.TextEditor
TextLinkTooltipProvider tooltipProvider;
- public TextLinkEditMode (TextEditor editor, int baseOffset, List<TextLink> links)
+ public TextLinkEditMode (MonoTextEditor editor, int baseOffset, List<TextLink> links)
{
this.editor = editor;
this.links = links;
@@ -547,7 +547,7 @@ namespace Mono.TextEditor
this.mode = mode;
}
#region ITooltipProvider implementation
- public override TooltipItem GetItem (TextEditor Editor, int offset)
+ public override TooltipItem GetItem (MonoTextEditor Editor, int offset)
{
int o = offset - mode.BaseOffset;
for (int i = 0; i < mode.Links.Count; i++) {
@@ -559,7 +559,7 @@ namespace Mono.TextEditor
//return mode.Links.First (l => l.PrimaryLink != null && l.PrimaryLink.Offset <= o && o <= l.PrimaryLink.EndOffset);
}
- protected override Gtk.Window CreateTooltipWindow (TextEditor Editor, int offset, Gdk.ModifierType modifierState, TooltipItem item)
+ public override Gtk.Window CreateTooltipWindow (MonoTextEditor Editor, int offset, Gdk.ModifierType modifierState, TooltipItem item)
{
TextLink link = item.Item as TextLink;
if (link == null || string.IsNullOrEmpty (link.Tooltip))
@@ -570,7 +570,7 @@ namespace Mono.TextEditor
return window;
}
- protected override void GetRequiredPosition (TextEditor Editor, Gtk.Window tipWindow, out int requiredWidth, out double xalign)
+ protected override void GetRequiredPosition (MonoTextEditor Editor, Gtk.Window tipWindow, out int requiredWidth, out double xalign)
{
TooltipWindow win = (TooltipWindow)tipWindow;
requiredWidth = win.SetMaxWidth (win.Screen.Width);
@@ -594,7 +594,7 @@ namespace Mono.TextEditor
IsVisible = true;
}
- public override bool DrawBackground (TextEditor editor, Cairo.Context cr, double y, LineMetrics metrics)
+ public override bool DrawBackground (MonoTextEditor editor, Cairo.Context cr, LineMetrics metrics)
{
int caretOffset = editor.Caret.Offset - BaseOffset;
@@ -625,7 +625,7 @@ namespace Mono.TextEditor
double x1 = metrics.TextRenderStartPosition + x_pos - 1;
double x2 = metrics.TextRenderStartPosition + x_pos2 - 1 + 0.5;
- cr.Rectangle (x1 + 0.5, y + 0.5, x2 - x1, editor.LineHeight - 1);
+ cr.Rectangle (x1 + 0.5, metrics.LineYRenderStartPosition + 0.5, x2 - x1, editor.LineHeight - 1);
cr.SetSourceColor (fillGc);
cr.FillPreserve ();
@@ -649,7 +649,7 @@ namespace Mono.TextEditor
return margin is GutterMargin;
}
- public override bool DrawBackground (TextEditor editor, Cairo.Context cr, MarginDrawMetrics metrics)
+ public override bool DrawBackground (MonoTextEditor editor, Cairo.Context cr, MarginDrawMetrics metrics)
{
var width = metrics.Width;
@@ -661,7 +661,7 @@ namespace Mono.TextEditor
return true;
}
- public override void DrawForeground (TextEditor editor, Cairo.Context cr, MarginDrawMetrics metrics)
+ public override void DrawForeground (MonoTextEditor editor, Cairo.Context cr, MarginDrawMetrics metrics)
{
var width = metrics.Width;
var lineNumberBgGC = editor.ColorStyle.LineNumbers.Background;
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/TextSegmentMarker.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/TextSegmentMarker.cs
index cb2c35d223..e76300b91a 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/TextSegmentMarker.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/TextSegmentMarker.cs
@@ -53,7 +53,11 @@ namespace Mono.TextEditor
{
}
- public virtual void Draw (TextEditor editor, Cairo.Context cr, Pango.Layout layout, bool selected, int startOffset, int endOffset, double y, double startXPos, double endXPos)
+ public virtual void Draw (MonoTextEditor editor, Context cr, LineMetrics metrics, int startOffset, int endOffset)
+ {
+ }
+
+ public virtual void DrawBackground (MonoTextEditor editor, Context cr, LineMetrics metrics, int startOffset, int endOffset)
{
}
@@ -67,7 +71,7 @@ namespace Mono.TextEditor
{
void TransformChunks (List<Chunk> chunks);
- void ChangeForeColor (TextEditor editor, Chunk chunk, ref Cairo.Color color);
+ void ChangeForeColor (MonoTextEditor editor, Chunk chunk, ref Cairo.Color color);
}
public class UnderlineTextSegmentMarker : TextSegmentMarker
@@ -88,14 +92,16 @@ namespace Mono.TextEditor
public Cairo.Color Color { get; set; }
public bool Wave { get; set; }
- public override void Draw (TextEditor editor, Cairo.Context cr, Pango.Layout layout, bool selected, int startOffset, int endOffset, double y, double startXPos, double endXPos)
+ public override void Draw (MonoTextEditor editor, Cairo.Context cr, LineMetrics metrics, int startOffset, int endOffset)
{
int markerStart = Segment.Offset;
int markerEnd = Segment.EndOffset;
if (markerEnd < startOffset || markerStart > endOffset)
return;
-
-
+ var layout = metrics.Layout.Layout;
+ double startXPos = metrics.TextRenderStartPosition;
+ double endXPos = metrics.TextRenderEndPosition;
+ double y = metrics.LineYRenderStartPosition;
if (editor.IsSomethingSelected) {
var range = editor.SelectionRange;
if (range.Contains (markerStart)) {
@@ -121,7 +127,7 @@ namespace Mono.TextEditor
InternalDraw (markerStart, markerEnd, editor, cr, layout, false, startOffset, endOffset, y, startXPos, endXPos);
}
- void InternalDraw (int markerStart, int markerEnd, TextEditor editor, Cairo.Context cr, Pango.Layout layout, bool selected, int startOffset, int endOffset, double y, double startXPos, double endXPos)
+ void InternalDraw (int markerStart, int markerEnd, MonoTextEditor editor, Cairo.Context cr, Pango.Layout layout, bool selected, int startOffset, int endOffset, double y, double startXPos, double endXPos)
{
if (markerStart >= markerEnd)
return;
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/UnderlineMarker.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/UnderlineMarker.cs
index 0e8f4240d2..06698c143b 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/UnderlineMarker.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/UnderlineMarker.cs
@@ -57,12 +57,13 @@ namespace Mono.TextEditor
public int EndCol { get; set; }
public bool Wave { get; set; }
- public override void Draw (TextEditor editor, Cairo.Context cr, double y, LineMetrics metrics)
+ public override void Draw (MonoTextEditor editor, Cairo.Context cr, LineMetrics metrics)
{
var startOffset = metrics.TextStartOffset;
int endOffset = metrics.TextEndOffset;
double startXPos = metrics.TextRenderStartPosition;
double endXPos = metrics.TextRenderEndPosition;
+ double y = metrics.LineYRenderStartPosition;
var layout = metrics.Layout.Layout;
int markerStart = LineSegment.Offset + System.Math.Max (StartCol - 1, 0);
@@ -95,7 +96,7 @@ namespace Mono.TextEditor
InternalDraw (markerStart, markerEnd, editor, cr, layout, false, startOffset, endOffset, y, startXPos, endXPos);
}
- void InternalDraw (int markerStart, int markerEnd, TextEditor editor, Cairo.Context cr, Pango.Layout layout, bool selected, int startOffset, int endOffset, double y, double startXPos, double endXPos)
+ void InternalDraw (int markerStart, int markerEnd, MonoTextEditor editor, Cairo.Context cr, Pango.Layout layout, bool selected, int startOffset, int endOffset, double y, double startXPos, double endXPos)
{
if (markerStart >= markerEnd)
return;
diff --git a/main/src/core/Mono.Texteditor/Mono.TextEditor/UrlMarker.cs b/main/src/core/Mono.Texteditor/Mono.TextEditor/UrlMarker.cs
index 88469244a8..681fee2e66 100644
--- a/main/src/core/Mono.Texteditor/Mono.TextEditor/UrlMarker.cs
+++ b/main/src/core/Mono.Texteditor/Mono.TextEditor/UrlMarker.cs
@@ -100,12 +100,13 @@ namespace Mono.TextEditor
line = null;
}
- public override void Draw (TextEditor editor, Cairo.Context cr, double y, LineMetrics metrics)
+ public override void Draw (MonoTextEditor editor, Cairo.Context cr, LineMetrics metrics)
{
var startOffset = metrics.TextStartOffset;
int endOffset = metrics.TextEndOffset;
double startXPos = metrics.TextRenderStartPosition;
double endXPos = metrics.TextRenderEndPosition;
+ double y = metrics.LineYRenderStartPosition;
var layout = metrics.Layout.Layout;
int markerStart = line.Offset + startColumn;
int markerEnd = line.Offset + endColumn;