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/MonoDevelop.Ide/MonoDevelop.Ide.Editor')
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/AutoSave.cs225
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Commands/DynamicAbbrevHandler.cs193
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/CustomEditorOptions.cs186
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DefaultSourceEditorOptions.cs785
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DocumentContext.cs158
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DocumentLocation.cs241
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DocumentRegion.cs181
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/EditActions.cs516
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/FileSettingsStore.cs73
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/FoldSegmentFactory.cs86
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/IDocumentLine.cs118
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/IFoldSegment.cs74
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/IReadonlyTextDocument.cs246
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/ITextDocument.cs140
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/ITextEditorOptions.cs124
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/ITextLineMarker.cs80
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/ITextSegmentMarker.cs86
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/IUnitTestMarker.cs38
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InsertionCursorEventArgs.cs51
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InsertionModeOptions.cs88
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InsertionPoint.cs119
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/IEditorActionHost.cs123
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/ITextEditorFactory.cs44
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/ITextEditorImpl.cs244
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/ITextMarkerFactory.cs58
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/LineEventArgs.cs48
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/MessageBubbles/MessageBubbleCommands.cs77
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/IProjectionExtension.cs39
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedCompletionExtension.cs433
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedDocumentContext.cs143
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedFilterCompletionTextEditorExtension.cs186
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedSegment.cs89
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedSemanticHighlighting.cs98
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedTooltipProvider.cs95
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/Projection.cs130
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/SegmentTree.cs801
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/SelectionMode.cs33
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditor.cs1260
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorDisplayBinding.cs114
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorFactory.cs95
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorViewContent.cs1233
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextLink.cs136
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextLinkModeEventArgs.cs43
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextLinkModeOptions.cs66
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextMarkerFactory.cs121
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextMarkerMouseEventArgs.cs49
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TooltipExtensionNode.cs49
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TooltipProvider.cs177
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/WordFindStrategy.cs38
49 files changed, 9830 insertions, 0 deletions
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/AutoSave.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/AutoSave.cs
new file mode 100644
index 0000000000..a0eacc97ef
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/AutoSave.cs
@@ -0,0 +1,225 @@
+//
+// AutoSave.cs
+//
+// Author:
+// Mike Krüger <mkrueger@novell.com>
+//
+// Copyright (c) 2009 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.IO;
+using System.Threading;
+using MonoDevelop.Core;
+using Gtk;
+using MonoDevelop.Core.Text;
+
+namespace MonoDevelop.Ide.Editor
+{
+ /// <summary>
+ /// This class handles the auto save mechanism for open files.
+ /// It should only be used by editor implementations.
+ /// </summary>
+ static class AutoSave
+ {
+ //FIXME: is this path a good one? wouldn't it be better to put autosaves beside the files anyway?
+ static string autoSavePath = UserProfile.Current.CacheDir.Combine ("AutoSave");
+ static bool autoSaveEnabled;
+
+ static AutoSave ()
+ {
+ try {
+ if (!Directory.Exists (autoSavePath))
+ Directory.CreateDirectory (autoSavePath);
+ } catch (Exception e) {
+ LoggingService.LogError ("Can't create auto save path:" + autoSavePath +". Auto save is disabled.", e);
+ autoSaveEnabled = false;
+ return;
+ }
+ autoSaveEnabled = true;
+ StartAutoSaveThread ();
+ }
+
+ static string GetAutoSaveFileName (string fileName)
+ {
+ if (fileName == null)
+ return null;
+ string newFileName = Path.Combine (Path.GetDirectoryName (fileName), Path.GetFileNameWithoutExtension (fileName) + Path.GetExtension (fileName) + "~");
+ newFileName = Path.Combine (autoSavePath, newFileName.Replace(',','_').Replace(" ","").Replace (":","").Replace (Path.DirectorySeparatorChar, '_').Replace (Path.AltDirectorySeparatorChar, '_'));
+ return newFileName;
+ }
+
+ /// <summary>
+ /// Returns true if an auto save exists for the given file name.
+ /// </summary>
+ public static bool AutoSaveExists (string fileName)
+ {
+ if (!autoSaveEnabled)
+ return false;
+ try {
+ var autoSaveFilename = GetAutoSaveFileName (fileName);
+ bool autoSaveExists = File.Exists (autoSaveFilename);
+ if (autoSaveExists) {
+ if (File.GetLastWriteTimeUtc (autoSaveFilename) < File.GetLastWriteTimeUtc (fileName)) {
+ File.Delete (autoSaveFilename);
+ return false;
+ }
+ }
+ return autoSaveExists;
+ } catch (Exception e) {
+ LoggingService.LogError ("Error in auto save - disableing.", e);
+ DisableAutoSave ();
+ return false;
+ }
+ }
+
+ static void CreateAutoSave (string fileName, ITextSource content)
+ {
+ if (!autoSaveEnabled)
+ return;
+ try {
+ // Directory may have removed/unmounted. Therefore this operation is not guaranteed to work.
+ var autosaveFileName = GetAutoSaveFileName (fileName);
+ if (File.Exists (autosaveFileName))
+ File.Delete (autosaveFileName);
+ content.WriteTextTo (autosaveFileName);
+ Counters.AutoSavedFiles++;
+ } catch (Exception e) {
+ LoggingService.LogError ("Error in auto save while creating: " + fileName +". Disableing auto save.", e);
+ DisableAutoSave ();
+ }
+ }
+
+ #region AutoSave
+ class FileContent
+ {
+ public string FileName;
+ public ITextSource Content;
+
+ public FileContent (string fileName, ITextSource content)
+ {
+ this.FileName = fileName;
+ this.Content = content;
+ }
+ }
+ static readonly AutoResetEvent resetEvent = new AutoResetEvent (false);
+
+ public static bool Running {
+ get {
+ return autoSaveThreadRunning;
+ }
+ }
+ static bool autoSaveThreadRunning = false;
+ static Thread autoSaveThread;
+ static Queue<FileContent> queue = new Queue<FileContent> ();
+ static object contentLock = new object ();
+
+ static void StartAutoSaveThread ()
+ {
+ autoSaveThreadRunning = true;
+ if (autoSaveThread == null) {
+ autoSaveThread = new Thread (AutoSaveThread);
+ autoSaveThread.Name = "Autosave";
+ autoSaveThread.IsBackground = true;
+ autoSaveThread.Start ();
+ }
+ }
+
+ static void AutoSaveThread ()
+ {
+ while (autoSaveThreadRunning) {
+ resetEvent.WaitOne ();
+ while (queue.Count > 0) {
+ var content = queue.Dequeue ();
+ // Don't create an auto save for unsaved files.
+ if (string.IsNullOrEmpty (content.FileName))
+ continue;
+ CreateAutoSave (content.FileName, content.Content);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Loads the content from an auto save file and removes the auto save file.
+ /// </summary>
+ public static ITextSource LoadAndRemoveAutoSave (string fileName)
+ {
+ string autoSaveFileName = GetAutoSaveFileName (fileName);
+ var result = StringTextSource.ReadFrom (autoSaveFileName);
+ AutoSave.RemoveAutoSaveFile (fileName);
+ return result;
+ }
+
+ /// <summary>
+ /// Loads the content from an auto save file.
+ /// </summary>
+ public static ITextSource LoadAutoSave (string fileName)
+ {
+ string autoSaveFileName = GetAutoSaveFileName (fileName);
+ return StringTextSource.ReadFrom (autoSaveFileName);
+ }
+
+ /// <summary>
+ /// Removes the auto save file.
+ /// </summary>
+ /// <param name="fileName">The file name for which the auto save file should be removed.</param>
+ public static void RemoveAutoSaveFile (string fileName)
+ {
+ if (!autoSaveEnabled)
+ return;
+ if (AutoSaveExists (fileName)) {
+ string autoSaveFileName = GetAutoSaveFileName (fileName);
+ try {
+ lock (contentLock) {
+ File.Delete (autoSaveFileName);
+ }
+ } catch (Exception e) {
+ LoggingService.LogError ("Can't delete auto save file: " + autoSaveFileName +". Disableing auto save.", e);
+ DisableAutoSave ();
+ }
+ }
+ }
+
+ internal static void InformAutoSaveThread (ITextSource content, string fileName, bool isDirty)
+ {
+ if (content == null || !autoSaveEnabled)
+ return;
+ if (isDirty) {
+ queue.Enqueue (new FileContent (fileName, content));
+ resetEvent.Set ();
+ } else {
+ RemoveAutoSaveFile (fileName);
+ }
+ }
+
+ static void DisableAutoSave ()
+ {
+ autoSaveThreadRunning = false;
+ if (autoSaveThread != null) {
+ resetEvent.Set ();
+ autoSaveThread.Join ();
+ autoSaveThread = null;
+ }
+ autoSaveEnabled = false;
+ }
+#endregion
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Commands/DynamicAbbrevHandler.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Commands/DynamicAbbrevHandler.cs
new file mode 100644
index 0000000000..08306da514
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Commands/DynamicAbbrevHandler.cs
@@ -0,0 +1,193 @@
+//
+// DynamicAbbrevHandler.cs
+//
+// Author:
+// Mike Krüger <mkrueger@novell.com>
+//
+// Copyright (c) 2009 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.Linq;
+using MonoDevelop.Components.Commands;
+using MonoDevelop.Ide.Gui;
+using System.Collections.Generic;
+using MonoDevelop.Ide;
+
+namespace MonoDevelop.Ide.Editor
+{
+ class DynamicAbbrevHandler : CommandHandler
+ {
+ enum AbbrevState {
+ SearchBackward,
+ SearchForward,
+ SearchOtherBuffers,
+ CycleThroughFoundWords
+ }
+
+ static TextEditor lastView = null;
+ static string lastAbbrev = null;
+ static int lastTriggerOffset = 0;
+ static int lastInsertPos = 0;
+ static List<string> foundWords = new List<string> ();
+ static int lastStartOffset = 0;
+ static AbbrevState curState;
+
+ protected override void Run (object data)
+ {
+ var doc = IdeApp.Workbench.ActiveDocument;
+ if (doc == null)
+ return;
+ var editor = doc.Editor;
+ if (editor == null)
+ return;
+
+ string abbrevWord;
+ int offset;
+ int startOffset;
+
+ if (lastView == editor && editor.CaretOffset == lastTriggerOffset) {
+ abbrevWord = lastAbbrev;
+ offset = lastStartOffset;
+ } else {
+ abbrevWord = GetWordBeforeCaret (editor);
+ lastAbbrev = abbrevWord;
+ offset = editor.CaretOffset - abbrevWord.Length - 1;
+ lastInsertPos = lastTriggerOffset = offset + 1;
+ foundWords.Clear ();
+ foundWords.Add (abbrevWord);
+ curState = AbbrevState.SearchBackward;
+ }
+
+ lastView = editor;
+ switch (curState) {
+ case AbbrevState.SearchBackward:
+ while (offset > 0) {
+ if (IsMatchAt (editor, offset, abbrevWord)) {
+ int endOffset = SearchEndPos (offset, editor);
+ string curWord = editor.GetTextBetween (offset, endOffset);
+ if (foundWords.Contains (curWord)) {
+ offset--;
+ continue;
+ }
+ foundWords.Add (curWord);
+ ReplaceWord (editor, curWord);
+ lastStartOffset = offset - 1;
+ return;
+ }
+ offset--;
+ }
+ offset = editor.CaretOffset;
+ curState = AbbrevState.SearchForward;
+ goto case AbbrevState.SearchForward;
+ case AbbrevState.SearchForward:
+ while (offset < editor.Length) {
+ if (IsMatchAt (editor, offset, abbrevWord)) {
+ int endOffset = SearchEndPos (offset, editor);
+ string curWord = editor.GetTextBetween (offset, endOffset);
+ if (foundWords.Contains (curWord)) {
+ offset++;
+ continue;
+ }
+ foundWords.Add (curWord);
+ ReplaceWord (editor, curWord);
+ lastStartOffset = offset + 1;
+ return;
+ }
+ offset++;
+ }
+ curState = AbbrevState.SearchOtherBuffers;
+ goto case AbbrevState.SearchOtherBuffers;
+ case AbbrevState.SearchOtherBuffers:
+ foreach (Document curDoc in IdeApp.Workbench.Documents) {
+ var otherView = curDoc.GetContent<TextEditor> ();
+ if (curDoc == doc || otherView == null)
+ continue;
+ for (int i = 0; i < otherView.Length; i++) {
+ if (IsMatchAt (otherView, i, abbrevWord)) {
+ int endOffset = SearchEndPos (i, otherView);
+ string curWord = otherView.GetTextBetween (i, endOffset);
+ if (foundWords.Contains (curWord))
+ continue;
+ foundWords.Add (curWord);
+ }
+ }
+ }
+ curState = AbbrevState.CycleThroughFoundWords;
+ goto case AbbrevState.CycleThroughFoundWords;
+ case AbbrevState.CycleThroughFoundWords:
+ int index = foundWords.IndexOf (editor.GetTextAt (lastInsertPos, editor.CaretOffset - lastInsertPos));
+ if (index < 0)
+ break;
+ startOffset = offset;
+ offset = startOffset + foundWords[index].Length;
+ index = (index + foundWords.Count + 1) % foundWords.Count;
+ ReplaceWord (editor, foundWords[index]);
+ break;
+ }
+ }
+
+ public static bool IsIdentifierPart (char ch)
+ {
+ return char.IsLetterOrDigit (ch) || ch == '_';
+ }
+
+ static string GetWordBeforeCaret (TextEditor editor)
+ {
+ int startOffset = editor.CaretOffset;
+ int offset = startOffset - 1;
+ while (offset > 0) {
+ char ch = editor.GetCharAt (offset);
+ if (!IsIdentifierPart (ch)) {
+ offset++;
+ break;
+ }
+ offset--;
+ }
+ if (offset >= startOffset)
+ return "";
+ return editor.GetTextBetween (offset, startOffset);
+ }
+
+ static void ReplaceWord (TextEditor editor, string curWord)
+ {
+ editor.ReplaceText (lastInsertPos, editor.CaretOffset - lastInsertPos, curWord);
+ lastTriggerOffset = editor.CaretOffset;
+ }
+
+ static int SearchEndPos (int offset, TextEditor editor)
+ {
+ while (offset < editor.Length && IsIdentifierPart (editor.GetCharAt (offset))) {
+ offset++;
+ }
+ return offset;
+ }
+
+ static bool IsMatchAt (TextEditor editor, int offset, string abbrevWord)
+ {
+ if (offset + abbrevWord.Length >= editor.Length)
+ return false;
+ if (offset > 0 && IsIdentifierPart (editor.GetCharAt (offset - 1)))
+ return false;
+ if (offset + abbrevWord.Length < editor.Length && !IsIdentifierPart (editor.GetCharAt (offset + abbrevWord.Length)))
+ return false;
+ return editor.GetTextAt (offset, abbrevWord.Length) == abbrevWord;
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/CustomEditorOptions.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/CustomEditorOptions.cs
new file mode 100644
index 0000000000..a3932167e2
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/CustomEditorOptions.cs
@@ -0,0 +1,186 @@
+//
+// ChangeableEditorOptions.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2015 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;
+
+namespace MonoDevelop.Ide.Editor
+{
+ public sealed class CustomEditorOptions : ITextEditorOptions
+ {
+ #region ITextEditorOptions implementation
+ public WordFindStrategy WordFindStrategy {
+ get;
+ set;
+ }
+
+ public bool TabsToSpaces {
+ get;
+ set;
+ }
+
+ public int IndentationSize {
+ get;
+ set;
+ }
+
+ public int TabSize {
+ get;
+ set;
+ }
+
+ public bool ShowIconMargin {
+ get;
+ set;
+ }
+
+ public bool ShowLineNumberMargin {
+ get;
+ set;
+ }
+
+ public bool ShowFoldMargin {
+ get;
+ set;
+ }
+
+ public bool HighlightCaretLine {
+ get;
+ set;
+ }
+
+ public int RulerColumn {
+ get;
+ set;
+ }
+
+ public bool ShowRuler {
+ get;
+ set;
+ }
+
+ public IndentStyle IndentStyle {
+ get;
+ set;
+ }
+
+ public bool OverrideDocumentEolMarker {
+ get;
+ set;
+ }
+
+ public bool EnableSyntaxHighlighting {
+ get;
+ set;
+ }
+
+ public bool RemoveTrailingWhitespaces {
+ get;
+ set;
+ }
+
+ public bool WrapLines {
+ get;
+ set;
+ }
+
+ public string FontName {
+ get;
+ set;
+ }
+
+ public string GutterFontName {
+ get;
+ set;
+ }
+
+ public string ColorScheme {
+ get;
+ set;
+ }
+
+ public string DefaultEolMarker {
+ get;
+ set;
+ }
+
+ public bool GenerateFormattingUndoStep {
+ get;
+ set;
+ }
+
+ public ShowWhitespaces ShowWhitespaces {
+ get;
+ set;
+ }
+
+ public IncludeWhitespaces IncludeWhitespaces {
+ get;
+ set;
+ }
+ #endregion
+
+ public CustomEditorOptions ()
+ {
+ this.ColorScheme = "Default";
+ this.TabSize = this.IndentationSize = 4;
+ this.DefaultEolMarker = "\n";
+ }
+
+ public CustomEditorOptions (ITextEditorOptions initializeFrom)
+ {
+ if (initializeFrom == null)
+ throw new ArgumentNullException (nameof (initializeFrom));
+ WordFindStrategy = initializeFrom.WordFindStrategy;
+ TabsToSpaces = initializeFrom.TabsToSpaces;
+ IndentationSize = initializeFrom.IndentationSize;
+ TabSize = initializeFrom.TabSize;
+ ShowIconMargin = initializeFrom.ShowIconMargin;
+ ShowLineNumberMargin = initializeFrom.ShowLineNumberMargin;
+ ShowFoldMargin = initializeFrom.ShowFoldMargin;
+ HighlightCaretLine = initializeFrom.HighlightCaretLine;
+ RulerColumn = initializeFrom.RulerColumn;
+ ShowRuler = initializeFrom.ShowRuler;
+ IndentStyle = initializeFrom.IndentStyle;
+ OverrideDocumentEolMarker = initializeFrom.OverrideDocumentEolMarker;
+ EnableSyntaxHighlighting = initializeFrom.EnableSyntaxHighlighting;
+ RemoveTrailingWhitespaces = initializeFrom.RemoveTrailingWhitespaces;
+ WrapLines = initializeFrom.WrapLines;
+ FontName = initializeFrom.FontName;
+ GutterFontName = initializeFrom.GutterFontName;
+ ColorScheme = initializeFrom.ColorScheme;
+ DefaultEolMarker = initializeFrom.DefaultEolMarker;
+ GenerateFormattingUndoStep = initializeFrom.GenerateFormattingUndoStep;
+ ShowWhitespaces = initializeFrom.ShowWhitespaces;
+ IncludeWhitespaces = initializeFrom.IncludeWhitespaces;
+ }
+
+ #region IDisposable implementation
+ public void Dispose ()
+ {
+ }
+ #endregion
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DefaultSourceEditorOptions.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DefaultSourceEditorOptions.cs
new file mode 100644
index 0000000000..1e83737769
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DefaultSourceEditorOptions.cs
@@ -0,0 +1,785 @@
+//
+// DefaultSourceEditorOptions.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 MonoDevelop.Core;
+using MonoDevelop.Ide.Gui.Content;
+using MonoDevelop.Ide.Fonts;
+using MonoDevelop.Ide.Editor.Extension;
+
+namespace MonoDevelop.Ide.Editor
+{
+ [Obsolete ("Use WordNavigationStyle")]
+ public enum ControlLeftRightMode
+ {
+ MonoDevelop,
+ Emacs,
+ SharpDevelop
+ }
+
+ public enum WordNavigationStyle
+ {
+ Unix,
+ Windows
+ }
+
+ public enum LineEndingConversion {
+ Ask,
+ LeaveAsIs,
+ ConvertAlways
+ }
+
+ /// <summary>
+ /// This class contains all text editor options from ITextEditorOptions and additional options
+ /// the text editor frontend may use.
+ /// </summary>
+ public sealed class DefaultSourceEditorOptions : ITextEditorOptions
+ {
+ static DefaultSourceEditorOptions instance;
+ //static TextStylePolicy defaultPolicy;
+ static bool inited;
+
+ public static DefaultSourceEditorOptions Instance {
+ get { return instance; }
+ }
+
+ public static ITextEditorOptions PlainEditor {
+ get;
+ private set;
+ }
+
+ static DefaultSourceEditorOptions ()
+ {
+ Init ();
+ }
+
+ public static void Init ()
+ {
+ if (inited)
+ return;
+ inited = true;
+
+ var policy = MonoDevelop.Projects.Policies.PolicyService.GetDefaultPolicy<TextStylePolicy> ("text/plain");
+ instance = new DefaultSourceEditorOptions (policy);
+ MonoDevelop.Projects.Policies.PolicyService.DefaultPolicies.PolicyChanged += instance.HandlePolicyChanged;
+
+ PlainEditor = new PlainEditorOptions ();
+ }
+
+ class PlainEditorOptions : ITextEditorOptions
+ {
+ #region IDisposable implementation
+
+ void IDisposable.Dispose ()
+ {
+ // nothing
+ }
+
+ #endregion
+
+ #region ITextEditorOptions implementation
+
+ WordFindStrategy ITextEditorOptions.WordFindStrategy {
+ get {
+ return DefaultSourceEditorOptions.Instance.WordFindStrategy;
+ }
+ }
+
+ bool ITextEditorOptions.TabsToSpaces {
+ get {
+ return DefaultSourceEditorOptions.Instance.TabsToSpaces;
+ }
+ }
+
+ int ITextEditorOptions.IndentationSize {
+ get {
+ return DefaultSourceEditorOptions.Instance.IndentationSize;
+ }
+ }
+
+ int ITextEditorOptions.TabSize {
+ get {
+ return DefaultSourceEditorOptions.Instance.TabSize;
+ }
+ }
+
+ bool ITextEditorOptions.ShowIconMargin {
+ get {
+ return false;
+ }
+ }
+
+ bool ITextEditorOptions.ShowLineNumberMargin {
+ get {
+ return false;
+ }
+ }
+
+ bool ITextEditorOptions.ShowFoldMargin {
+ get {
+ return false;
+ }
+ }
+
+ bool ITextEditorOptions.HighlightCaretLine {
+ get {
+ return DefaultSourceEditorOptions.Instance.HighlightCaretLine;
+ }
+ }
+
+ int ITextEditorOptions.RulerColumn {
+ get {
+ return DefaultSourceEditorOptions.Instance.RulerColumn;
+ }
+ }
+
+ bool ITextEditorOptions.ShowRuler {
+ get {
+ return false;
+ }
+ }
+
+ IndentStyle ITextEditorOptions.IndentStyle {
+ get {
+ return DefaultSourceEditorOptions.Instance.IndentStyle;
+ }
+ }
+
+ bool ITextEditorOptions.OverrideDocumentEolMarker {
+ get {
+ return false;
+ }
+ }
+
+ bool ITextEditorOptions.EnableSyntaxHighlighting {
+ get {
+ return DefaultSourceEditorOptions.Instance.EnableSyntaxHighlighting;
+ }
+ }
+
+ bool ITextEditorOptions.RemoveTrailingWhitespaces {
+ get {
+ return DefaultSourceEditorOptions.Instance.RemoveTrailingWhitespaces;
+ }
+ }
+
+ bool ITextEditorOptions.WrapLines {
+ get {
+ return DefaultSourceEditorOptions.Instance.WrapLines;
+ }
+ }
+
+ string ITextEditorOptions.FontName {
+ get {
+ return DefaultSourceEditorOptions.Instance.FontName;
+ }
+ }
+
+ string ITextEditorOptions.GutterFontName {
+ get {
+ return DefaultSourceEditorOptions.Instance.GutterFontName;
+ }
+ }
+
+ string ITextEditorOptions.ColorScheme {
+ get {
+ return DefaultSourceEditorOptions.Instance.ColorScheme;
+ }
+ }
+
+ string ITextEditorOptions.DefaultEolMarker {
+ get {
+ return DefaultSourceEditorOptions.Instance.DefaultEolMarker;
+ }
+ }
+
+ bool ITextEditorOptions.GenerateFormattingUndoStep {
+ get {
+ return DefaultSourceEditorOptions.Instance.GenerateFormattingUndoStep;
+ }
+ }
+
+ ShowWhitespaces ITextEditorOptions.ShowWhitespaces {
+ get {
+ return ShowWhitespaces.Never;
+ }
+ }
+
+ IncludeWhitespaces ITextEditorOptions.IncludeWhitespaces {
+ get {
+ return DefaultSourceEditorOptions.Instance.IncludeWhitespaces;
+ }
+ }
+
+ #endregion
+
+
+ }
+
+ void HandlePolicyChanged (object sender, MonoDevelop.Projects.Policies.PolicyChangedEventArgs args)
+ {
+ TextStylePolicy pol = MonoDevelop.Projects.Policies.PolicyService.GetDefaultPolicy<TextStylePolicy> ("text/plain");
+ UpdateStylePolicy (pol);
+ }
+
+ DefaultSourceEditorOptions (TextStylePolicy currentPolicy)
+ {
+ var defaultControlMode = (ControlLeftRightMode)Enum.Parse (typeof(ControlLeftRightMode), DesktopService.DefaultControlLeftRightBehavior);
+ controlLeftRightMode = new PropertyWrapper<ControlLeftRightMode> ("ControlLeftRightMode", defaultControlMode);
+
+ WordNavigationStyle defaultWordNavigation = WordNavigationStyle.Unix;
+ if (Platform.IsWindows || controlLeftRightMode.Value == ControlLeftRightMode.SharpDevelop) {
+ defaultWordNavigation = WordNavigationStyle.Windows;
+ }
+ wordNavigationStyle = new PropertyWrapper<WordNavigationStyle> ("WordNavigationStyle", defaultWordNavigation);
+
+ UpdateStylePolicy (currentPolicy);
+ FontService.RegisterFontChangedCallback ("Editor", UpdateFont);
+ FontService.RegisterFontChangedCallback ("MessageBubbles", UpdateFont);
+ }
+
+ void UpdateFont ()
+ {
+ this.OnChanged (EventArgs.Empty);
+ }
+
+ void UpdateStylePolicy (MonoDevelop.Ide.Gui.Content.TextStylePolicy currentPolicy)
+ {
+ defaultEolMarker = TextStylePolicy.GetEolMarker (currentPolicy.EolMarker);
+ tabsToSpaces = currentPolicy.TabsToSpaces; // PropertyService.Get ("TabsToSpaces", false);
+ indentationSize = currentPolicy.TabWidth; //PropertyService.Get ("TabIndent", 4);
+ rulerColumn = currentPolicy.FileWidth; //PropertyService.Get ("RulerColumn", 80);
+ allowTabsAfterNonTabs = !currentPolicy.NoTabsAfterNonTabs; //PropertyService.Get ("AllowTabsAfterNonTabs", true);
+ removeTrailingWhitespaces = currentPolicy.RemoveTrailingWhitespace; //PropertyService.Get ("RemoveTrailingWhitespaces", true);
+ }
+
+ public ITextEditorOptions WithTextStyle (MonoDevelop.Ide.Gui.Content.TextStylePolicy policy)
+ {
+ if (policy == null)
+ throw new ArgumentNullException ("policy");
+ var result = (DefaultSourceEditorOptions)MemberwiseClone ();
+ result.UpdateStylePolicy (policy);
+ result.Changed = null;
+ return result;
+ }
+
+ #region new options
+
+ public bool EnableAutoCodeCompletion {
+ get { return CompletionTextEditorExtension.EnableAutoCodeCompletion; }
+ set { CompletionTextEditorExtension.EnableAutoCodeCompletion.Set (value); }
+ }
+
+ PropertyWrapper<bool> defaultRegionsFolding = new PropertyWrapper<bool> ("DefaultRegionsFolding", false);
+ public bool DefaultRegionsFolding {
+ get {
+ return defaultRegionsFolding;
+ }
+ set {
+ if (defaultRegionsFolding.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+
+ PropertyWrapper<bool> defaultCommentFolding = new PropertyWrapper<bool> ("DefaultCommentFolding", true);
+ public bool DefaultCommentFolding {
+ get {
+ return defaultCommentFolding;
+ }
+ set {
+ if (defaultCommentFolding.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+
+ PropertyWrapper<bool> enableSemanticHighlighting = new PropertyWrapper<bool> ("EnableSemanticHighlighting", true);
+ public bool EnableSemanticHighlighting {
+ get {
+ return enableSemanticHighlighting;
+ }
+ set {
+ if (enableSemanticHighlighting.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+
+ PropertyWrapper<bool> tabIsReindent = new PropertyWrapper<bool> ("TabIsReindent", false);
+ public bool TabIsReindent {
+ get {
+ return tabIsReindent;
+ }
+ set {
+ if (tabIsReindent.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+
+ PropertyWrapper<bool> autoInsertMatchingBracket = new PropertyWrapper<bool> ("AutoInsertMatchingBracket", false);
+ public bool AutoInsertMatchingBracket {
+ get {
+ return autoInsertMatchingBracket;
+ }
+ set {
+ if (autoInsertMatchingBracket.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+
+ PropertyWrapper<bool> smartSemicolonPlacement = new PropertyWrapper<bool> ("SmartSemicolonPlacement", false);
+ public bool SmartSemicolonPlacement {
+ get {
+ return smartSemicolonPlacement;
+ }
+ set {
+ if (smartSemicolonPlacement.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+
+ PropertyWrapper<bool> underlineErrors = new PropertyWrapper<bool> ("UnderlineErrors", true);
+ public bool UnderlineErrors {
+ get {
+ return underlineErrors;
+ }
+ set {
+ if (underlineErrors.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+
+ PropertyWrapper<IndentStyle> indentStyle = new PropertyWrapper<IndentStyle> ("IndentStyle", IndentStyle.Smart);
+ public IndentStyle IndentStyle {
+ get {
+ return indentStyle;
+ }
+ set {
+ if (indentStyle.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+
+ PropertyWrapper<bool> enableHighlightUsages = new PropertyWrapper<bool> ("EnableHighlightUsages", false);
+ public bool EnableHighlightUsages {
+ get {
+ return enableHighlightUsages;
+ }
+ set {
+ if (enableHighlightUsages.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+
+ PropertyWrapper<LineEndingConversion> lineEndingConversion = new PropertyWrapper<LineEndingConversion> ("LineEndingConversion", LineEndingConversion.Ask);
+ public LineEndingConversion LineEndingConversion {
+ get {
+ return lineEndingConversion;
+ }
+ set {
+ if (lineEndingConversion.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+
+ #endregion
+
+ PropertyWrapper<bool> useViModes = new PropertyWrapper<bool> ("UseViModes", true);
+ public bool UseViModes {
+ get {
+ return useViModes;
+ }
+ set {
+ if (useViModes.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+
+ PropertyWrapper<bool> onTheFlyFormatting = new PropertyWrapper<bool> ("OnTheFlyFormatting", true);
+ public bool OnTheFlyFormatting {
+ get {
+ return onTheFlyFormatting;
+ }
+ set {
+ if (onTheFlyFormatting.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+
+ #region ITextEditorOptions
+ string defaultEolMarker = Environment.NewLine;
+ public string DefaultEolMarker {
+ get {
+ return defaultEolMarker;
+ }
+ set {
+ if (defaultEolMarker != value) {
+ defaultEolMarker = value;
+ OnChanged (EventArgs.Empty);
+ }
+ }
+ }
+
+ PropertyWrapper<ControlLeftRightMode> controlLeftRightMode;
+ [Obsolete("Use WordNavigationStyle")]
+ public ControlLeftRightMode ControlLeftRightMode {
+ get {
+ return controlLeftRightMode;
+ }
+ set {
+ if (controlLeftRightMode.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+
+ PropertyWrapper<WordNavigationStyle> wordNavigationStyle;
+ public WordNavigationStyle WordNavigationStyle {
+ get {
+ return wordNavigationStyle;
+ }
+ set {
+ if (wordNavigationStyle.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+
+ public WordFindStrategy WordFindStrategy {
+ get {
+ if (useViModes) {
+ return WordFindStrategy.Vim;
+ }
+ switch (WordNavigationStyle) {
+ case WordNavigationStyle.Windows:
+ return WordFindStrategy.SharpDevelop;
+ default:
+ return WordFindStrategy.Emacs;
+ }
+ }
+ set {
+ throw new System.NotImplementedException ();
+ }
+ }
+
+ bool allowTabsAfterNonTabs = true;
+ public bool AllowTabsAfterNonTabs {
+ get {
+ return allowTabsAfterNonTabs;
+ }
+ set {
+ if (allowTabsAfterNonTabs != value) {
+ PropertyService.Set ("AllowTabsAfterNonTabs", value);
+ allowTabsAfterNonTabs = value;
+ OnChanged (EventArgs.Empty);
+ }
+ }
+ }
+
+ bool tabsToSpaces = false;
+ public bool TabsToSpaces {
+ get {
+ return tabsToSpaces;
+ }
+ set {
+ if (tabsToSpaces != value) {
+ PropertyService.Set ("TabsToSpaces", value);
+ tabsToSpaces = value;
+ OnChanged (EventArgs.Empty);
+ }
+ }
+ }
+
+ int indentationSize = 4;
+ public int IndentationSize {
+ get {
+ return indentationSize;
+ }
+ set {
+ if (indentationSize != value) {
+ PropertyService.Set ("TabIndent", value);
+ indentationSize = value;
+ OnChanged (EventArgs.Empty);
+ }
+ }
+ }
+
+
+ public string IndentationString {
+ get {
+ return TabsToSpaces ? new string (' ', this.TabSize) : "\t";
+ }
+ }
+
+ public int TabSize {
+ get {
+ return IndentationSize;
+ }
+ set {
+ IndentationSize = value;
+ }
+ }
+
+
+ bool removeTrailingWhitespaces = true;
+ public bool RemoveTrailingWhitespaces {
+ get {
+ return removeTrailingWhitespaces;
+ }
+ set {
+ if (removeTrailingWhitespaces != value) {
+ PropertyService.Set ("RemoveTrailingWhitespaces", value);
+ OnChanged (EventArgs.Empty);
+ removeTrailingWhitespaces = value;
+ }
+ }
+ }
+
+ PropertyWrapper<bool> showLineNumberMargin = new PropertyWrapper<bool> ("ShowLineNumberMargin", true);
+ public bool ShowLineNumberMargin {
+ get {
+ return showLineNumberMargin;
+ }
+ set {
+ if (showLineNumberMargin.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+
+ PropertyWrapper<bool> showFoldMargin = new PropertyWrapper<bool> ("ShowFoldMargin", false);
+ public bool ShowFoldMargin {
+ get {
+ return showFoldMargin;
+ }
+ set {
+ if (showFoldMargin.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+
+ bool showIconMargin = true;
+ public bool ShowIconMargin {
+ get {
+ return showIconMargin;
+ }
+ set {
+ if (showIconMargin != value) {
+ PropertyService.Set ("ShowIconMargin", value);
+ showIconMargin = value;
+ OnChanged (EventArgs.Empty);
+ }
+ }
+ }
+
+ PropertyWrapper<bool> highlightCaretLine = new PropertyWrapper<bool> ("HighlightCaretLine", false);
+ public bool HighlightCaretLine {
+ get {
+ return highlightCaretLine;
+ }
+ set {
+ if (highlightCaretLine.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+
+ PropertyWrapper<bool> enableSyntaxHighlighting = new PropertyWrapper<bool> ("EnableSyntaxHighlighting", true);
+ public bool EnableSyntaxHighlighting {
+ get {
+ return enableSyntaxHighlighting;
+ }
+ set {
+ if (enableSyntaxHighlighting.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+
+ PropertyWrapper<bool> highlightMatchingBracket = new PropertyWrapper<bool> ("HighlightMatchingBracket", true);
+ public bool HighlightMatchingBracket {
+ get {
+ return highlightMatchingBracket;
+ }
+ set {
+ if (highlightMatchingBracket.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+
+ int rulerColumn = 80;
+
+ public int RulerColumn {
+ get {
+ return rulerColumn;
+ }
+ set {
+ if (rulerColumn != value) {
+ PropertyService.Set ("RulerColumn", value);
+ rulerColumn = value;
+ OnChanged (EventArgs.Empty);
+ }
+ }
+ }
+
+ PropertyWrapper<bool> showRuler = new PropertyWrapper<bool> ("ShowRuler", true);
+ public bool ShowRuler {
+ get {
+ return showRuler;
+ }
+ set {
+ if (showRuler.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+
+ PropertyWrapper<bool> enableAnimations = new PropertyWrapper<bool> ("EnableAnimations", true);
+ public bool EnableAnimations {
+ get {
+ return enableAnimations;
+ }
+ set {
+ if (enableAnimations.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+
+ PropertyWrapper<bool> drawIndentationMarkers = new PropertyWrapper<bool> ("DrawIndentationMarkers", false);
+ public bool DrawIndentationMarkers {
+ get {
+ return drawIndentationMarkers;
+ }
+ set {
+ if (drawIndentationMarkers.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+
+ PropertyWrapper<bool> wrapLines = new PropertyWrapper<bool> ("WrapLines", false);
+ public bool WrapLines {
+ get {
+ return wrapLines;
+ }
+ set {
+ if (wrapLines.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+
+ PropertyWrapper<bool> enableQuickDiff = new PropertyWrapper<bool> ("EnableQuickDiff", false);
+ public bool EnableQuickDiff {
+ get {
+ return enableQuickDiff;
+ }
+ set {
+ if (enableQuickDiff.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+
+ public string FontName {
+ get {
+ return FontService.FilterFontName (FontService.GetUnderlyingFontName ("Editor"));
+ }
+ set {
+ throw new InvalidOperationException ("Set font through font service");
+ }
+ }
+
+ public string GutterFontName {
+ get {
+ return FontService.FilterFontName (FontService.GetUnderlyingFontName ("Editor"));
+ }
+ set {
+ throw new InvalidOperationException ("Set font through font service");
+ }
+ }
+
+ PropertyWrapper<string> colorScheme = new PropertyWrapper<string> ("ColorScheme", "Default");
+ public string ColorScheme {
+ get {
+ return colorScheme;
+ }
+ set {
+ if (colorScheme.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+
+ PropertyWrapper<bool> generateFormattingUndoStep = new PropertyWrapper<bool> ("GenerateFormattingUndoStep", false);
+ public bool GenerateFormattingUndoStep {
+ get {
+ return generateFormattingUndoStep;
+ }
+ set {
+ if (generateFormattingUndoStep.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+
+ bool overrideDocumentEolMarker = false;
+ public bool OverrideDocumentEolMarker {
+ get {
+ return overrideDocumentEolMarker;
+ }
+ set {
+ if (overrideDocumentEolMarker != value) {
+ overrideDocumentEolMarker = value;
+ OnChanged (EventArgs.Empty);
+ }
+ }
+ }
+
+ PropertyWrapper<ShowWhitespaces> showWhitespaces = new PropertyWrapper<ShowWhitespaces> ("ShowWhitespaces", ShowWhitespaces.Never);
+ public ShowWhitespaces ShowWhitespaces {
+ get {
+ return showWhitespaces;
+ }
+ set {
+ if (showWhitespaces.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+
+ PropertyWrapper<IncludeWhitespaces> includeWhitespaces = new PropertyWrapper<IncludeWhitespaces> ("IncludeWhitespaces", IncludeWhitespaces.All);
+ public IncludeWhitespaces IncludeWhitespaces {
+ get {
+ return includeWhitespaces;
+ }
+ set {
+ if (includeWhitespaces.Set (value))
+ OnChanged (EventArgs.Empty);
+ }
+ }
+ #endregion
+
+ public void Dispose ()
+ {
+ FontService.RemoveCallback (UpdateFont);
+ }
+
+ protected void OnChanged (EventArgs args)
+ {
+ if (Changed != null)
+ Changed (null, args);
+ }
+
+ public event EventHandler Changed;
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DocumentContext.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DocumentContext.cs
new file mode 100644
index 0000000000..58cac26e26
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DocumentContext.cs
@@ -0,0 +1,158 @@
+//
+// DocumentContext.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 MonoDevelop.Projects;
+using ICSharpCode.NRefactory.TypeSystem;
+using MonoDevelop.Ide.TypeSystem;
+using System.Threading.Tasks;
+using System.Threading;
+using Microsoft.CodeAnalysis.Options;
+using System.Collections.Generic;
+using MonoDevelop.Ide.Editor.Projection;
+
+namespace MonoDevelop.Ide.Editor
+{
+ /// <summary>
+ /// A document context puts a textual document in a semantic context inside a project and gives access
+ /// to the parse information of the textual document.
+ /// </summary>
+ public abstract class DocumentContext
+ {
+ /// <summary>
+ /// The name of the document. It's the file name for files on disc.
+ /// For unsaved files that name is different.
+ /// </summary>
+ public abstract string Name
+ {
+ get;
+ }
+
+ /// <summary>
+ /// Project != null
+ /// </summary>
+ public virtual bool HasProject
+ {
+ get { return Project != null; }
+ }
+
+ /// <summary>
+ /// Gets the project this context is in.
+ /// </summary>
+ public abstract Project Project
+ {
+ get;
+ }
+
+ public Microsoft.CodeAnalysis.Workspace RoslynWorkspace
+ {
+ get;
+ protected set;
+ }
+
+ /// <summary>
+ /// Returns the roslyn document for this document. This may return <c>null</c> if it's no compileable document.
+ /// Even if it's a C# file.
+ /// </summary>
+ public abstract Microsoft.CodeAnalysis.Document AnalysisDocument
+ {
+ get;
+ }
+
+ /// <summary>
+ /// The parsed document. Contains all syntax information about the text.
+ /// </summary>
+ public abstract ParsedDocument ParsedDocument
+ {
+ get;
+ }
+
+ /// <summary>
+ /// If true, the document is part of the ProjectContent.
+ /// </summary>
+ public virtual bool IsCompileableInProject
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ public virtual T GetContent<T>() where T : class
+ {
+ var t = this as T;
+ if (t != null)
+ return t;
+ return null;
+ }
+
+ public virtual IEnumerable<T> GetContents<T>() where T : class
+ {
+ var t = this as T;
+ if (t != null)
+ yield return t;
+ }
+
+ /// <summary>
+ /// This is called after the ParsedDocument updated.
+ /// </summary>
+ public event EventHandler DocumentParsed;
+
+ protected void OnDocumentParsed (EventArgs e)
+ {
+ var handler = DocumentParsed;
+ if (handler != null)
+ handler (this, e);
+ }
+
+ public abstract void AttachToProject (Project project);
+
+ /// <summary>
+ /// Forces a reparse of the document. This call doesn't block the ui thread.
+ /// The next call to ParsedDocument will give always the current parsed document but may block the UI thread.
+ /// </summary>
+ public abstract void ReparseDocument ();
+
+ public abstract OptionSet GetOptionSet ();
+
+ public abstract ParsedDocument UpdateParseDocument ();
+
+ // TODO: IMO that needs to be handled differently (this is atm only used in the ASP.NET binding)
+ // Maybe using the file service. Files can be changed/saved w/o beeing opened.
+ public event EventHandler Saved;
+
+ protected virtual void OnSaved (EventArgs e)
+ {
+ var handler = Saved;
+ if (handler != null)
+ handler (this, e);
+ }
+
+ internal virtual Task<IReadOnlyList<Editor.Projection.Projection>> GetPartialProjectionsAsync (CancellationToken cancellationToken = default(CancellationToken))
+ {
+ return null;
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DocumentLocation.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DocumentLocation.cs
new file mode 100644
index 0000000000..7e95e9df8e
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DocumentLocation.cs
@@ -0,0 +1,241 @@
+//
+// DocumentLocation.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.ComponentModel;
+using System.Globalization;
+
+namespace MonoDevelop.Ide.Editor
+{
+ /// <summary>
+ /// A line/column position.
+ /// Text editor lines/columns are counted started from one.
+ /// </summary>
+ /// <remarks>
+ /// The document provides the methods <see cref="Editor.IDocument.GetLocation"/> and
+ /// <see cref="Editor.IDocument.GetOffset(TextLocation)"/> to convert between offsets and TextLocations.
+ /// </remarks>
+ [Serializable]
+ [TypeConverter(typeof(DocumentLocationConverter))]
+ public struct DocumentLocation : IComparable<DocumentLocation>, IEquatable<DocumentLocation>
+ {
+ /// <summary>
+ /// Represents no text location (0, 0).
+ /// </summary>
+ public static readonly DocumentLocation Empty = new DocumentLocation(0, 0);
+
+ /// <summary>
+ /// Constant of the minimum line.
+ /// </summary>
+ public const int MinLine = 1;
+
+ /// <summary>
+ /// Constant of the minimum column.
+ /// </summary>
+ public const int MinColumn = 1;
+
+ /// <summary>
+ /// Creates a TextLocation instance.
+ /// </summary>
+ public DocumentLocation(int line, int column)
+ {
+ this.line = line;
+ this.column = column;
+ }
+
+ int column, line;
+
+ /// <summary>
+ /// Gets the line number.
+ /// </summary>
+ public int Line {
+ get { return line; }
+ }
+
+ /// <summary>
+ /// Gets the column number.
+ /// </summary>
+ public int Column {
+ get { return column; }
+ }
+
+ /// <summary>
+ /// Gets whether the TextLocation instance is empty.
+ /// </summary>
+ public bool IsEmpty {
+ get {
+ return column < MinLine && line < MinColumn;
+ }
+ }
+
+ /// <summary>
+ /// Gets a string representation for debugging purposes.
+ /// </summary>
+ public override string ToString()
+ {
+ return string.Format(CultureInfo.InvariantCulture, "(Line {1}, Col {0})", this.column, this.line);
+ }
+
+ /// <summary>
+ /// Gets a hash code.
+ /// </summary>
+ public override int GetHashCode()
+ {
+ return unchecked (column << 20 ^ line);
+ }
+
+ /// <summary>
+ /// Equality test.
+ /// </summary>
+ public override bool Equals(object obj)
+ {
+ if (!(obj is DocumentLocation)) return false;
+ return (DocumentLocation)obj == this;
+ }
+
+ /// <summary>
+ /// Equality test.
+ /// </summary>
+ public bool Equals(DocumentLocation other)
+ {
+ return this == other;
+ }
+
+ /// <summary>
+ /// Equality test.
+ /// </summary>
+ public static bool operator ==(DocumentLocation left, DocumentLocation right)
+ {
+ return left.column == right.column && left.line == right.line;
+ }
+
+ /// <summary>
+ /// Inequality test.
+ /// </summary>
+ public static bool operator !=(DocumentLocation left, DocumentLocation right)
+ {
+ return left.column != right.column || left.line != right.line;
+ }
+
+ /// <summary>
+ /// Compares two text locations.
+ /// </summary>
+ public static bool operator <(DocumentLocation left, DocumentLocation right)
+ {
+ if (left.line < right.line)
+ return true;
+ else if (left.line == right.line)
+ return left.column < right.column;
+ else
+ return false;
+ }
+
+ /// <summary>
+ /// Compares two text locations.
+ /// </summary>
+ public static bool operator >(DocumentLocation left, DocumentLocation right)
+ {
+ if (left.line > right.line)
+ return true;
+ else if (left.line == right.line)
+ return left.column > right.column;
+ else
+ return false;
+ }
+
+ /// <summary>
+ /// Compares two text locations.
+ /// </summary>
+ public static bool operator <=(DocumentLocation left, DocumentLocation right)
+ {
+ return !(left > right);
+ }
+
+ /// <summary>
+ /// Compares two text locations.
+ /// </summary>
+ public static bool operator >=(DocumentLocation left, DocumentLocation right)
+ {
+ return !(left < right);
+ }
+
+ public static implicit operator Microsoft.CodeAnalysis.Text.LinePosition (DocumentLocation location)
+ {
+ return new Microsoft.CodeAnalysis.Text.LinePosition (location.Line - 1, location.Column - 1);
+ }
+
+ public static implicit operator DocumentLocation(Microsoft.CodeAnalysis.Text.LinePosition location)
+ {
+ return new DocumentLocation (location.Line + 1, location.Character + 1);
+ }
+
+ /// <summary>
+ /// Compares two text locations.
+ /// </summary>
+ public int CompareTo(DocumentLocation other)
+ {
+ if (this == other)
+ return 0;
+ if (this < other)
+ return -1;
+ else
+ return 1;
+ }
+ }
+
+ public class DocumentLocationConverter : TypeConverter
+ {
+ public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
+ {
+ return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
+ }
+
+ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
+ {
+ return destinationType == typeof(DocumentLocation) || base.CanConvertTo(context, destinationType);
+ }
+
+ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
+ {
+ if (value is string) {
+ string[] parts = ((string)value).Split(';', ',');
+ if (parts.Length == 2) {
+ return new DocumentLocation(int.Parse(parts[0]), int.Parse(parts[1]));
+ }
+ }
+ return base.ConvertFrom(context, culture, value);
+ }
+
+ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
+ {
+ if (value is DocumentLocation) {
+ var loc = (DocumentLocation)value;
+ return loc.Line + ";" + loc.Column;
+ }
+ return base.ConvertTo(context, culture, value, destinationType);
+ }
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DocumentRegion.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DocumentRegion.cs
new file mode 100644
index 0000000000..ff5863936b
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DocumentRegion.cs
@@ -0,0 +1,181 @@
+//
+// DocumentRegion.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2012 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 MonoDevelop.Core.Text;
+using Microsoft.CodeAnalysis;
+
+namespace MonoDevelop.Ide.Editor
+{
+ /// <summary>
+ /// An (Begin, End) pair representing a document span. It's a TextSegment working with lines &amp; columns instead of offsets.
+ /// </summary>
+ public struct DocumentRegion : IEquatable<DocumentRegion>
+ {
+ public static readonly DocumentRegion Empty = new DocumentRegion (0, 0, 0, 0);
+
+ /// <summary>
+ /// Gets a value indicating whether this DocumentRegion is empty.
+ /// </summary>
+ public bool IsEmpty {
+ get {
+ return beginLine < 1;
+ }
+ }
+
+ readonly int beginLine;
+ public int BeginLine {
+ get {
+ return beginLine;
+ }
+ }
+
+ readonly int beginColumn;
+ public int BeginColumn {
+ get {
+ return beginColumn;
+ }
+ }
+
+ readonly int endLine;
+ public int EndLine {
+ get {
+ return endLine;
+ }
+ }
+
+ readonly int endColumn;
+ public int EndColumn {
+ get {
+ return endColumn;
+ }
+ }
+
+ public DocumentLocation Begin {
+ get {
+ return new DocumentLocation (BeginLine, BeginColumn);
+ }
+ }
+
+ public DocumentLocation End {
+ get {
+ return new DocumentLocation (EndLine, EndColumn);
+ }
+ }
+
+ public DocumentRegion (int beginLine, int beginColumn, int endLine, int endColumn)
+ {
+ this.beginLine = beginLine;
+ this.beginColumn = beginColumn;
+ this.endLine = endLine;
+ this.endColumn = endColumn;
+ }
+
+ public DocumentRegion (DocumentLocation begin, DocumentLocation end)
+ {
+ beginLine = begin.Line;
+ beginColumn = begin.Column;
+ endLine = end.Line;
+ endColumn = end.Column;
+ }
+
+ public bool Contains (DocumentLocation location)
+ {
+ return Begin <= location && location < End;
+ }
+
+ public bool Contains (int line, int column)
+ {
+ return Contains (new DocumentLocation (line, column));
+ }
+
+ public bool IsInside (DocumentLocation location)
+ {
+ return Begin <= location && location <= End;
+ }
+
+ public bool IsInside (int line, int column)
+ {
+ return IsInside (new DocumentLocation (line, column));
+ }
+
+ public override bool Equals (object obj)
+ {
+ return obj is DocumentRegion && Equals ((DocumentRegion)obj);
+ }
+
+ public override int GetHashCode ()
+ {
+ return unchecked (Begin.GetHashCode () ^ End.GetHashCode ());
+ }
+
+ public bool Equals (DocumentRegion other)
+ {
+ return Begin == other.Begin && End == other.End;
+ }
+
+ public static bool operator == (DocumentRegion left, DocumentRegion right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator != (DocumentRegion left, DocumentRegion right)
+ {
+ return !left.Equals(right);
+ }
+
+ public TextSegment GetSegment (TextEditor document)
+ {
+ if (document == null)
+ throw new ArgumentNullException ("document");
+ var begin = document.LocationToOffset (Begin);
+ var end = document.LocationToOffset (End);
+ return new TextSegment (begin, end - begin);
+ }
+
+ public static implicit operator Microsoft.CodeAnalysis.Text.LinePositionSpan (DocumentRegion location)
+ {
+ return new Microsoft.CodeAnalysis.Text.LinePositionSpan (location.Begin, location.End);
+ }
+
+ public static implicit operator DocumentRegion(Microsoft.CodeAnalysis.Text.LinePositionSpan location)
+ {
+ return new DocumentRegion (location.Start, location.End);
+ }
+
+
+ public static implicit operator DocumentRegion(FileLinePositionSpan location)
+ {
+ return new DocumentRegion (location.StartLinePosition, location.EndLinePosition);
+ }
+
+
+ public override string ToString ()
+ {
+ return string.Format ("[DocumentRegion: Begin={0}, End={1}]", Begin, End);
+ }
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/EditActions.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/EditActions.cs
new file mode 100644
index 0000000000..a72688c0e2
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/EditActions.cs
@@ -0,0 +1,516 @@
+//
+// EditActions.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.Text;
+using MonoDevelop.Core.Text;
+using MonoDevelop.Ide.Editor.Util;
+
+namespace MonoDevelop.Ide.Editor
+{
+ /// <summary>
+ /// This class contains some common actions for the text editor.
+ /// </summary>
+ public static class EditActions
+ {
+ public static void MoveCaretDown (TextEditor editor)
+ {
+ editor.EditorActionHost.MoveCaretDown ();
+ }
+
+ public static void MoveCaretUp (TextEditor editor)
+ {
+ editor.EditorActionHost.MoveCaretUp ();
+ }
+
+ public static void MoveCaretRight (TextEditor editor)
+ {
+ editor.EditorActionHost.MoveCaretRight ();
+ }
+
+ public static void MoveCaretLeft (TextEditor editor)
+ {
+ editor.EditorActionHost.MoveCaretLeft ();
+ }
+
+ public static void MoveCaretToLineEnd (TextEditor editor)
+ {
+ editor.EditorActionHost.MoveCaretToLineEnd ();
+ }
+
+ public static void MoveCaretToLineStart (TextEditor editor)
+ {
+ editor.EditorActionHost.MoveCaretToLineStart ();
+ }
+
+ public static void MoveCaretToDocumentStart (TextEditor editor)
+ {
+ editor.EditorActionHost.MoveCaretToDocumentStart ();
+ }
+
+ public static void MoveCaretToDocumentEnd (TextEditor editor)
+ {
+ editor.EditorActionHost.MoveCaretToDocumentEnd ();
+ }
+
+ public static void Backspace (TextEditor editor)
+ {
+ editor.EditorActionHost.Backspace ();
+ }
+
+ public static void Delete (TextEditor editor)
+ {
+ editor.EditorActionHost.Delete ();
+ }
+
+ public static void ClipboardCopy (TextEditor editor)
+ {
+ editor.EditorActionHost.ClipboardCopy ();
+ }
+
+ public static void ClipboardCut (TextEditor editor)
+ {
+ editor.EditorActionHost.ClipboardCut ();
+ }
+
+ public static void ClipboardPaste (TextEditor editor)
+ {
+ editor.EditorActionHost.ClipboardPaste ();
+ }
+
+
+
+ public static void SelectAll (TextEditor editor)
+ {
+ editor.EditorActionHost.SelectAll ();
+ }
+
+ public static void NewLine (TextEditor editor)
+ {
+ editor.EditorActionHost.NewLine ();
+ }
+
+ public static void PageUp (TextEditor textEditor)
+ {
+ textEditor.EditorActionHost.PageUp ();
+ }
+
+ public static void PageDown (TextEditor textEditor)
+ {
+ textEditor.EditorActionHost.PageDown ();
+ }
+
+ public static void Undo (TextEditor editor)
+ {
+ editor.EditorActionHost.Undo ();
+ }
+
+ public static void Redo (TextEditor editor)
+ {
+ editor.EditorActionHost.Redo ();
+ }
+
+ public static void DeleteCurrentLine (TextEditor textEditor)
+ {
+ textEditor.EditorActionHost.DeleteCurrentLine ();
+ }
+
+ public static void DeleteCurrentLineToEnd (TextEditor textEditor)
+ {
+ textEditor.EditorActionHost.DeleteCurrentLineToEnd ();
+ }
+
+ public static void ScrollLineUp (TextEditor textEditor)
+ {
+ textEditor.EditorActionHost.ScrollLineUp ();
+ }
+
+ public static void ScrollLineDown (TextEditor textEditor)
+ {
+ textEditor.EditorActionHost.ScrollLineDown ();
+ }
+
+ public static void ScrollPageUp (TextEditor textEditor)
+ {
+ textEditor.EditorActionHost.ScrollPageUp ();
+ }
+
+ public static void ScrollPageDown (TextEditor textEditor)
+ {
+ textEditor.EditorActionHost.ScrollPageDown ();
+ }
+
+ public static void GotoMatchingBrace (TextEditor textEditor)
+ {
+ var offset = SimpleBracketMatcher.GetMatchingBracketOffset (textEditor, textEditor.CaretOffset);
+ if (offset > 0)
+ textEditor.CaretOffset = offset;
+ }
+
+ public static void MovePrevWord (TextEditor textEditor)
+ {
+ textEditor.EditorActionHost.MovePrevWord ();
+ }
+
+ public static void MoveNextWord (TextEditor textEditor)
+ {
+ textEditor.EditorActionHost.MoveNextWord ();
+ }
+
+ public static void MovePrevSubWord (TextEditor textEditor)
+ {
+ textEditor.EditorActionHost.MovePrevSubWord ();
+ }
+
+ public static void MoveNextSubWord (TextEditor textEditor)
+ {
+ textEditor.EditorActionHost.MoveNextSubWord ();
+ }
+
+
+ public static void TransposeCharacters (TextEditor textEditor)
+ {
+ // Code from Mono.TextEditor.MiscActions.TransposeCharacters
+ if (textEditor.CaretOffset == 0)
+ return;
+ var line = textEditor.GetLine (textEditor.CaretLine);
+ if (line == null)
+ return;
+ using (var undoGroup = textEditor.OpenUndoGroup ()) {
+ int transposeOffset = textEditor.CaretOffset - 1;
+ char ch;
+ if (textEditor.CaretColumn == 0) {
+ var lineAbove = textEditor.GetLine (textEditor.CaretLine - 1);
+ if (lineAbove.Length == 0 && line.Length == 0)
+ return;
+
+ if (line.Length != 0) {
+ ch = textEditor.GetCharAt (textEditor.CaretOffset);
+ textEditor.RemoveText (textEditor.CaretOffset, 1);
+ textEditor.InsertText (lineAbove.Offset + lineAbove.Length, ch.ToString ());
+ return;
+ }
+
+ int lastCharOffset = lineAbove.Offset + lineAbove.Length - 1;
+ ch = textEditor.GetCharAt (lastCharOffset);
+ textEditor.RemoveText (lastCharOffset, 1);
+ textEditor.InsertAtCaret (ch.ToString ());
+ return;
+ }
+
+ int offset = textEditor.CaretOffset;
+ if (textEditor.CaretColumn >= line.Length + 1) {
+ offset = line.Offset + line.Length - 1;
+ transposeOffset = offset - 1;
+ // case one char in line:
+ if (transposeOffset < line.Offset) {
+ var lineAbove = textEditor.GetLine (textEditor.CaretLine - 1);
+ transposeOffset = lineAbove.Offset + lineAbove.Length;
+ ch = textEditor.GetCharAt (offset);
+ textEditor.RemoveText (offset, 1);
+ textEditor.InsertText (transposeOffset, ch.ToString ());
+ textEditor.CaretOffset = line.Offset;
+ return;
+ }
+ }
+
+ ch = textEditor.GetCharAt (offset);
+ textEditor.ReplaceText (offset, 1, textEditor.GetCharAt (transposeOffset).ToString ());
+ textEditor.ReplaceText (transposeOffset, 1, ch.ToString ());
+ if (textEditor.CaretColumn < line.Length + 1)
+ textEditor.CaretOffset = offset + 1;
+ }
+ }
+
+ public static void DuplicateCurrentLine (TextEditor textEditor)
+ {
+ // Code from Mono.TextEditor.MiscActions.DuplicateLine
+ using (var undoGroup = textEditor.OpenUndoGroup ()) {
+ if (textEditor.IsSomethingSelected) {
+ var selectedText = textEditor.SelectedText;
+ textEditor.ClearSelection ();
+ textEditor.InsertAtCaret (selectedText);
+ } else {
+ var line = textEditor.GetLine (textEditor.CaretLine);
+ if (line == null)
+ return;
+ textEditor.InsertText (line.Offset, textEditor.GetTextAt (line.SegmentIncludingDelimiter));
+ }
+ }
+ }
+
+ public static void JoinLines (TextEditor textEditor)
+ {
+ textEditor.EditorActionHost.JoinLines ();
+ }
+
+ public static void RecenterEditor (TextEditor textEditor)
+ {
+ textEditor.EditorActionHost.RecenterEditor ();
+ }
+
+ public static void StartCaretPulseAnimation (TextEditor textEditor)
+ {
+ textEditor.EditorActionHost.StartCaretPulseAnimation ();
+ }
+
+ public static void DeleteNextSubword (TextEditor textEditor)
+ {
+ textEditor.EditorActionHost.DeleteNextSubword ();
+ }
+
+ public static void DeletePreviousSubword (TextEditor textEditor)
+ {
+ textEditor.EditorActionHost.DeletePreviousSubword ();
+ }
+
+ public static void DeleteNextWord (TextEditor textEditor)
+ {
+ textEditor.EditorActionHost.DeleteNextWord ();
+ }
+
+ public static void DeletePreviousWord (TextEditor textEditor)
+ {
+ textEditor.EditorActionHost.DeletePreviousWord ();
+ }
+
+ public static void InsertNewLinePreserveCaretPosition (TextEditor textEditor)
+ {
+ if (textEditor.IsReadOnly)
+ return;
+ using (var undoGroup = textEditor.OpenUndoGroup ()) {
+ var loc = textEditor.CaretLocation;
+ InsertNewLine (textEditor);
+ textEditor.CaretLocation = loc;
+ }
+ }
+
+ public static void InsertNewLineAtEnd (TextEditor textEditor)
+ {
+ if (textEditor.IsReadOnly)
+ return;
+ using (var undoGroup = textEditor.OpenUndoGroup ()) {
+ MoveCaretToLineEnd (textEditor);
+ InsertNewLine (textEditor);
+ }
+ }
+
+ public static void InsertNewLine (TextEditor textEditor)
+ {
+ textEditor.EditorActionHost.InsertNewLine ();
+ }
+
+ public static void RemoveTab (TextEditor textEditor)
+ {
+ textEditor.EditorActionHost.RemoveTab ();
+ }
+
+ public static void InsertTab (TextEditor textEditor)
+ {
+ textEditor.EditorActionHost.InsertTab ();
+ }
+
+ public static void SwitchCaretMode (TextEditor textEditor)
+ {
+ textEditor.EditorActionHost.SwitchCaretMode ();
+ }
+
+ public static void MoveBlockUp (TextEditor textEditor)
+ {
+ textEditor.EditorActionHost.MoveBlockUp ();
+ }
+
+ public static void MoveBlockDown (TextEditor textEditor)
+ {
+ textEditor.EditorActionHost.MoveBlockDown ();
+ }
+
+ public static void ToggleBlockSelectionMode (TextEditor textEditor)
+ {
+ textEditor.EditorActionHost.ToggleBlockSelectionMode ();
+ }
+
+ public static void IndentSelection (TextEditor editor)
+ {
+ editor.EditorActionHost.IndentSelection ();
+ }
+
+ public static void UnIndentSelection (TextEditor editor)
+ {
+ editor.EditorActionHost.UnIndentSelection ();
+ }
+
+ #region SelectionActions
+
+ static void RunSelectionAction (TextEditor textEditor, Action<TextEditor> action)
+ {
+ using (var undo = textEditor.OpenUndoGroup ()) {
+ var anchor = textEditor.IsSomethingSelected ? textEditor.SelectionAnchorOffset : textEditor.CaretOffset;
+ action (textEditor);
+ textEditor.SetSelection (anchor, textEditor.CaretOffset);
+ }
+ }
+
+ public static void SelectionMoveToDocumentEnd (TextEditor textEditor)
+ {
+ RunSelectionAction (textEditor, MoveCaretToDocumentEnd);
+ }
+
+ public static void ExpandSelectionToLine (TextEditor textEditor)
+ {
+ // from Mono.TextEditor.SelectionActions.ExpandSelectionToLine
+ using (var undoGroup = textEditor.OpenUndoGroup ()) {
+ var curLineSegment = textEditor.GetLine (textEditor.CaretLine).SegmentIncludingDelimiter;
+ var range = textEditor.SelectionRange;
+ var selection = TextSegment.FromBounds (
+ System.Math.Min (range.Offset, curLineSegment.Offset),
+ System.Math.Max (range.EndOffset, curLineSegment.EndOffset));
+ textEditor.CaretOffset = selection.EndOffset;
+ textEditor.SelectionRange = selection;
+ }
+ }
+
+ public static void SelectionMoveToDocumentStart (TextEditor textEditor)
+ {
+ RunSelectionAction (textEditor, MoveCaretToDocumentStart);
+ }
+
+ public static void SelectionMoveLineEnd (TextEditor textEditor)
+ {
+ RunSelectionAction (textEditor, MoveCaretToLineEnd);
+ }
+
+ public static void SelectionMoveLineStart (TextEditor textEditor)
+ {
+ RunSelectionAction (textEditor, MoveCaretToLineStart);
+ }
+
+ public static void SelectionMoveDown (TextEditor textEditor)
+ {
+ RunSelectionAction (textEditor, MoveCaretDown);
+ }
+
+ public static void SelectionMoveUp (TextEditor textEditor)
+ {
+ RunSelectionAction (textEditor, MoveCaretUp);
+ }
+
+ public static void SelectionPageUp (TextEditor textEditor)
+ {
+ RunSelectionAction (textEditor, PageUp);
+ }
+
+ public static void SelectionPageDown (TextEditor textEditor)
+ {
+ RunSelectionAction (textEditor, PageDown);
+ }
+
+ public static void SelectionMovePrevSubWord (TextEditor textEditor)
+ {
+ RunSelectionAction (textEditor, MovePrevSubWord);
+ }
+
+ public static void SelectionMoveNextSubWord (TextEditor textEditor)
+ {
+ RunSelectionAction (textEditor, MoveNextSubWord);
+ }
+
+ public static void SelectionMoveLeft (TextEditor textEditor)
+ {
+ RunSelectionAction (textEditor, MoveCaretLeft);
+ }
+
+ public static void SelectionMoveRight (TextEditor textEditor)
+ {
+ RunSelectionAction (textEditor, MoveCaretRight);
+ }
+
+ public static void SelectionMovePrevWord (TextEditor textEditor)
+ {
+ RunSelectionAction (textEditor, MovePrevWord);
+ }
+
+ public static void SelectionMoveNextWord (TextEditor textEditor)
+ {
+ RunSelectionAction (textEditor, MoveNextWord);
+ }
+
+ public static void SortSelectedLines (TextEditor textEditor)
+ {
+ var selectionRegion = textEditor.SelectionRegion;
+ var start = selectionRegion.Begin;
+ var end = selectionRegion.End;
+ var caret = textEditor.CaretLocation;
+
+ 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 = textEditor.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 = textEditor.LocationToOffset (startLine, 1);
+ textEditor.ReplaceText (startOffset, length, sb.ToString ());
+
+ textEditor.CaretLocation = LimitColumn (textEditor, caret);
+ textEditor.SetSelection (LimitColumn (textEditor, start), LimitColumn (textEditor, end));
+ }
+
+ static DocumentLocation LimitColumn (TextEditor data, DocumentLocation loc)
+ {
+ return new DocumentLocation (loc.Line, System.Math.Min (loc.Column, data.GetLine (loc.Line).Length + 1));
+ }
+ #endregion
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/FileSettingsStore.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/FileSettingsStore.cs
new file mode 100644
index 0000000000..bec7ddd1b6
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/FileSettingsStore.cs
@@ -0,0 +1,73 @@
+//
+// FileSettingsStore.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2013 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.Collections.Generic;
+
+namespace MonoDevelop.Ide.Editor
+{
+ public static class FileSettingsStore
+ {
+ public class Settings
+ {
+ public int CaretOffset { get; set; }
+
+ public double vAdjustment { get; set; }
+
+ public double hAdjustment { get; set; }
+
+ public Dictionary<int, bool> FoldingStates = new Dictionary<int, bool> ();
+
+ public override string ToString ()
+ {
+ return string.Format ("[Settings: CaretOffset={0}, vAdjustment={1}, hAdjustment={2}]", CaretOffset, vAdjustment, hAdjustment);
+ }
+ }
+
+ static Dictionary<string, Settings> settingStore = new Dictionary<string, Settings> ();
+
+ public static bool TryGetValue (string contentName, out Settings settings)
+ {
+ if (contentName == null)
+ throw new ArgumentNullException ("contentName");
+ return settingStore.TryGetValue (contentName, out settings);
+ }
+
+ public static void Store (string contentName, Settings settings)
+ {
+ if (contentName == null)
+ throw new ArgumentNullException ("contentName");
+ if (settings == null)
+ throw new ArgumentNullException ("settings");
+ settingStore [contentName] = settings;
+ }
+
+ public static void Remove (string fileName)
+ {
+ settingStore.Remove (fileName);
+ }
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/FoldSegmentFactory.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/FoldSegmentFactory.cs
new file mode 100644
index 0000000000..468defb00f
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/FoldSegmentFactory.cs
@@ -0,0 +1,86 @@
+//
+// FoldSegmentFactory.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 MonoDevelop.Core.Text;
+
+namespace MonoDevelop.Ide.Editor
+{
+ public static class FoldSegmentFactory
+ {
+ public static IFoldSegment CreateFoldSegment (TextEditor editor, int offset, int length, bool isCollapsed = false, string collapsedText = "...", FoldingType foldingType = FoldingType.Unknown)
+ {
+ if (editor == null)
+ throw new ArgumentNullException ("editor");
+ var result = editor.CreateFoldSegment (offset, length, isCollapsed);
+ result.CollapsedText = collapsedText;
+ result.FoldingType = foldingType;
+ return result;
+ }
+
+ public static IFoldSegment CreateFoldSegment (TextEditor editor, ISegment segment, bool isCollapsed = false, string collapsedText = "...", FoldingType foldingType = FoldingType.Unknown)
+ {
+ if (editor == null)
+ throw new ArgumentNullException ("editor");
+ if (segment == null)
+ throw new ArgumentNullException ("segment");
+ return CreateFoldSegment (editor, segment.Offset, segment.Length, isCollapsed, collapsedText, foldingType);
+ }
+
+ public static IFoldSegment CreateFoldSegment (TextEditor editor, int offset, int length, string collapsedText = "...", FoldingType foldingType = FoldingType.Unknown)
+ {
+ if (editor == null)
+ throw new ArgumentNullException ("editor");
+ return CreateFoldSegment (editor, offset, length, false, collapsedText, foldingType);
+ }
+
+ public static IFoldSegment CreateFoldSegment (TextEditor editor, ISegment segment, string collapsedText = "...", FoldingType foldingType = FoldingType.Unknown)
+ {
+ if (editor == null)
+ throw new ArgumentNullException ("editor");
+ if (segment == null)
+ throw new ArgumentNullException ("segment");
+ return CreateFoldSegment (editor, segment.Offset, segment.Length, false, collapsedText, foldingType);
+ }
+
+ public static IFoldSegment CreateFoldSegment (TextEditor editor, int offset, int length, FoldingType foldingType = FoldingType.Unknown)
+ {
+ if (editor == null)
+ throw new ArgumentNullException ("editor");
+ return CreateFoldSegment (editor, offset, length, false, "...", foldingType);
+ }
+
+ public static IFoldSegment CreateFoldSegment (TextEditor editor, ISegment segment, FoldingType foldingType = FoldingType.Unknown)
+ {
+ if (editor == null)
+ throw new ArgumentNullException ("editor");
+ if (segment == null)
+ throw new ArgumentNullException ("segment");
+ return CreateFoldSegment (editor, segment.Offset, segment.Length, false, "...", foldingType);
+ }
+
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/IDocumentLine.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/IDocumentLine.cs
new file mode 100644
index 0000000000..7135d74dad
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/IDocumentLine.cs
@@ -0,0 +1,118 @@
+//
+// IDocumentLine.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 MonoDevelop.Core.Text;
+using System.Text;
+using MonoDevelop.Ide.Editor.Highlighting;
+
+namespace MonoDevelop.Ide.Editor
+{
+ /// <summary>
+ /// A line inside a <see cref="ITextDocument"/>.
+ /// </summary>
+ public interface IDocumentLine : ISegment
+ {
+ /// <summary>
+ /// Gets the length of the line including the line delimiter.
+ /// </summary>
+ int LengthIncludingDelimiter {
+ get;
+ }
+
+ int EndOffsetIncludingDelimiter {
+ get;
+ }
+
+ /// <summary>
+ /// Gets the text segment of the line including the line delimiter.
+ /// </summary>
+ ISegment SegmentIncludingDelimiter {
+ get;
+ }
+
+ /// <summary>
+ /// Gets the unicode newline for this line. Returns UnicodeNewline.Unknown for no new line (in the last line of the document)
+ /// </summary>EndOffsetIncludingDelimiterEndOffsetIncludingDelimiter
+ UnicodeNewline UnicodeNewline {
+ get;
+ }
+
+ /// <summary>
+ /// Gets the length of the line terminator.
+ /// Returns 1 or 2; or 0 at the end of the document.
+ /// </summary>
+ int DelimiterLength { get; }
+
+ /// <summary>
+ /// Gets the number of this line.
+ /// The first line has the number 1.
+ /// </summary>
+ int LineNumber { get; }
+
+ /// <summary>
+ /// Gets the previous line. Returns null if this is the first line in the document.
+ /// </summary>
+ IDocumentLine PreviousLine { get; }
+
+ /// <summary>
+ /// Gets the next line. Returns null if this is the last line in the document.
+ /// </summary>
+ IDocumentLine NextLine { get; }
+
+ /// <summary>
+ /// Gets whether the line was deleted.
+ /// </summary>
+ bool IsDeleted { get; }
+ }
+
+ public static class DocumentLineExt
+ {
+ /// <summary>
+ /// This method gets the line indentation.
+ /// </summary>
+ /// <param name = "line"></param>
+ /// <param name="doc">
+ /// The <see cref="IReadonlyTextDocument"/> the line belongs to.
+ /// </param>
+ /// <returns>
+ /// The indentation of the line (all whitespace chars up to the first non ws char).
+ /// </returns>
+ public static string GetIndentation (this IDocumentLine line, IReadonlyTextDocument doc)
+ {
+ var result = new StringBuilder ();
+ int offset = line.Offset;
+ int max = Math.Min (offset + line.LengthIncludingDelimiter, doc.Length);
+ for (int i = offset; i < max; i++) {
+ char ch = doc.GetCharAt (i);
+ if (ch != ' ' && ch != '\t')
+ break;
+ result.Append (ch);
+ }
+ return result.ToString ();
+ }
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/IFoldSegment.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/IFoldSegment.cs
new file mode 100644
index 0000000000..d1708faf36
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/IFoldSegment.cs
@@ -0,0 +1,74 @@
+//
+// IFoldSegment.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 MonoDevelop.Core.Text;
+using System.Web.UI.WebControls;
+
+namespace MonoDevelop.Ide.Editor
+{
+ /// <summary>
+ /// Represents the origin for a fold segment
+ /// </summary>
+ public enum FoldingType {
+ Unknown,
+ Region,
+ TypeDefinition,
+ TypeMember,
+ Comment
+ }
+
+ /// <summary>
+ /// A fold segment represents a collapsible region inside the text editor.
+ /// </summary>
+ public interface IFoldSegment : ISegment
+ {
+ /// <summary>
+ /// Gets or sets a value indicating whether this fold segment is collapsed.
+ /// </summary>
+ bool IsCollapsed {
+ get;
+ set;
+ }
+
+ /// <summary>
+ /// Gets or sets the collapsed text. This is displayed when the folding is collapsed instead of the collapsed region.
+ /// </summary>
+ string CollapsedText {
+ get;
+ set;
+ }
+
+ /// <summary>
+ /// Gets or sets the type of the folding. This type gives some info about where this folding segment
+ /// originates from.
+ /// </summary>
+ FoldingType FoldingType {
+ get;
+ set;
+ }
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/IReadonlyTextDocument.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/IReadonlyTextDocument.cs
new file mode 100644
index 0000000000..616e2fd1d1
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/IReadonlyTextDocument.cs
@@ -0,0 +1,246 @@
+//
+// IReadonlyTextDocument.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 MonoDevelop.Core.Text;
+using System.Collections.Generic;
+using System.Text;
+using MonoDevelop.Core;
+using MonoDevelop.Ide.Editor.Util;
+
+namespace MonoDevelop.Ide.Editor
+{
+ public interface IReadonlyTextDocument : ITextSource
+ {
+ bool IsReadOnly { get; }
+
+ FilePath FileName { get; }
+
+ string MimeType { get; }
+
+ /// <summary>
+ /// Gets the number of lines in the document.
+ /// </summary>
+ int LineCount { get; }
+
+ int LocationToOffset (int line, int column);
+
+ DocumentLocation OffsetToLocation (int offset);
+
+ IDocumentLine GetLine (int lineNumber);
+
+ IDocumentLine GetLineByOffset (int offset);
+ }
+
+ public static class ReadonlyTextDocumentExtensions
+ {
+ /// <summary>
+ /// Retrieves the text for a portion of the document.
+ /// </summary>
+ /// <exception cref="ArgumentOutOfRangeException">offset or length is outside the valid range.</exception>
+ public static string GetTextAt(this IReadonlyTextDocument source, ISegment segment)
+ {
+ if (source == null)
+ throw new ArgumentNullException ("source");
+ return source.GetTextAt (segment.Offset, segment.Length);
+ }
+
+ public static IEnumerable<IDocumentLine> GetLines (this IReadonlyTextDocument document)
+ {
+ if (document == null)
+ throw new ArgumentNullException ("document");
+ return document.GetLinesStartingAt (1);
+ }
+
+ public static IEnumerable<IDocumentLine> GetLinesBetween (this IReadonlyTextDocument document, int startLine, int endLine)
+ {
+ if (document == null)
+ throw new ArgumentNullException ("document");
+ if (startLine < 1 || startLine > document.LineCount)
+ throw new ArgumentOutOfRangeException ("startLine", startLine, string.Format ("value should be between 1 and {0}", document.LineCount));
+ if (endLine < 1 || endLine > document.LineCount)
+ throw new ArgumentOutOfRangeException ("endLine", endLine, string.Format ("value should be between 1 and {0}", document.LineCount));
+
+ var curLine = document.GetLine (startLine);
+ int count = endLine - startLine;
+ while (curLine != null && count --> 0) {
+ yield return curLine;
+ curLine = curLine.NextLine;
+ }
+ }
+
+ public static IEnumerable<IDocumentLine> GetLinesStartingAt (this IReadonlyTextDocument document, int startLine)
+ {
+ if (document == null)
+ throw new ArgumentNullException ("document");
+ if (startLine < 1 || startLine > document.LineCount)
+ throw new ArgumentOutOfRangeException ("startLine", startLine, string.Format ("value should be between 1 and {0}", document.LineCount));
+ var curLine = document.GetLine (startLine);
+ while (curLine != null) {
+ yield return curLine;
+ curLine = curLine.NextLine;
+ }
+ }
+
+ public static IEnumerable<IDocumentLine> GetLinesReverseStartingAt (this IReadonlyTextDocument document, int startLine)
+ {
+ if (startLine < 1 || startLine > document.LineCount)
+ throw new ArgumentOutOfRangeException ("startLine", startLine, string.Format ("value should be between 1 and {0}", document.LineCount));
+ var curLine = document.GetLine (startLine);
+ while (curLine != null) {
+ yield return curLine;
+ curLine = curLine.PreviousLine;
+ }
+ }
+
+ public static string GetTextBetween (this IReadonlyTextDocument document, int startLine, int startColumn, int endLine, int endColumn)
+ {
+ if (document == null)
+ throw new ArgumentNullException ("document");
+ return document.GetTextBetween (new DocumentLocation (startLine, startColumn), new DocumentLocation (endLine, endColumn));
+ }
+
+ public static string GetLineIndent (this IReadonlyTextDocument document, int lineNumber)
+ {
+ if (document == null)
+ throw new ArgumentNullException ("document");
+ return document.GetLineIndent (document.GetLine (lineNumber));
+ }
+
+ public static string GetLineIndent (this IReadonlyTextDocument document, IDocumentLine segment)
+ {
+ if (document == null)
+ throw new ArgumentNullException ("document");
+ if (segment == null)
+ throw new ArgumentNullException ("segment");
+ return segment.GetIndentation (document);
+ }
+
+ public static string GetLineText (this IReadonlyTextDocument document, IDocumentLine line, bool includeDelimiter = false)
+ {
+ if (document == null)
+ throw new ArgumentNullException ("document");
+ if (line == null)
+ throw new ArgumentNullException ("line");
+ return document.GetTextAt (includeDelimiter ? line.SegmentIncludingDelimiter : line);
+ }
+
+ public static string GetLineText (this IReadonlyTextDocument document, int lineNumber, bool includeDelimiter = false)
+ {
+ if (document == null)
+ throw new ArgumentNullException ("document");
+ var line = document.GetLine (lineNumber);
+ return document.GetTextAt (includeDelimiter ? line.SegmentIncludingDelimiter : line);
+ }
+
+ static int[] GetDiffCodes (IReadonlyTextDocument document, ref int codeCounter, Dictionary<string, int> codeDictionary, bool includeEol)
+ {
+ int i = 0;
+ var result = new int[document.LineCount];
+ foreach (var line in document.GetLinesStartingAt (1)) {
+ string lineText = document.GetTextAt (line.Offset, includeEol ? line.LengthIncludingDelimiter : line.Length);
+ int curCode;
+ if (!codeDictionary.TryGetValue (lineText, out curCode)) {
+ codeDictionary[lineText] = curCode = ++codeCounter;
+ }
+ result[i] = curCode;
+ i++;
+ }
+ return result;
+ }
+
+ public static IEnumerable<DiffHunk> GetDiff (this IReadonlyTextDocument document, IReadonlyTextDocument changedDocument, bool includeEol = true)
+ {
+ if (document == null)
+ throw new ArgumentNullException ("document");
+ if (changedDocument == null)
+ throw new ArgumentNullException ("changedDocument");
+ var codeDictionary = new Dictionary<string, int> ();
+ int codeCounter = 0;
+ return Diff.GetDiff<int> (GetDiffCodes (document, ref codeCounter, codeDictionary, includeEol),
+ GetDiffCodes (changedDocument, ref codeCounter, codeDictionary, includeEol));
+ }
+
+ public static string GetDiffAsString (this IReadonlyTextDocument document, IReadonlyTextDocument changedDocument, bool includeEol = true)
+ {
+ if (document == null)
+ throw new ArgumentNullException ("document");
+ if (changedDocument == null)
+ throw new ArgumentNullException ("changedDocument");
+ return Diff.GetDiffString (GetDiff (document, changedDocument, includeEol), document, changedDocument, document.FileName, changedDocument.FileName);
+ }
+
+ public static int OffsetToLineNumber (this IReadonlyTextDocument document, int offset)
+ {
+ if (document == null)
+ throw new ArgumentNullException ("document");
+ if (offset < 0 || offset > document.Length)
+ throw new ArgumentOutOfRangeException ("offset", string.Format ("offset should be between 0 and <={0} but was {1}.", document.Length, offset));
+ return document.OffsetToLocation (offset).Line;
+ }
+
+ public static int LocationToOffset (this IReadonlyTextDocument document, DocumentLocation location)
+ {
+ if (document == null)
+ throw new ArgumentNullException ("document");
+ return document.LocationToOffset (location.Line, location.Column);
+ }
+
+ public static string GetTextBetween (this IReadonlyTextDocument document, int startOffset, int endOffset)
+ {
+ if (document == null)
+ throw new ArgumentNullException ("document");
+ if (startOffset < 0 || startOffset > document.Length)
+ throw new ArgumentNullException ("startOffset");
+ if (endOffset < 0 || endOffset > document.Length)
+ throw new ArgumentNullException ("endOffset");
+ if (startOffset > endOffset)
+ throw new InvalidOperationException ();
+ return document.GetTextAt (startOffset, endOffset - startOffset);
+ }
+
+ public static string GetTextBetween (this IReadonlyTextDocument document, DocumentLocation start, DocumentLocation end)
+ {
+ if (document == null)
+ throw new ArgumentNullException ("document");
+ return document.GetTextBetween (document.LocationToOffset (start), document.LocationToOffset (end));
+ }
+
+ public static string GetEolMarker (this IReadonlyTextDocument document)
+ {
+ if (document == null)
+ throw new ArgumentNullException ("document");
+ string eol = null;
+ if (document.LineCount > 0) {
+ var line = document.GetLine (1);
+ if (line.DelimiterLength > 0)
+ eol = document.GetTextAt (line.Length, line.DelimiterLength);
+ }
+
+ return !string.IsNullOrEmpty (eol) ? eol : DefaultSourceEditorOptions.Instance.DefaultEolMarker;
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/ITextDocument.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/ITextDocument.cs
new file mode 100644
index 0000000000..b2ed856224
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/ITextDocument.cs
@@ -0,0 +1,140 @@
+//
+// ITextDocument.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 MonoDevelop.Core.Text;
+using System.Text;
+using MonoDevelop.Core;
+
+namespace MonoDevelop.Ide.Editor
+{
+ public interface ITextDocument : IReadonlyTextDocument
+ {
+ /// <summary>
+ /// Gets/Sets the text of the whole document..
+ /// </summary>
+ new string Text { get; set; } // hides ITextSource.Text to add the setter
+
+ /// <summary>
+ /// Gets or Sets 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>
+ new char this [int offset] { get; set; }
+
+ new bool IsReadOnly { get; set; }
+
+ new FilePath FileName { get; set; }
+
+ new string MimeType { get; set; }
+
+ new bool UseBOM { get; set; }
+
+ new Encoding Encoding { get; set; }
+
+ void InsertText (int offset, string text);
+
+ void InsertText (int offset, ITextSource text);
+
+ void RemoveText (int offset, int length);
+
+ void ReplaceText (int offset, int length, string value);
+
+ void ReplaceText (int offset, int length, ITextSource value);
+
+ bool IsInAtomicUndo {
+ get;
+ }
+
+ IDisposable OpenUndoGroup();
+
+ /// <summary>
+ /// This event is called directly before a change is applied to the document.
+ /// </summary>
+ /// <remarks>
+ /// It is invalid to modify the document within this event handler.
+ /// Aborting the change (by throwing an exception) is likely to cause corruption of data structures
+ /// that listen to the Changing and Changed events.
+ /// </remarks>
+ event EventHandler<TextChangeEventArgs> TextChanging;
+
+ /// <summary>
+ /// This event is called directly after a change is applied to the document.
+ /// </summary>
+ /// <remarks>
+ /// It is invalid to modify the document within this event handler.
+ /// Aborting the event handler (by throwing an exception) is likely to cause corruption of data structures
+ /// that listen to the Changing and Changed events.
+ /// </remarks>
+ event EventHandler<TextChangeEventArgs> TextChanged;
+
+ event EventHandler FileNameChanged;
+ event EventHandler MimeTypeChanged;
+
+ /// <summary>
+ /// Creates an immutable snapshot of this document.
+ /// </summary>
+ IReadonlyTextDocument CreateDocumentSnapshot();
+
+// event EventHandler<LineEventArgs> LineChanged;
+// event EventHandler<LineEventArgs> LineInserted;
+// event EventHandler<LineEventArgs> LineRemoved;
+ }
+
+ public static class DocumentExtensions
+ {
+ public static void RemoveText (this ITextDocument document, ISegment segment)
+ {
+ if (document == null)
+ throw new ArgumentNullException ("document");
+ document.RemoveText (segment.Offset, segment.Length);
+ }
+
+ public static void ReplaceText (this ITextDocument document, ISegment segment, string value)
+ {
+ if (document == null)
+ throw new ArgumentNullException ("document");
+ document.ReplaceText (segment.Offset, segment.Length, value);
+ }
+
+ public static void ReplaceText (this ITextDocument document, ISegment segment, ITextSource textSource)
+ {
+ if (document == null)
+ throw new ArgumentNullException ("document");
+ document.ReplaceText (segment.Offset, segment.Length, textSource);
+ }
+
+ public static void Save (this ITextDocument document)
+ {
+ if (document == null)
+ throw new ArgumentNullException ("document");
+ document.WriteTextTo (document.FileName);
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/ITextEditorOptions.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/ITextEditorOptions.cs
new file mode 100644
index 0000000000..223f2cf2ec
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/ITextEditorOptions.cs
@@ -0,0 +1,124 @@
+//
+// ITextEditorOptions.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 MonoDevelop.Ide.Editor.Highlighting;
+
+namespace MonoDevelop.Ide.Editor
+{
+ public enum IndentStyle
+ {
+ /// <summary>
+ /// No indentation occurs
+ /// </summary>
+ None,
+
+ /// <summary>
+ /// The indentation from the line above will be
+ /// taken to indent the current line
+ /// </summary>
+ Auto,
+
+ /// <summary>
+ /// Intelligent, context sensitive indentation will occur
+ /// </summary>
+ Smart,
+
+ /// <summary>
+ /// Intelligent, context sensitive indentation that minimizes whitespaces will occur
+ /// </summary>
+ Virtual
+ }
+
+ public enum ShowWhitespaces {
+ Never,
+ Selection,
+ Always
+ }
+
+ [Flags]
+ public enum IncludeWhitespaces {
+ None = 0,
+ Space = 1,
+ Tab = 2,
+ LineEndings = 4,
+ All = Space | Tab | LineEndings
+ }
+
+ public interface ITextEditorOptions : IDisposable
+ {
+ WordFindStrategy WordFindStrategy { get; }
+
+ bool TabsToSpaces { get; }
+ int IndentationSize { get; }
+ int TabSize { get; }
+ bool ShowIconMargin { get; }
+ bool ShowLineNumberMargin { get; }
+ bool ShowFoldMargin { get; }
+ bool HighlightCaretLine { get; }
+ int RulerColumn { get; }
+ bool ShowRuler { get; }
+ IndentStyle IndentStyle { get; }
+ bool OverrideDocumentEolMarker { get; }
+ bool EnableSyntaxHighlighting { get; }
+ bool RemoveTrailingWhitespaces { get; }
+
+ bool WrapLines { get; }
+
+ string FontName { get; }
+
+ string GutterFontName { get; }
+
+ string ColorScheme { get; }
+
+ string DefaultEolMarker { get; }
+
+ bool GenerateFormattingUndoStep { get; }
+
+ ShowWhitespaces ShowWhitespaces { get; }
+
+ IncludeWhitespaces IncludeWhitespaces { get; }
+ }
+
+ public static class TextEditorOptionsExtension
+ {
+ public static ColorScheme GetColorStyle (this ITextEditorOptions options)
+ {
+ if (options == null)
+ throw new ArgumentNullException ("options");
+ return SyntaxModeService.GetColorStyle (options.ColorScheme);
+ }
+
+ /// <summary>
+ /// Gets the indentation string for a single indent.
+ /// </summary>
+ public static string GetIndentationString (this ITextEditorOptions options)
+ {
+ if (options == null)
+ throw new ArgumentNullException ("options");
+ return options.TabsToSpaces ? new string (' ', options.IndentationSize) : "\t";
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/ITextLineMarker.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/ITextLineMarker.cs
new file mode 100644
index 0000000000..982033e951
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/ITextLineMarker.cs
@@ -0,0 +1,80 @@
+//
+// ITextLineMarker.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.Collections.Generic;
+
+namespace MonoDevelop.Ide.Editor
+{
+ public interface ITextLineMarker
+ {
+ IDocumentLine Line {
+ get;
+ }
+
+ bool IsVisible {
+ get;
+ set;
+ }
+
+ object Tag {
+ get;
+ set;
+ }
+ }
+
+ public enum UrlType {
+ Unknown,
+ Url,
+ Email
+ }
+
+ public interface IUrlTextLineMarker : ITextLineMarker
+ {
+ UrlType UrlType {
+ get;
+ }
+
+ string Url {
+ get;
+ }
+ }
+
+ public interface ICurrentDebugLineTextMarker : ITextLineMarker
+ {
+
+ }
+
+ public interface IMessageBubbleLineMarker : ITextLineMarker
+ {
+ int TaskCount { get; }
+
+ MonoDevelop.Ide.Tasks.TaskListEntry PrimaryTask { get; set; }
+
+ IEnumerable<MonoDevelop.Ide.Tasks.TaskListEntry> Tasks { get; }
+
+ void AddTask (MonoDevelop.Ide.Tasks.TaskListEntry task);
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/ITextSegmentMarker.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/ITextSegmentMarker.cs
new file mode 100644
index 0000000000..625f960988
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/ITextSegmentMarker.cs
@@ -0,0 +1,86 @@
+//
+// ITextSegmentMarker.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 MonoDevelop.Core.Text;
+using MonoDevelop.Ide.Editor;
+using MonoDevelop.Components;
+using MonoDevelop.Ide.TypeSystem;
+
+namespace MonoDevelop.Ide.Editor
+{
+ public interface ITextSegmentMarker : ISegment
+ {
+ bool IsVisible {
+ get;
+ set;
+ }
+
+ object Tag {
+ get;
+ set;
+ }
+
+ event EventHandler<TextMarkerMouseEventArgs> MousePressed;
+ event EventHandler<TextMarkerMouseEventArgs> MouseHover;
+ }
+
+ public enum TextSegmentMarkerEffect {
+ /// <summary>
+ /// The region is marked as waved underline.
+ /// </summary>
+ WavedLine,
+
+ /// <summary>
+ /// The region is marked as dotted line.
+ /// </summary>
+ DottedLine,
+
+ /// <summary>
+ /// The text is grayed out.
+ /// </summary>
+ GrayOut
+ }
+
+ public interface IGenericTextSegmentMarker : ITextSegmentMarker
+ {
+ TextSegmentMarkerEffect Effect { get; }
+
+ HslColor Color { get; set; }
+ }
+
+ public interface IErrorMarker : ITextSegmentMarker
+ {
+ Error Error { get; }
+ }
+
+ public interface ISmartTagMarker : ITextSegmentMarker
+ {
+ bool IsInsideSmartTag (double x, double y);
+
+ bool IsInsideWindow (Gtk.MotionNotifyEventArgs args);
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/IUnitTestMarker.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/IUnitTestMarker.cs
new file mode 100644
index 0000000000..0350ff9839
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/IUnitTestMarker.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+
+namespace MonoDevelop.Ide.Editor
+{
+ public interface IUnitTestMarker : ITextLineMarker
+ {
+ UnitTestLocation UnitTest { get; }
+
+ void UpdateState ();
+ }
+
+ public abstract class UnitTestMarkerHost
+ {
+ public abstract Xwt.Drawing.Image GetStatusIcon (string unitTestIdentifier, string caseId = null);
+ public abstract bool IsFailure (string unitTestIdentifier, string caseId = null);
+ public abstract string GetMessage (string unitTestIdentifier, string caseId = null);
+ public abstract bool HasResult (string unitTestIdentifier, string caseId = null);
+
+ public abstract void PopupContextMenu (UnitTestLocation unitTest, int x, int y);
+ }
+
+ public class UnitTestLocation
+ {
+ public int Offset { get; set; }
+ public bool IsFixture { get; set; }
+ public string UnitTestIdentifier { get; set; }
+ public bool IsIgnored { get; set; }
+
+ public List<string> TestCases = new List<string> ();
+
+ public UnitTestLocation (int offset)
+ {
+ Offset = offset;
+ }
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InsertionCursorEventArgs.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InsertionCursorEventArgs.cs
new file mode 100644
index 0000000000..2f5996cc6a
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InsertionCursorEventArgs.cs
@@ -0,0 +1,51 @@
+//
+// InsertionCursorEventArgs.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;
+
+namespace MonoDevelop.Ide.Editor
+{
+ [Serializable]
+ public sealed class InsertionCursorEventArgs : EventArgs
+ {
+ public bool Success {
+ get;
+ private set;
+ }
+
+ public InsertionPoint InsertionPoint {
+ get;
+ private set;
+ }
+
+ public InsertionCursorEventArgs (bool success, InsertionPoint insertionPoint)
+ {
+ Success = success;
+ InsertionPoint = insertionPoint;
+ }
+ }
+
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InsertionModeOptions.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InsertionModeOptions.cs
new file mode 100644
index 0000000000..c65e4cea08
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InsertionModeOptions.cs
@@ -0,0 +1,88 @@
+//
+// InsertionModeOptions.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.Collections.Generic;
+
+namespace MonoDevelop.Ide.Editor
+{
+ /// <summary>
+ /// This class contains information the editor needs to initiate the insertion mode.
+ /// </summary>
+ public sealed class InsertionModeOptions
+ {
+ /// <summary>
+ /// A user visible string describing this operation.
+ /// </summary>
+ public string Operation {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// The list of insertion points that are used for the insertion mode. The caret is only able to move between
+ /// the insertion points.
+ /// </summary>
+ public IList<InsertionPoint> InsertionPoints {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// That's the action that is started after the insertion mode ended.
+ /// </summary>
+ public Action<InsertionCursorEventArgs> ModeExitedAction {
+ get;
+ set;
+ }
+
+ /// <summary>
+ /// Gets or sets the first selected insertion point. The default value is 0.
+ /// </summary>
+ public int FirstSelectedInsertionPoint {
+ get;
+ set;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MonoDevelop.Ide.Editor.InsertionModeOptions"/> class.
+ /// </summary>
+ /// <param name="operation">A user visible string describing this operation.</param>
+ /// <param name="insertionPoints">The list of insertion points that are used for the insertion mode.</param>
+ /// <param name="modeExitedAction">The action that is started after the exit mode ended.</param>
+ public InsertionModeOptions (string operation, IList<InsertionPoint> insertionPoints, Action<InsertionCursorEventArgs> modeExitedAction)
+ {
+ if (operation == null)
+ throw new ArgumentNullException ("operation");
+ if (insertionPoints == null)
+ throw new ArgumentNullException ("insertionPoints");
+ if (modeExitedAction == null)
+ throw new ArgumentNullException ("modeExitedAction");
+ Operation = operation;
+ InsertionPoints = insertionPoints;
+ ModeExitedAction = modeExitedAction;
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InsertionPoint.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InsertionPoint.cs
new file mode 100644
index 0000000000..7bf2808146
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InsertionPoint.cs
@@ -0,0 +1,119 @@
+//
+// InsertionPoint.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 MonoDevelop.Core.Text;
+using MonoDevelop.Ide.CodeFormatting;
+
+namespace MonoDevelop.Ide.Editor
+{
+ public enum NewLineInsertion
+ {
+ None,
+ Eol,
+ BlankLine
+ }
+
+ public sealed class InsertionPoint
+ {
+ public DocumentLocation Location {
+ get;
+ set;
+ }
+
+ public NewLineInsertion LineBefore { get; set; }
+ public NewLineInsertion LineAfter { get; set; }
+
+ public InsertionPoint (DocumentLocation location, NewLineInsertion lineBefore, NewLineInsertion lineAfter)
+ {
+ this.Location = location;
+ this.LineBefore = lineBefore;
+ this.LineAfter = lineAfter;
+ }
+
+ public override string ToString ()
+ {
+ return string.Format ("[InsertionPoint: Location={0}, LineBefore={1}, LineAfter={2}]", Location, LineBefore, LineAfter);
+ }
+
+ public void InsertNewLine (ITextDocument editor, NewLineInsertion insertion, ref int offset)
+ {
+ string str = null;
+ switch (insertion) {
+ case NewLineInsertion.Eol:
+ str = editor.GetEolMarker ();
+ break;
+ case NewLineInsertion.BlankLine:
+ str = editor.GetEolMarker () + editor.GetEolMarker ();
+ break;
+ default:
+ return;
+ }
+
+ editor.InsertText (offset, str);
+ offset += str.Length;
+ }
+
+ public int Insert (TextEditor editor, DocumentContext ctx, string text)
+ {
+ int offset = editor.LocationToOffset (Location);
+ using (var undo = editor.OpenUndoGroup ()) {
+
+ var line = editor.GetLineByOffset (offset);
+ int insertionOffset = line.Offset + Location.Column - 1;
+ offset = insertionOffset;
+ InsertNewLine (editor, LineBefore, ref offset);
+ int result = offset - insertionOffset;
+
+ editor.InsertText (offset, text);
+ offset += text.Length;
+ InsertNewLine (editor, LineAfter, ref offset);
+ CodeFormatterService.Format (editor, ctx, TextSegment.FromBounds (insertionOffset - 1, offset));
+ return result;
+ }
+ }
+
+ public int Insert (ITextDocument editor, string text)
+ {
+ int offset = editor.LocationToOffset (Location);
+ using (var undo = editor.OpenUndoGroup ()) {
+
+ // TODO: Run formatter !!!
+ // text = editor.FormatString (Location, text);
+
+ var line = editor.GetLineByOffset (offset);
+ int insertionOffset = line.Offset + Location.Column - 1;
+ offset = insertionOffset;
+ InsertNewLine (editor, LineBefore, ref offset);
+ int result = offset - insertionOffset;
+
+ editor.InsertText (offset, text);
+ offset += text.Length;
+ InsertNewLine (editor, LineAfter, ref offset);
+ return result;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/IEditorActionHost.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/IEditorActionHost.cs
new file mode 100644
index 0000000000..f95b0607fe
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/IEditorActionHost.cs
@@ -0,0 +1,123 @@
+//
+// ITextEditor.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 MonoDevelop.Core.Text;
+
+namespace MonoDevelop.Ide.Editor
+{
+ interface IEditorActionHost
+ {
+ void SwitchCaretMode ();
+
+ void InsertTab ();
+
+ void RemoveTab ();
+
+ void InsertNewLine ();
+
+ void DeletePreviousWord ();
+
+ void DeleteNextWord ();
+
+ void DeletePreviousSubword ();
+
+ void DeleteNextSubword ();
+
+ void StartCaretPulseAnimation ();
+
+ void RecenterEditor ();
+
+ void JoinLines ();
+
+ void MoveNextSubWord ();
+
+ void MovePrevSubWord ();
+
+ void MoveNextWord ();
+
+ void MovePrevWord ();
+
+ void PageUp ();
+
+ void PageDown ();
+
+ void MoveCaretDown ();
+
+ void MoveCaretUp ();
+
+ void MoveCaretRight ();
+
+ void MoveCaretLeft ();
+
+ void MoveCaretToLineEnd ();
+
+ void MoveCaretToLineStart ();
+
+ void MoveCaretToDocumentStart ();
+
+ void MoveCaretToDocumentEnd ();
+
+ void Backspace ();
+
+ void Delete ();
+
+ void ClipboardCopy ();
+
+ void ClipboardCut ();
+
+ void ClipboardPaste ();
+
+ void SelectAll ();
+
+ void NewLine ();
+
+ void Undo ();
+
+ void Redo ();
+
+ void DeleteCurrentLine ();
+
+ void DeleteCurrentLineToEnd ();
+
+ void ScrollLineUp ();
+
+ void ScrollLineDown ();
+
+ void ScrollPageUp ();
+
+ void ScrollPageDown ();
+
+ void MoveBlockUp ();
+
+ void MoveBlockDown ();
+
+ void ToggleBlockSelectionMode ();
+
+ void IndentSelection ();
+
+ void UnIndentSelection ();
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/ITextEditorFactory.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/ITextEditorFactory.cs
new file mode 100644
index 0000000000..4b88445dab
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/ITextEditorFactory.cs
@@ -0,0 +1,44 @@
+//
+// ITextEditorFactory.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 MonoDevelop.Core.Text;
+
+namespace MonoDevelop.Ide.Editor
+{
+ interface ITextEditorFactory
+ {
+ ITextDocument CreateNewDocument ();
+ ITextDocument CreateNewDocument (ITextSource textSource, string fileName, string mimeType);
+
+ IReadonlyTextDocument CreateNewReadonlyDocument (ITextSource textSource, string fileName, string mimeType);
+
+ ITextEditorImpl CreateNewEditor ();
+ ITextEditorImpl CreateNewEditor (IReadonlyTextDocument document);
+
+ string[] GetSyntaxProperties (string mimeType, string name);
+ }
+
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/ITextEditorImpl.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/ITextEditorImpl.cs
new file mode 100644
index 0000000000..4b299a40ef
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/ITextEditorImpl.cs
@@ -0,0 +1,244 @@
+//
+// ITextEditorImpl.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 MonoDevelop.Core.Text;
+using System.Collections.Generic;
+using MonoDevelop.Ide.Gui;
+using MonoDevelop.Ide.Editor.Extension;
+using MonoDevelop.Ide.Editor.Highlighting;
+using MonoDevelop.Components;
+
+namespace MonoDevelop.Ide.Editor
+{
+ public enum EditMode
+ {
+ Edit,
+ TextLink,
+ CursorInsertion
+ }
+
+ /// <summary>
+ /// A skip char is a character in the editor at a defined position that is skipped when this
+ /// exact character is pressed at the character position. That's useful for inserting automatically generated brackets without
+ /// interfering with the typing flow.
+ /// </summary>
+ public struct SkipChar
+ {
+ /// <summary>
+ /// Gets the offset.
+ /// </summary>
+ public readonly int Offset;
+
+ /// <summary>
+ /// Gets the char.
+ /// </summary>
+ public readonly char Char;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MonoDevelop.Ide.Editor.SkipChar"/> struct.
+ /// </summary>
+ /// <param name="offset">The offset of the char.</param>
+ /// <param name="ch">The character</param>
+ public SkipChar (int offset, char ch)
+ {
+ Offset = offset;
+ Char = ch;
+ }
+
+ /// <summary>
+ /// Returns a <see cref="System.String"/> that represents the current <see cref="MonoDevelop.Ide.Editor.SkipChar"/>.
+ /// </summary>
+ /// <returns>A <see cref="System.String"/> that represents the current <see cref="MonoDevelop.Ide.Editor.SkipChar"/>.</returns>
+ public override string ToString ()
+ {
+ return string.Format ("[SkipChar: Offset={0}, Char={1}]", Offset, Char);
+ }
+ }
+
+ interface ITextEditorImpl : IViewContent, IDisposable
+ {
+ EditMode EditMode { get; }
+
+ ITextEditorOptions Options { get; set; }
+
+ IReadonlyTextDocument Document { get; set; }
+
+ DocumentLocation CaretLocation { get; set; }
+
+ SemanticHighlighting SemanticHighlighting { get; set; }
+
+ int CaretOffset { get; set; }
+
+ bool IsSomethingSelected { get; }
+
+ SelectionMode SelectionMode { get; }
+
+ ISegment SelectionRange { get; set; }
+ int SelectionAnchorOffset { get; set; }
+ int SelectionLeadOffset { get; set; }
+
+ DocumentRegion SelectionRegion { get; set; }
+
+ void SetSelection (int anchorOffset, int leadOffset);
+
+ event EventHandler SelectionChanged;
+
+ event EventHandler CaretPositionChanged;
+
+ event EventHandler BeginMouseHover;
+
+ event EventHandler VAdjustmentChanged;
+
+ event EventHandler HAdjustmentChanged;
+
+ void ClearSelection ();
+
+ void CenterToCaret ();
+
+ void StartCaretPulseAnimation ();
+
+ int EnsureCaretIsNotVirtual ();
+
+ void FixVirtualIndentation ();
+
+ IEditorActionHost Actions { get; }
+
+ ITextMarkerFactory TextMarkerFactory { get; }
+
+ event EventHandler BeginAtomicUndoOperation;
+
+ event EventHandler EndAtomicUndoOperation;
+
+ object CreateNativeControl ();
+
+ void RunWhenLoaded (Action action);
+
+ string FormatString (int offset, string code);
+
+ void StartInsertionMode (InsertionModeOptions insertionModeOptions);
+
+ void StartTextLinkMode (TextLinkModeOptions textLinkModeOptions);
+
+ double LineHeight { get; }
+
+ DocumentLocation PointToLocation (double xp, double yp, bool endAtEol = false);
+
+ Xwt.Point LocationToPoint (int line, int column);
+
+ void AddMarker (IDocumentLine line, ITextLineMarker lineMarker);
+
+ void RemoveMarker (ITextLineMarker lineMarker);
+
+ void ScrollTo (int offset);
+
+ void CenterTo (int offset);
+
+ IList<SkipChar> SkipChars
+ {
+ get;
+ }
+
+ void AddSkipChar (int offset, char ch);
+
+ string GetVirtualIndentationString (int lineNumber);
+
+ IEnumerable<ITextLineMarker> GetLineMarkers (IDocumentLine line);
+
+ #region Text segment markers
+
+ IEnumerable<ITextSegmentMarker> GetTextSegmentMarkersAt (ISegment segment);
+
+ IEnumerable<ITextSegmentMarker> GetTextSegmentMarkersAt (int offset);
+
+ /// <summary>
+ /// Adds a marker to the document.
+ /// </summary>
+ void AddMarker (ITextSegmentMarker marker);
+
+ /// <summary>
+ /// Removes a marker from the document.
+ /// </summary>
+ /// <returns><c>true</c>, if marker was removed, <c>false</c> otherwise.</returns>
+ /// <param name="marker">Marker.</param>
+ bool RemoveMarker (ITextSegmentMarker marker);
+
+ #endregion
+
+ IFoldSegment CreateFoldSegment (int offset, int length, bool isFolded = false);
+
+ void SetFoldings (IEnumerable<IFoldSegment> foldings);
+
+ IEnumerable<IFoldSegment> GetFoldingsContaining (int offset);
+
+ IEnumerable<IFoldSegment> GetFoldingsIn (int offset, int length);
+
+ string GetPangoMarkup (int offset, int length);
+
+ void SetIndentationTracker (IndentationTracker indentationTracker);
+ void SetSelectionSurroundingProvider (SelectionSurroundingProvider surroundingProvider);
+ void SetTextPasteHandler (TextPasteHandler textPasteHandler);
+
+ event EventHandler<LineEventArgs> LineChanged;
+ event EventHandler<LineEventArgs> LineInserted;
+ event EventHandler<LineEventArgs> LineRemoved;
+
+ #region Internal use only API (do not mirror in TextEditor)
+
+ TextEditorExtension EditorExtension
+ {
+ get;
+ set;
+ }
+
+ IEnumerable<TooltipProvider> TooltipProvider
+ {
+ get;
+ }
+
+ void ClearTooltipProviders ();
+
+ void AddTooltipProvider (TooltipProvider provider);
+
+ void RemoveTooltipProvider (TooltipProvider provider);
+
+ Xwt.Point GetEditorWindowOrigin ();
+
+ Xwt.Rectangle GetEditorAllocation ();
+
+ void InformLoadComplete ();
+
+ void SetUsageTaskProviders (IEnumerable<UsageProviderEditorExtension> providers);
+
+ void SetQuickTaskProviders (IEnumerable<IQuickTaskProvider> providers);
+ #endregion
+
+ double ZoomLevel { get; set; }
+ event EventHandler ZoomLevelChanged;
+
+ void AddOverlay (Control messageOverlayContent, Func<int> sizeFunc);
+ void RemoveOverlay (Control messageOverlayContent);
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/ITextMarkerFactory.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/ITextMarkerFactory.cs
new file mode 100644
index 0000000000..9c870c9874
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/ITextMarkerFactory.cs
@@ -0,0 +1,58 @@
+//
+// ITextMarkerFactory.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 MonoDevelop.Core.Text;
+using MonoDevelop.Ide.Editor.Extension;
+using MonoDevelop.Ide.TypeSystem;
+
+namespace MonoDevelop.Ide.Editor
+{
+ public enum LinkRequest
+ {
+ SameView,
+ RequestNewView
+ }
+
+ interface ITextMarkerFactory
+ {
+ #region Line marker
+ IUrlTextLineMarker CreateUrlTextMarker (TextEditor editor, IDocumentLine line, string value, UrlType url, string syntax, int startCol, int endCol);
+ ICurrentDebugLineTextMarker CreateCurrentDebugLineTextMarker (TextEditor editor);
+ ITextLineMarker CreateAsmLineMarker (TextEditor editor);
+ IUnitTestMarker CreateUnitTestMarker (TextEditor editor, UnitTestMarkerHost host, UnitTestLocation unitTestLocation);
+ IMessageBubbleLineMarker CreateMessageBubbleLineMarker (TextEditor editor);
+ #endregion
+
+ #region Segment marker
+ ITextSegmentMarker CreateUsageMarker (TextEditor editor, Usage usage);
+ ITextSegmentMarker CreateLinkMarker (TextEditor editor, int offset, int length, Action<LinkRequest> activateLink);
+
+ IGenericTextSegmentMarker CreateGenericTextSegmentMarker (TextEditor editor, TextSegmentMarkerEffect effect, int offset, int length);
+ ISmartTagMarker CreateSmartTagMarker (TextEditor editor, int offset, DocumentLocation realLocation);
+ IErrorMarker CreateErrorMarker (TextEditor editor, Error info, int offset, int length);
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/LineEventArgs.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/LineEventArgs.cs
new file mode 100644
index 0000000000..3dd6e0cc56
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/LineEventArgs.cs
@@ -0,0 +1,48 @@
+//
+// LineEventArgs.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;
+
+namespace MonoDevelop.Ide.Editor
+{
+ public class LineEventArgs : System.EventArgs
+ {
+ readonly IDocumentLine line;
+
+ public IDocumentLine Line {
+ get {
+ return line;
+ }
+ }
+
+ public LineEventArgs (IDocumentLine line)
+ {
+ if (line == null)
+ throw new ArgumentNullException ("line");
+ this.line = line;
+ }
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/MessageBubbles/MessageBubbleCommands.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/MessageBubbles/MessageBubbleCommands.cs
new file mode 100644
index 0000000000..2e81dbf847
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/MessageBubbles/MessageBubbleCommands.cs
@@ -0,0 +1,77 @@
+//
+// MessageBubbleCommands.cs
+//
+// Author:
+// Mike Krüger <mkrueger@novell.com>
+//
+// Copyright (c) 2010 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 MonoDevelop.Ide;
+using MonoDevelop.Components.Commands;
+using MonoDevelop.Core;
+
+namespace MonoDevelop.Ide.Editor
+{
+ enum MessageBubbleCommands
+ {
+ Toggle,
+ HideIssues,
+ ToggleIssues
+ }
+
+ class HideIssuesHandler : CommandHandler
+ {
+ protected override void Update (CommandInfo info)
+ {
+ base.Update (info);
+ info.Text = IdeApp.Preferences.DefaultHideMessageBubbles ? GettextCatalog.GetString ("_Show Message Bubbles") : GettextCatalog.GetString ("_Hide Message Bubbles");
+ }
+
+ protected override void Run (object data)
+ {
+ IdeApp.Preferences.DefaultHideMessageBubbles = !IdeApp.Preferences.DefaultHideMessageBubbles;
+ }
+ }
+
+ class ToggleIssuesHandler : CommandHandler
+ {
+ protected override void Run (object data)
+ {
+ Action action = data as Action;
+ if (action != null)
+ action ();
+ }
+
+ protected override void Update (CommandArrayInfo ainfo)
+ {
+ CommandInfo info = ainfo.Add (GettextCatalog.GetString ("_Errors & Warnings"), new Action (delegate {
+ MonoDevelop.Ide.IdeApp.Preferences.ShowMessageBubbles = MonoDevelop.Ide.ShowMessageBubbles.ForErrorsAndWarnings;
+ }));
+ info.Checked = MonoDevelop.Ide.IdeApp.Preferences.ShowMessageBubbles == MonoDevelop.Ide.ShowMessageBubbles.ForErrorsAndWarnings;
+
+ info = ainfo.Add (GettextCatalog.GetString ("E_rrors only"), new Action (delegate {
+ MonoDevelop.Ide.IdeApp.Preferences.ShowMessageBubbles = MonoDevelop.Ide.ShowMessageBubbles.ForErrors;
+ }));
+ info.Checked = MonoDevelop.Ide.IdeApp.Preferences.ShowMessageBubbles == MonoDevelop.Ide.ShowMessageBubbles.ForErrors;
+ }
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/IProjectionExtension.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/IProjectionExtension.cs
new file mode 100644
index 0000000000..b995eaabe1
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/IProjectionExtension.cs
@@ -0,0 +1,39 @@
+//
+// IProjectionExtension.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2015 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.Collections.Generic;
+
+namespace MonoDevelop.Ide.Editor.Projection
+{
+ interface IProjectionExtension
+ {
+ IReadOnlyList<Projection> Projections {
+ get;
+ set;
+ }
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedCompletionExtension.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedCompletionExtension.cs
new file mode 100644
index 0000000000..91f4b820a9
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedCompletionExtension.cs
@@ -0,0 +1,433 @@
+//
+// ProjectedCompletionExtension.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2015 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 MonoDevelop.Ide.Editor.Extension;
+using System.Collections.Generic;
+using MonoDevelop.Ide.CodeCompletion;
+
+
+namespace MonoDevelop.Ide.Editor.Projection
+{
+ sealed class ProjectedCompletionExtension : CompletionTextEditorExtension, IProjectionExtension
+ {
+ DocumentContext ctx;
+ IReadOnlyList<Projection> projections;
+
+ public IReadOnlyList<Projection> Projections {
+ get {
+ return projections;
+ }
+ set {
+ projections = value;
+ }
+ }
+
+ public ProjectedCompletionExtension (DocumentContext ctx, IReadOnlyList<Projection> projections)
+ {
+ if (projections == null)
+ throw new ArgumentNullException ("projections");
+ this.ctx = ctx;
+ this.projections = projections;
+ }
+
+ public override bool IsValidInContext (DocumentContext context)
+ {
+ var pctx = context as ProjectedDocumentContext;
+ if (pctx == null)
+ return false;
+ return pctx.ProjectedEditor.GetContent<CompletionTextEditorExtension> () != null;
+ }
+
+ Projection GetProjectionAt (int offset)
+ {
+ foreach (var projection in projections) {
+ foreach (var seg in projection.ProjectedSegments) {
+ if (seg.ContainsOriginal (offset)) {
+ projection.ProjectedEditor.CaretOffset = seg.FromOriginalToProjected (offset);
+ return projection;
+ }
+ }
+ }
+ return null;
+ }
+
+ CompletionTextEditorExtension GetExtensionAt (int offset)
+ {
+ var projection = GetProjectionAt (offset);
+ if (projection != null) {
+ var result = projection.ProjectedEditor.GetContent<CompletionTextEditorExtension> ();
+ if (result != null) {
+ result.CompletionWidget = new ProjectedCompletionWidget (CompletionWidget, projection);
+ }
+ return result;
+ }
+ return null;
+ }
+
+ class ProjectedCompletionWidget : ICompletionWidget
+ {
+ readonly ICompletionWidget completionWidget;
+ readonly Projection projection;
+
+ public ProjectedCompletionWidget (ICompletionWidget completionWidget, Projection projection)
+ {
+ if (completionWidget == null)
+ throw new ArgumentNullException ("completionWidget");
+ if (projection == null)
+ throw new ArgumentNullException ("projection");
+ this.projection = projection;
+ this.completionWidget = completionWidget;
+ }
+
+ #region ICompletionWidget implementation
+ public double ZoomLevel {
+ get {
+ return completionWidget.ZoomLevel;
+ }
+ }
+
+ event EventHandler ICompletionWidget.CompletionContextChanged {
+ add {
+ completionWidget.CompletionContextChanged += value;
+ }
+ remove {
+ completionWidget.CompletionContextChanged -= value;
+ }
+ }
+
+ string ICompletionWidget.GetText (int startOffset, int endOffset)
+ {
+ return projection.ProjectedEditor.GetTextBetween (startOffset, endOffset);
+ }
+
+ char ICompletionWidget.GetChar (int offset)
+ {
+ return projection.ProjectedEditor.GetCharAt (offset);
+ }
+
+ void ICompletionWidget.Replace (int offset, int count, string text)
+ {
+ foreach (var seg in projection.ProjectedSegments) {
+ if (seg.ContainsProjected (offset)) {
+ offset = seg.FromProjectedToOriginal (offset);
+ break;
+ }
+ }
+
+ completionWidget.Replace (offset, count, text);
+ }
+
+ int ConvertOffset (int triggerOffset)
+ {
+ int result = triggerOffset;
+ foreach (var seg in projection.ProjectedSegments) {
+ if (seg.ContainsProjected (result)) {
+ result = seg.FromProjectedToOriginal (result);
+ break;
+ }
+ }
+ return result;
+ }
+
+ int ProjectOffset (int offset)
+ {
+ int result = offset;
+ foreach (var seg in projection.ProjectedSegments) {
+ if (seg.ContainsOriginal (result)) {
+ result = seg.FromOriginalToProjected (result);
+ break;
+ }
+ }
+ return result;
+
+ }
+
+ CodeCompletionContext ICompletionWidget.CreateCodeCompletionContext (int triggerOffset)
+ {
+ var originalTriggerOffset = ConvertOffset (triggerOffset);
+ var completionContext = completionWidget.CreateCodeCompletionContext (originalTriggerOffset);
+ return ConvertContext (completionContext, projection);
+ }
+
+ string ICompletionWidget.GetCompletionText (CodeCompletionContext ctx)
+ {
+ return completionWidget.GetCompletionText (ImportContext (ctx, projection));
+ }
+
+ void ICompletionWidget.SetCompletionText (CodeCompletionContext ctx, string partial_word, string complete_word)
+ {
+ completionWidget.SetCompletionText (ImportContext (ctx, projection), partial_word, complete_word);
+ }
+
+ void ICompletionWidget.SetCompletionText (CodeCompletionContext ctx, string partial_word, string complete_word, int completeWordOffset)
+ {
+ completionWidget.SetCompletionText (ImportContext (ctx, projection), partial_word, complete_word, completeWordOffset);
+ }
+
+ void ICompletionWidget.AddSkipChar (int cursorPosition, char c)
+ {
+ completionWidget.AddSkipChar (ProjectOffset (cursorPosition), c);
+ }
+
+ CodeCompletionContext ICompletionWidget.CurrentCodeCompletionContext {
+ get {
+ return ConvertContext (completionWidget.CurrentCodeCompletionContext, projection);
+ }
+ }
+
+ int ICompletionWidget.CaretOffset {
+ get {
+ return ConvertOffset (completionWidget.CaretOffset);
+ }
+ set {
+ completionWidget.CaretOffset = ProjectOffset (value);
+ }
+ }
+
+ int ICompletionWidget.TextLength {
+ get {
+ return projection.ProjectedEditor.Length;
+ }
+ }
+
+ int ICompletionWidget.SelectedLength {
+ get {
+ return completionWidget.SelectedLength;
+ }
+ }
+
+ Gtk.Style ICompletionWidget.GtkStyle {
+ get {
+ return completionWidget.GtkStyle;
+ }
+ }
+ #endregion
+ }
+
+ CompletionTextEditorExtension GetCurrentExtension ()
+ {
+ return GetExtensionAt (Editor.CaretOffset);
+ }
+
+ public override bool CanRunCompletionCommand ()
+ {
+ var projectedExtension = GetCurrentExtension ();
+ if (projectedExtension == null)
+ return false;
+ return projectedExtension.CanRunCompletionCommand ();
+ }
+
+ public override MonoDevelop.Ide.CodeCompletion.ICompletionDataList CodeCompletionCommand (MonoDevelop.Ide.CodeCompletion.CodeCompletionContext completionContext)
+ {
+ var projectedExtension = GetExtensionAt (completionContext.TriggerOffset);
+ if (projectedExtension == null)
+ return null;
+ return projectedExtension.CodeCompletionCommand (ConvertContext (completionContext));
+ }
+
+ public override bool CanRunParameterCompletionCommand ()
+ {
+ var projectedExtension = GetCurrentExtension ();
+ if (projectedExtension == null)
+ return false;
+ return projectedExtension.CanRunParameterCompletionCommand ();
+ }
+
+ public override string CompletionLanguage {
+ get {
+ var projectedExtension = GetCurrentExtension ();
+ if (projectedExtension == null)
+ return base.CompletionLanguage;
+ return projectedExtension.CompletionLanguage;
+ }
+ }
+
+ public override bool GetCompletionCommandOffset (out int cpos, out int wlen)
+ {
+ var projectedExtension = GetCurrentExtension ();
+ if (projectedExtension == null) {
+ cpos = 0;
+ wlen = 0;
+ return false;
+ }
+ return projectedExtension.GetCompletionCommandOffset (out cpos, out wlen);
+ }
+
+ public override int GetCurrentParameterIndex (int startOffset)
+ {
+ var projectedExtension = GetExtensionAt (startOffset);
+ if (projectedExtension == null)
+ return -1;
+ return projectedExtension.GetCurrentParameterIndex (startOffset);
+ }
+
+ public override int GuessBestMethodOverload (ParameterHintingResult provider, int currentOverload)
+ {
+ var projectedExtension = GetCurrentExtension ();
+ if (projectedExtension == null)
+ return -1;
+ return projectedExtension.GuessBestMethodOverload (provider, currentOverload);
+ }
+
+ public override System.Threading.Tasks.Task<MonoDevelop.Ide.CodeCompletion.ICompletionDataList> HandleCodeCompletionAsync (MonoDevelop.Ide.CodeCompletion.CodeCompletionContext completionContext, char completionChar, System.Threading.CancellationToken token)
+ {
+ var projectedExtension = GetExtensionAt (completionContext.TriggerOffset);
+ if (projectedExtension == null)
+ return null;
+
+ return projectedExtension.HandleCodeCompletionAsync (ConvertContext (completionContext), completionChar, token);
+ }
+
+ public override System.Threading.Tasks.Task<ParameterHintingResult> HandleParameterCompletionAsync (MonoDevelop.Ide.CodeCompletion.CodeCompletionContext completionContext, char completionChar, System.Threading.CancellationToken token)
+ {
+ var projectedExtension = GetExtensionAt (completionContext.TriggerOffset);
+ if (projectedExtension == null)
+ return null;
+ return projectedExtension.HandleParameterCompletionAsync (ConvertContext (completionContext), completionChar, token);
+ }
+
+ public override bool KeyPress (KeyDescriptor descriptor)
+ {
+ projections = ctx.GetPartialProjectionsAsync ().Result;
+ var projectedExtension = GetCurrentExtension();
+ if (projectedExtension != null)
+ return projectedExtension.KeyPress (descriptor);
+ return base.KeyPress (descriptor);
+ }
+
+ public override ParameterHintingResult ParameterCompletionCommand (MonoDevelop.Ide.CodeCompletion.CodeCompletionContext completionContext)
+ {
+ var projectedExtension = GetExtensionAt (completionContext.TriggerOffset);
+ if (projectedExtension == null)
+ return null;
+ return projectedExtension.ParameterCompletionCommand (ConvertContext (completionContext));
+ }
+
+ public override void RunCompletionCommand ()
+ {
+ var projectedExtension = GetCurrentExtension();
+ if (projectedExtension == null)
+ return;
+ projectedExtension.RunCompletionCommand ();
+ }
+
+ public override void RunParameterCompletionCommand ()
+ {
+ var projectedExtension = GetCurrentExtension();
+ if (projectedExtension == null)
+ return;
+
+ projectedExtension.RunParameterCompletionCommand ();
+ }
+
+ public override void RunShowCodeTemplatesWindow ()
+ {
+ var projectedExtension = GetCurrentExtension();
+ if (projectedExtension == null)
+ return;
+ projectedExtension.RunShowCodeTemplatesWindow ();
+ }
+
+ public override MonoDevelop.Ide.CodeCompletion.ICompletionDataList ShowCodeSurroundingsCommand (MonoDevelop.Ide.CodeCompletion.CodeCompletionContext completionContext)
+ {
+ var projectedExtension = GetExtensionAt (completionContext.TriggerOffset);
+ if (projectedExtension == null)
+ return null;
+ return projectedExtension.ShowCodeSurroundingsCommand (ConvertContext (completionContext));
+ }
+
+ public override MonoDevelop.Ide.CodeCompletion.ICompletionDataList ShowCodeTemplatesCommand (MonoDevelop.Ide.CodeCompletion.CodeCompletionContext completionContext)
+ {
+ var projectedExtension = GetExtensionAt (completionContext.TriggerOffset);
+ if (projectedExtension == null)
+ return null;
+ return projectedExtension.ShowCodeTemplatesCommand (ConvertContext (completionContext));
+ }
+
+ CodeCompletionContext ConvertContext (CodeCompletionContext completionContext)
+ {
+ var projection = GetProjectionAt (completionContext.TriggerOffset);
+ return ConvertContext (completionContext, projection);
+ }
+
+ static CodeCompletionContext ConvertContext (CodeCompletionContext completionContext, Projection projection)
+ {
+ int offset = completionContext.TriggerOffset;
+ int line = completionContext.TriggerLine;
+ int lineOffset = completionContext.TriggerLineOffset;
+
+ if (projection != null) {
+ foreach (var seg in projection.ProjectedSegments) {
+ if (seg.ContainsOriginal (offset)) {
+ offset = seg.FromOriginalToProjected (offset);
+ var loc = projection.ProjectedEditor.OffsetToLocation (offset);
+ line = loc.Line;
+ lineOffset = loc.Column - 1;
+ }
+ }
+ }
+
+ return new MonoDevelop.Ide.CodeCompletion.CodeCompletionContext {
+ TriggerOffset = offset,
+ TriggerLine = line,
+ TriggerLineOffset = lineOffset,
+ TriggerXCoord = completionContext.TriggerXCoord,
+ TriggerYCoord = completionContext.TriggerYCoord,
+ TriggerTextHeight = completionContext.TriggerTextHeight,
+ TriggerWordLength = completionContext.TriggerWordLength
+ };
+ }
+
+ static CodeCompletionContext ImportContext (CodeCompletionContext completionContext, Projection projection)
+ {
+ int offset = completionContext.TriggerOffset;
+ int line = completionContext.TriggerLine;
+ int lineOffset = completionContext.TriggerLineOffset;
+
+ if (projection != null) {
+ foreach (var seg in projection.ProjectedSegments) {
+ if (seg.ContainsProjected (offset)) {
+ offset = seg.FromProjectedToOriginal (offset);
+ var loc = projection.ProjectedEditor.OffsetToLocation (offset);
+ line = loc.Line;
+ lineOffset = loc.Column - 1;
+ }
+ }
+ }
+
+ return new MonoDevelop.Ide.CodeCompletion.CodeCompletionContext {
+ TriggerOffset = offset,
+ TriggerLine = line,
+ TriggerLineOffset = lineOffset,
+ TriggerXCoord = completionContext.TriggerXCoord,
+ TriggerYCoord = completionContext.TriggerYCoord,
+ TriggerTextHeight = completionContext.TriggerTextHeight,
+ TriggerWordLength = completionContext.TriggerWordLength
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedDocumentContext.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedDocumentContext.cs
new file mode 100644
index 0000000000..4571fefeb8
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedDocumentContext.cs
@@ -0,0 +1,143 @@
+//
+// ProjectedDocumentContext.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2015 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 MonoDevelop.Ide.Editor;
+using System.Collections.Immutable;
+using MonoDevelop.Core.Text;
+using MonoDevelop.Ide.Gui;
+using MonoDevelop.Ide.TypeSystem;
+
+namespace MonoDevelop.Ide.Editor.Projection
+{
+
+ class ProjectedDocumentContext : DocumentContext
+ {
+ DocumentContext originalContext;
+ TextEditor projectedEditor;
+ ParsedDocument parsedDocument;
+
+ public TextEditor ProjectedEditor {
+ get {
+ return projectedEditor;
+ }
+ }
+
+ public DocumentContext OriginalContext {
+ get {
+ return originalContext;
+ }
+ }
+
+ Microsoft.CodeAnalysis.Document projectedDocument;
+
+ public ProjectedDocumentContext (TextEditor projectedEditor, DocumentContext originalContext)
+ {
+ if (projectedEditor == null)
+ throw new ArgumentNullException ("projectedEditor");
+ if (originalContext == null)
+ throw new ArgumentNullException ("originalContext");
+ this.projectedEditor = projectedEditor;
+ this.originalContext = originalContext;
+
+ if (originalContext.Project != null) {
+ var originalProjectId = TypeSystemService.GetProjectId (originalContext.Project);
+ if (originalProjectId != null) {
+ var originalProject = TypeSystemService.Workspace.CurrentSolution.GetProject (originalProjectId);
+ if (originalProject != null) {
+ projectedDocument = originalProject.AddDocument (
+ projectedEditor.FileName,
+ projectedEditor
+ );
+ }
+ }
+ }
+
+ projectedEditor.TextChanged += delegate(object sender, TextChangeEventArgs e) {
+ if (projectedDocument != null)
+ projectedDocument = projectedDocument.WithText (projectedEditor);
+ ReparseDocument ();
+ };
+
+ ReparseDocument ();
+ }
+
+ #region implemented abstract members of DocumentContext
+ public override void AttachToProject (MonoDevelop.Projects.Project project)
+ {
+ }
+
+ public override void ReparseDocument ()
+ {
+ var options = new ParseOptions {
+ FileName = projectedEditor.FileName,
+ Content = projectedEditor,
+ Project = Project,
+ RoslynDocument = projectedDocument
+ };
+ parsedDocument = TypeSystemService.ParseFile (options, projectedEditor.MimeType).Result;
+
+ base.OnDocumentParsed (EventArgs.Empty);
+ }
+
+ public override Microsoft.CodeAnalysis.Options.OptionSet GetOptionSet ()
+ {
+ return originalContext.GetOptionSet ();
+ }
+
+ public override MonoDevelop.Ide.TypeSystem.ParsedDocument UpdateParseDocument ()
+ {
+ ReparseDocument ();
+ return parsedDocument;
+ }
+
+ public override string Name {
+ get {
+ return projectedEditor.FileName;
+ }
+ }
+
+ public override MonoDevelop.Projects.Project Project {
+ get {
+ return originalContext.Project;
+ }
+ }
+
+ public override Microsoft.CodeAnalysis.Document AnalysisDocument {
+ get {
+
+ return projectedDocument;
+ }
+ }
+
+ public override MonoDevelop.Ide.TypeSystem.ParsedDocument ParsedDocument {
+ get {
+ return parsedDocument;
+ }
+ }
+ #endregion
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedFilterCompletionTextEditorExtension.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedFilterCompletionTextEditorExtension.cs
new file mode 100644
index 0000000000..15b3ecfc61
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedFilterCompletionTextEditorExtension.cs
@@ -0,0 +1,186 @@
+//
+// ProjectedFilterCompletionTextEditorExtension.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2015 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.Collections.Generic;
+using MonoDevelop.Ide.Editor.Extension;
+
+namespace MonoDevelop.Ide.Editor.Projection
+{
+ sealed class ProjectedFilterCompletionTextEditorExtension : CompletionTextEditorExtension, IProjectionExtension
+ {
+ CompletionTextEditorExtension completionTextEditorExtension;
+ IReadOnlyList<Projection> projections;
+
+ IReadOnlyList<Projection> IProjectionExtension.Projections {
+ get {
+ return projections;
+ }
+ set {
+ projections = value;
+ }
+ }
+
+ public ProjectedFilterCompletionTextEditorExtension (CompletionTextEditorExtension completionTextEditorExtension, IReadOnlyList<Projection> projections)
+ {
+ this.completionTextEditorExtension = completionTextEditorExtension;
+ this.projections = projections;
+ }
+
+ bool IsInProjection ()
+ {
+ int offset = Editor.CaretOffset;
+ foreach (var p in projections) {
+ foreach (var seg in p.ProjectedSegments) {
+ if (seg.ContainsOriginal (offset))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public override bool KeyPress (KeyDescriptor descriptor)
+ {
+ if (IsInProjection())
+ return Next == null || Next.KeyPress (descriptor);
+ return completionTextEditorExtension.KeyPress (descriptor);
+ }
+
+ public override bool IsValidInContext (DocumentContext context)
+ {
+ if (IsInProjection())
+ return false;
+ return completionTextEditorExtension.IsValidInContext (context);
+ }
+
+ public override int GetCurrentParameterIndex (int startOffset)
+ {
+ if (IsInProjection())
+ return -1;
+ return completionTextEditorExtension.GetCurrentParameterIndex (startOffset);
+ }
+
+ public override string CompletionLanguage {
+ get {
+ return completionTextEditorExtension.CompletionLanguage;
+ }
+ }
+
+ public override void RunCompletionCommand ()
+ {
+ if (IsInProjection ())
+ return;
+ completionTextEditorExtension.RunCompletionCommand ();
+ }
+
+ public override void RunShowCodeTemplatesWindow ()
+ {
+ if (IsInProjection ())
+ return;
+ completionTextEditorExtension.RunShowCodeTemplatesWindow ();
+ }
+
+ public override void RunParameterCompletionCommand ()
+ {
+ if (IsInProjection ())
+ return;
+ completionTextEditorExtension.RunParameterCompletionCommand ();
+ }
+
+ public override bool CanRunCompletionCommand ()
+ {
+ if (IsInProjection ())
+ return false;
+ return completionTextEditorExtension.CanRunCompletionCommand ();
+ }
+
+ public override bool CanRunParameterCompletionCommand ()
+ {
+ if (IsInProjection ())
+ return false;
+ return completionTextEditorExtension.CanRunParameterCompletionCommand ();
+ }
+
+ public override System.Threading.Tasks.Task<CodeCompletion.ICompletionDataList> HandleCodeCompletionAsync (CodeCompletion.CodeCompletionContext completionContext, char completionChar, System.Threading.CancellationToken token)
+ {
+ return completionTextEditorExtension.HandleCodeCompletionAsync (completionContext, completionChar, token);
+ }
+
+ public override System.Threading.Tasks.Task<CodeCompletion.ParameterHintingResult> HandleParameterCompletionAsync (CodeCompletion.CodeCompletionContext completionContext, char completionChar, System.Threading.CancellationToken token)
+ {
+ return completionTextEditorExtension.HandleParameterCompletionAsync (completionContext, completionChar, token);
+ }
+
+ public override bool GetCompletionCommandOffset (out int cpos, out int wlen)
+ {
+ if (IsInProjection ()) {
+ cpos = 0; wlen = 0;
+ return false;
+ }
+ return completionTextEditorExtension.GetCompletionCommandOffset (out cpos, out wlen);
+ }
+
+ public override CodeCompletion.ICompletionDataList ShowCodeSurroundingsCommand (CodeCompletion.CodeCompletionContext completionContext)
+ {
+ if (IsInProjection ()) return null;
+ return completionTextEditorExtension.ShowCodeSurroundingsCommand (completionContext);
+ }
+
+ public override CodeCompletion.ICompletionDataList ShowCodeTemplatesCommand (CodeCompletion.CodeCompletionContext completionContext)
+ {
+ if (IsInProjection ()) return null;
+ return completionTextEditorExtension.ShowCodeTemplatesCommand (completionContext);
+ }
+
+ public override CodeCompletion.ICompletionDataList CodeCompletionCommand (CodeCompletion.CodeCompletionContext completionContext)
+ {
+ if (IsInProjection ()) return null;
+ return completionTextEditorExtension.CodeCompletionCommand (completionContext);
+ }
+
+ public override CodeCompletion.ParameterHintingResult ParameterCompletionCommand (CodeCompletion.CodeCompletionContext completionContext)
+ {
+ if (IsInProjection ()) return null;
+ return completionTextEditorExtension.ParameterCompletionCommand (completionContext);
+ }
+
+ public override int GuessBestMethodOverload (CodeCompletion.ParameterHintingResult provider, int currentOverload)
+ {
+ if (IsInProjection ()) return -1;
+ return completionTextEditorExtension.GuessBestMethodOverload (provider, currentOverload);
+ }
+
+ protected override void Initialize ()
+ {
+ completionTextEditorExtension.InternalInitialize ();
+ }
+
+ public override void Dispose ()
+ {
+ completionTextEditorExtension.Dispose ();
+ }
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedSegment.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedSegment.cs
new file mode 100644
index 0000000000..8a49d40ca3
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedSegment.cs
@@ -0,0 +1,89 @@
+//
+// ProjectedSegment.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2015 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 MonoDevelop.Core.Text;
+
+namespace MonoDevelop.Ide.Editor.Projection
+{
+ public struct ProjectedSegment
+ {
+ public int Offset {
+ get;
+ private set;
+ }
+
+ public int ProjectedOffset {
+ get;
+ private set;
+ }
+
+ public int Length {
+ get;
+ private set;
+ }
+
+ public ProjectedSegment (int offset, int projectedOffset, int length)
+ : this ()
+ {
+ this.Offset = offset;
+ this.ProjectedOffset = projectedOffset;
+ this.Length = length;
+ }
+
+ public bool ContainsOriginal (int offset)
+ {
+ return Offset <= offset && offset < Offset + Length;
+ }
+
+ public bool ContainsProjected (int offset)
+ {
+ return ProjectedOffset <= offset && offset < ProjectedOffset + Length;
+ }
+
+ public bool IsInOriginal (ISegment segment)
+ {
+ if (segment == null)
+ throw new ArgumentNullException ("segment");
+
+ return segment.Contains(Offset) && segment.Contains (Offset + Length);
+ }
+
+ public ISegment FromOriginalToProjected (ISegment segment)
+ {
+ return new TextSegment (segment.Offset - Offset + ProjectedOffset, segment.Length);
+ }
+
+ public int FromOriginalToProjected (int offset)
+ {
+ return offset - Offset + ProjectedOffset;
+ }
+
+ public int FromProjectedToOriginal (int offset)
+ {
+ return offset + Offset - ProjectedOffset;
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedSemanticHighlighting.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedSemanticHighlighting.cs
new file mode 100644
index 0000000000..82ceb984a2
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedSemanticHighlighting.cs
@@ -0,0 +1,98 @@
+//
+// ProjectedSemanticHighlighting.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2015 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 MonoDevelop.Ide.Editor.Highlighting;
+using System.Collections.Generic;
+
+namespace MonoDevelop.Ide.Editor.Projection
+{
+ sealed class ProjectedSemanticHighlighting : SemanticHighlighting
+ {
+ List<Projection> projections;
+
+ public ProjectedSemanticHighlighting (TextEditor editor, DocumentContext documentContext, IEnumerable<Projection> projections) : base (editor, documentContext)
+ {
+ this.projections = new List<Projection> (projections);
+ foreach (var p in this.projections) {
+ if (p.ProjectedEditor.SemanticHighlighting == null)
+ continue;
+ p.ProjectedEditor.SemanticHighlighting.SemanticHighlightingUpdated += HandleSemanticHighlightingUpdated;
+ }
+ }
+
+ public void UpdateProjection (IEnumerable<Projection> projections)
+ {
+ foreach (var p in this.projections) {
+ if (p.ProjectedEditor.SemanticHighlighting == null)
+ continue;
+ p.ProjectedEditor.SemanticHighlighting.SemanticHighlightingUpdated -= HandleSemanticHighlightingUpdated;
+ }
+ this.projections = new List<Projection> (projections);
+ foreach (var p in this.projections) {
+ if (p.ProjectedEditor.SemanticHighlighting == null)
+ continue;
+ p.ProjectedEditor.SemanticHighlighting.SemanticHighlightingUpdated += HandleSemanticHighlightingUpdated;
+ }
+ }
+
+ void HandleSemanticHighlightingUpdated (object sender, EventArgs e)
+ {
+ NotifySemanticHighlightingUpdate ();
+ }
+
+ protected override void DocumentParsed ()
+ {
+ NotifySemanticHighlightingUpdate ();
+ }
+
+ public override IEnumerable<ColoredSegment> GetColoredSegments (MonoDevelop.Core.Text.ISegment segment)
+ {
+ foreach (Projection p in projections) {
+ foreach (var seg in p.ProjectedSegments) {
+ if (seg.IsInOriginal (segment)) {
+ if (p.ProjectedEditor.SemanticHighlighting == null)
+ continue;
+
+ int delta = segment.EndOffset - (seg.Offset + seg.Length);
+ int len = seg.Length;
+ if (delta < 0) {
+ len += delta;
+ }
+ foreach (var cs in p.ProjectedEditor.SemanticHighlighting.GetColoredSegments (new MonoDevelop.Core.Text.TextSegment (seg.ProjectedOffset, len))) {
+ yield return new ColoredSegment (cs.Offset - seg.ProjectedOffset + seg.Offset, cs.Length, cs.ColorStyleKey);
+
+ }
+ foreach (var cs in GetColoredSegments (MonoDevelop.Core.Text.TextSegment.FromBounds (seg.Offset + len, segment.EndOffset)))
+ yield return cs;
+ yield break;
+ }
+ }
+ }
+ }
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedTooltipProvider.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedTooltipProvider.cs
new file mode 100644
index 0000000000..bb50723ae7
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedTooltipProvider.cs
@@ -0,0 +1,95 @@
+//
+// ProjectedTooltipProvider.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2015 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 MonoDevelop.Components;
+
+namespace MonoDevelop.Ide.Editor.Projection
+{
+ sealed class ProjectedTooltipProvider : TooltipProvider
+ {
+ readonly TextEditor editor;
+ readonly DocumentContext ctx;
+ readonly Projection projection;
+ readonly TooltipProvider projectedTooltipProvider;
+
+ public ProjectedTooltipProvider (TextEditor editor, DocumentContext ctx, Projection projection, TooltipProvider projectedTooltipProvider)
+ {
+ if (editor == null)
+ throw new ArgumentNullException ("editor");
+ if (ctx == null)
+ throw new ArgumentNullException ("ctx");
+ if (projection == null)
+ throw new ArgumentNullException ("projection");
+ if (projectedTooltipProvider == null)
+ throw new ArgumentNullException ("projectedTooltipProvider");
+ this.projectedTooltipProvider = projectedTooltipProvider;
+ this.projection = projection;
+ this.editor = editor;
+ this.ctx = ctx;
+ }
+
+ public override TooltipItem GetItem (TextEditor editor, DocumentContext ctx, int offset)
+ {
+ foreach (var pseg in projection.ProjectedSegments) {
+ if (pseg.ContainsOriginal (offset)) {
+ var result = projectedTooltipProvider.GetItem (projection.ProjectedEditor, projection.ProjectedContext, pseg.FromOriginalToProjected (offset));
+ if (result == null)
+ return null;
+ result.Offset = pseg.FromProjectedToOriginal (result.Offset);
+ return result;
+ }
+ }
+ return null;
+ }
+
+ public override bool IsInteractive (TextEditor editor, Control tipWindow)
+ {
+ return projectedTooltipProvider.IsInteractive (editor, tipWindow);
+ }
+
+ public override void ShowTooltipWindow (TextEditor editor, Control tipWindow, TooltipItem item, Gdk.ModifierType modifierState, int mouseX, int mouseY)
+ {
+ projectedTooltipProvider.ShowTooltipWindow (editor, tipWindow, item, modifierState, mouseX, mouseY);
+ }
+
+ public override void GetRequiredPosition (TextEditor editor, Control tipWindow, out int requiredWidth, out double xalign)
+ {
+ projectedTooltipProvider.GetRequiredPosition (editor, tipWindow, out requiredWidth, out xalign);
+ }
+
+ public override Control CreateTooltipWindow (TextEditor editor, DocumentContext ctx, TooltipItem item, int offset, Gdk.ModifierType modifierState)
+ {
+ foreach (var pseg in projection.ProjectedSegments) {
+ if (pseg.ContainsOriginal (offset)) {
+ return projectedTooltipProvider.CreateTooltipWindow (projection.ProjectedEditor, projection.ProjectedContext, item, pseg.FromOriginalToProjected (offset), modifierState);
+ }
+ }
+ return null;
+ }
+
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/Projection.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/Projection.cs
new file mode 100644
index 0000000000..8698e57c4c
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/Projection.cs
@@ -0,0 +1,130 @@
+//
+// Projection.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2015 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 MonoDevelop.Ide.Editor;
+using System.Collections.Immutable;
+using MonoDevelop.Core.Text;
+using MonoDevelop.Ide.Gui;
+using MonoDevelop.Ide.TypeSystem;
+using System.Collections.Generic;
+
+namespace MonoDevelop.Ide.Editor.Projection
+{
+ public sealed class Projection
+ {
+ public ITextDocument Document { get; private set; }
+
+ SegmentTree<ProjectedTreeSegment> originalProjections = new SegmentTree<ProjectedTreeSegment> ();
+ SegmentTree<ProjectedTreeSegment> projectedProjections = new SegmentTree<ProjectedTreeSegment> ();
+
+ class ProjectedTreeSegment : TreeSegment
+ {
+ public ProjectedTreeSegment LinkedTo { get; set; }
+
+ public ProjectedTreeSegment (int offset, int length) : base (offset, length)
+ {
+ }
+ }
+
+ public IEnumerable<ProjectedSegment> ProjectedSegments {
+ get {
+ foreach (var treeSeg in originalProjections) {
+ yield return new ProjectedSegment (treeSeg.Offset, treeSeg.LinkedTo.Offset, treeSeg.Length);
+ }
+ }
+ }
+
+ TextEditor projectedEditor;
+
+ internal TextEditor ProjectedEditor
+ {
+ get
+ {
+ return projectedEditor;
+ }
+ }
+
+ ProjectedDocumentContext projectedDocumentContext;
+ TextEditor attachedEditor;
+
+ internal DocumentContext ProjectedContext {
+ get {
+ return projectedDocumentContext;
+ }
+ }
+
+ public TextEditor CreateProjectedEditor (DocumentContext originalContext)
+ {
+ if (projectedEditor == null) {
+ projectedEditor = TextEditorFactory.CreateNewEditor (Document);
+ projectedDocumentContext = new ProjectedDocumentContext (projectedEditor, originalContext);
+ projectedEditor.InitializeExtensionChain (projectedDocumentContext);
+ projectedProjections.InstallListener (projectedEditor);
+ }
+ return projectedEditor;
+ }
+
+ public Projection (ITextDocument document, IReadOnlyList<ProjectedSegment> projectedSegments)
+ {
+ if (document == null)
+ throw new ArgumentNullException (nameof (document));
+ this.Document = document;
+
+ for (int i = 0; i < projectedSegments.Count; i++) {
+ var p = projectedSegments [i];
+ var original = new ProjectedTreeSegment (p.Offset, p.Length);
+ var projected = new ProjectedTreeSegment (p.ProjectedOffset, p.Length);
+ original.LinkedTo = projected;
+ projected.LinkedTo = original;
+ originalProjections.Add (original);
+ projectedProjections.Add (projected);
+ }
+ }
+
+ internal void Dettach ()
+ {
+ attachedEditor.TextChanged -= HandleTextChanged;
+ }
+
+ internal void Attach (TextEditor textEditor)
+ {
+ attachedEditor = textEditor;
+ attachedEditor.TextChanged += HandleTextChanged;
+ }
+
+ void HandleTextChanged (object sender, TextChangeEventArgs e)
+ {
+ foreach (var segment in originalProjections) {
+ if (segment.Contains (e.Offset)) {
+ var projectedOffset = e.Offset - segment.Offset + segment.LinkedTo.Offset;
+ projectedEditor.ReplaceText (projectedOffset, e.RemovalLength, e.InsertedText);
+ }
+ }
+
+ originalProjections.UpdateOnTextReplace (sender, e);
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/SegmentTree.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/SegmentTree.cs
new file mode 100644
index 0000000000..e22632c0b9
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/SegmentTree.cs
@@ -0,0 +1,801 @@
+//
+// SegmentTree.cs
+//
+// Author:
+// mkrueger <mkrueger@novell.com>
+//
+// Copyright (c) 2011 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.Linq;
+using MonoDevelop.Core.Text;
+using System.Text;
+using System.Web.UI.WebControls;
+using System.Diagnostics;
+
+namespace MonoDevelop.Ide.Editor
+{
+ /// <summary>
+ /// A segment tree contains overlapping segments and get all segments overlapping a segment. It's implemented as a augmented interval tree
+ /// described in Cormen et al. (2001, Section 14.3: Interval trees, pp. 311–317).
+ /// </summary>
+ public class SegmentTree<T> : TextSegmentTree, ICollection<T> where T : TreeSegment
+ {
+ readonly RedBlackTree tree = new RedBlackTree ();
+
+ ITextDocument ownerDocument;
+
+ public int Count {
+ get {
+ return tree.Count;
+ }
+ }
+
+ public IEnumerator<T> GetEnumerator ()
+ {
+ var root = tree.Root;
+ if (root == null)
+ yield break;
+ var node = root.OuterLeft;
+ while (node != null) {
+ yield return (T)node;
+ node = node.NextNode;
+ }
+ }
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
+ {
+ return GetEnumerator ();
+ }
+
+ public bool Contains (T item)
+ {
+ return this.Any (item.Equals);
+ }
+
+ public void CopyTo (T[] array, int arrayIndex)
+ {
+ Debug.Assert (array != null);
+ Debug.Assert (0 <= arrayIndex && arrayIndex < array.Length);
+ int i = arrayIndex;
+ foreach (T value in this)
+ array[i++] = value;
+ }
+
+ bool ICollection<T>.IsReadOnly {
+ get {
+ return false;
+ }
+ }
+
+ public void Add (T item)
+ {
+ InternalAdd (item);
+ }
+
+ public bool Remove (T item)
+ {
+ return InternalRemove (item);
+ }
+
+ public void Clear ()
+ {
+ tree.Clear ();
+ }
+
+ public IEnumerable<T> GetSegmentsAt (int offset)
+ {
+ return GetSegmentsOverlapping (offset, 0);
+ }
+
+ public IEnumerable<T> GetSegmentsOverlapping (ISegment segment)
+ {
+ if (segment.Offset < 0)
+ return Enumerable.Empty<T> ();
+ return GetSegmentsOverlapping (segment.Offset, segment.Length);
+ }
+
+ public IEnumerable<T> GetSegmentsOverlapping (int offset, int length)
+ {
+ if (tree.Root == null)
+ yield break;
+ var intervalStack = new Stack<Interval> ();
+ intervalStack.Push (new Interval (tree.Root, offset, offset + length));
+ while (intervalStack.Count > 0) {
+ var interval = intervalStack.Pop ();
+ if (interval.end < 0)
+ continue;
+
+ var node = interval.node;
+ int nodeStart = interval.start - node.DistanceToPrevNode;
+ int nodeEnd = interval.end - node.DistanceToPrevNode;
+ var leftNode = node.Left;
+ if (leftNode != null) {
+ nodeStart -= leftNode.TotalLength;
+ nodeEnd -= leftNode.TotalLength;
+ }
+
+ if (node.DistanceToMaxEnd < nodeStart)
+ continue;
+
+ if (leftNode != null)
+ intervalStack.Push (new Interval (leftNode, interval.start, interval.end));
+
+ if (nodeEnd < 0)
+ continue;
+
+ if (nodeStart <= node.Length)
+ yield return (T)node;
+
+ var rightNode = node.Right;
+ if (rightNode != null)
+ intervalStack.Push (new Interval (rightNode, nodeStart, nodeEnd));
+ }
+ }
+
+ public void InstallListener (ITextDocument doc)
+ {
+ if (ownerDocument != null)
+ throw new InvalidOperationException ("Segment tree already installed");
+ ownerDocument = doc;
+ doc.TextChanged += UpdateOnTextReplace;
+ }
+
+ public void RemoveListener ()
+ {
+ if (ownerDocument == null)
+ throw new InvalidOperationException ("Segment tree is not installed");
+ ownerDocument.TextChanged -= UpdateOnTextReplace;
+ ownerDocument = null;
+ }
+
+ internal void UpdateOnTextReplace (object sender, TextChangeEventArgs e)
+ {
+ if (e.RemovalLength == 0) {
+ var length = e.InsertionLength;
+ foreach (var segment in GetSegmentsAt (e.Offset).Where (s => s.Offset < e.Offset && e.Offset < s.EndOffset)) {
+ segment.Length += length;
+ segment.UpdateAugmentedData ();
+ }
+ var node = SearchFirstSegmentWithStartAfter (e.Offset);
+ if (node != null) {
+ node.DistanceToPrevNode += length;
+ node.UpdateAugmentedData ();
+ }
+ return;
+ }
+ int delta = e.ChangeDelta;
+ foreach (var segment in new List<T> (GetSegmentsOverlapping (e.Offset, e.RemovalLength))) {
+ if (segment.Offset < e.Offset) {
+ if (segment.EndOffset >= e.Offset + e.RemovalLength) {
+ segment.Length += delta;
+ } else {
+ segment.Length = e.Offset - segment.Offset;
+ }
+ segment.UpdateAugmentedData ();
+ continue;
+ }
+ int remainingLength = segment.EndOffset - (e.Offset + e.RemovalLength);
+ InternalRemove (segment);
+ if (remainingLength > 0) {
+ segment.Offset = e.Offset + e.RemovalLength;
+ segment.Length = remainingLength;
+ InternalAdd (segment);
+ }
+ }
+ var next = SearchFirstSegmentWithStartAfter (e.Offset + 1);
+
+ if (next != null) {
+ next.DistanceToPrevNode += delta;
+ next.UpdateAugmentedData ();
+ }
+ }
+
+ void InternalAdd (TreeSegment node)
+ {
+ if (node == null)
+ throw new ArgumentNullException ("node");
+ if (node.segmentTree != null)
+ throw new InvalidOperationException ("Node already attached.");
+
+ node.segmentTree = this;
+
+
+ int insertionOffset = node.Offset;
+ node.DistanceToMaxEnd = node.Length;
+
+ if (tree.Root == null) {
+ tree.Count = 1;
+ tree.Root = (T)node;
+ node.TotalLength = node.DistanceToPrevNode;
+ return;
+ }
+
+ if (insertionOffset < tree.Root.TotalLength) {
+ var n = SearchNode (ref insertionOffset);
+ node.TotalLength = node.DistanceToPrevNode = insertionOffset;
+ n.DistanceToPrevNode -= insertionOffset;
+ tree.InsertBefore (n, node);
+ return;
+ }
+
+ node.DistanceToPrevNode = node.TotalLength = insertionOffset - tree.Root.TotalLength;
+ tree.InsertRight (tree.Root.OuterRight, node);
+ }
+
+ bool InternalRemove (TreeSegment node)
+ {
+ if (node.segmentTree == null)
+ return false;
+ if (node.segmentTree != this)
+ throw new InvalidOperationException ("Tried to remove tree segment from wrong tree.");
+ var calculatedOffset = node.Offset;
+ var next = node.NextNode;
+ if (next != null)
+ next.DistanceToPrevNode += node.DistanceToPrevNode;
+ tree.Remove (node);
+ if (next != null)
+ next.UpdateAugmentedData ();
+ node.segmentTree = null;
+ node.Parent = node.Left = node.Right = null;
+ node.DistanceToPrevNode = calculatedOffset;
+ return true;
+ }
+
+ TreeSegment SearchFirstSegmentWithStartAfter (int startOffset)
+ {
+ if (tree.Root == null)
+ return null;
+ if (startOffset <= 0)
+ return tree.Root.OuterLeft;
+ var result = SearchNode (ref startOffset);
+ while (startOffset == 0) {
+ var pre = result == null ? tree.Root.OuterRight : result.PrevNode;
+ if (pre == null)
+ return null;
+ startOffset += pre.DistanceToPrevNode;
+ result = pre;
+ }
+ return result;
+ }
+
+ TreeSegment SearchNode (ref int offset)
+ {
+ TreeSegment n = tree.Root;
+ while (true) {
+ if (n.Left != null) {
+ if (offset < n.Left.TotalLength) {
+ n = n.Left;
+ continue;
+ }
+ offset -= n.Left.TotalLength;
+ }
+ if (offset < n.DistanceToPrevNode)
+ return n;
+ offset -= n.DistanceToPrevNode;
+ if (n.Right == null)
+ return null;
+ n = n.Right;
+ }
+ }
+
+ #region TextSegmentTree implementation
+
+ void TextSegmentTree.Add (TreeSegment segment)
+ {
+ InternalAdd (segment);
+ }
+
+ bool TextSegmentTree.Remove (TreeSegment segment)
+ {
+ return InternalRemove (segment);
+ }
+
+ #endregion
+
+ const bool Black = false;
+ const bool Red = true;
+
+ struct Interval
+ {
+ internal TreeSegment node;
+ internal int start, end;
+
+ public Interval (TreeSegment node,int start,int end)
+ {
+ this.node = node;
+ this.start = start;
+ this.end = end;
+ }
+
+ public override string ToString ()
+ {
+ return string.Format ("[Interval: start={0},end={1}]", start, end);
+ }
+ }
+
+ sealed class RedBlackTree
+ {
+ public T Root { get; set; }
+
+ public void InsertBefore (TreeSegment node, TreeSegment newNode)
+ {
+ if (node.Left == null) {
+ InsertLeft (node, newNode);
+ } else {
+ InsertRight (node.Left.OuterRight, newNode);
+ }
+ }
+
+ public void InsertLeft (TreeSegment parentNode, TreeSegment newNode)
+ {
+ parentNode.Left = newNode;
+ newNode.Parent = parentNode;
+ newNode.Color = Red;
+ parentNode.UpdateAugmentedData ();
+ FixTreeOnInsert (newNode);
+ Count++;
+ }
+
+ public void InsertRight (TreeSegment parentNode, TreeSegment newNode)
+ {
+ parentNode.Right = newNode;
+ newNode.Parent = parentNode;
+ newNode.Color = Red;
+ parentNode.UpdateAugmentedData ();
+ FixTreeOnInsert (newNode);
+ Count++;
+ }
+
+ void FixTreeOnInsert (TreeSegment node)
+ {
+ var parent = node.Parent;
+ if (parent == null) {
+ node.Color = Black;
+ return;
+ }
+
+ if (parent.Color == Black)
+ return;
+ var uncle = node.Uncle;
+ TreeSegment grandParent = parent.Parent;
+
+ if (uncle != null && uncle.Color == Red) {
+ parent.Color = Black;
+ uncle.Color = Black;
+ grandParent.Color = Red;
+ FixTreeOnInsert (grandParent);
+ return;
+ }
+
+ if (node == parent.Right && parent == grandParent.Left) {
+ RotateLeft (parent);
+ node = node.Left;
+ } else if (node == parent.Left && parent == grandParent.Right) {
+ RotateRight (parent);
+ node = node.Right;
+ }
+
+ parent = node.Parent;
+ grandParent = parent.Parent;
+
+ parent.Color = Black;
+ grandParent.Color = Red;
+ if (node == parent.Left && parent == grandParent.Left) {
+ RotateRight (grandParent);
+ } else {
+ RotateLeft (grandParent);
+ }
+ }
+
+ void RotateLeft (TreeSegment node)
+ {
+ TreeSegment right = node.Right;
+ Replace (node, right);
+ node.Right = right.Left;
+ if (node.Right != null)
+ node.Right.Parent = node;
+ right.Left = node;
+ node.Parent = right;
+ node.UpdateAugmentedData ();
+ node.Parent.UpdateAugmentedData ();
+ }
+
+ void RotateRight (TreeSegment node)
+ {
+ TreeSegment left = node.Left;
+ Replace (node, left);
+ node.Left = left.Right;
+ if (node.Left != null)
+ node.Left.Parent = node;
+ left.Right = node;
+ node.Parent = left;
+ node.UpdateAugmentedData ();
+ node.Parent.UpdateAugmentedData ();
+ }
+
+ void Replace (TreeSegment oldNode, TreeSegment newNode)
+ {
+ if (newNode != null)
+ newNode.Parent = oldNode.Parent;
+ if (oldNode.Parent == null) {
+ Root = (T)newNode;
+ } else {
+ if (oldNode.Parent.Left == oldNode)
+ oldNode.Parent.Left = newNode;
+ else
+ oldNode.Parent.Right = newNode;
+ oldNode.Parent.UpdateAugmentedData ();
+ }
+ }
+
+ public void Remove (TreeSegment node)
+ {
+ if (node.Left != null && node.Right != null) {
+ var outerLeft = node.Right.OuterLeft;
+ InternalRemove (outerLeft);
+ Replace (node, outerLeft);
+
+ outerLeft.Color = node.Color;
+ outerLeft.Left = node.Left;
+ if (outerLeft.Left != null)
+ outerLeft.Left.Parent = outerLeft;
+
+ outerLeft.Right = node.Right;
+ if (outerLeft.Right != null)
+ outerLeft.Right.Parent = outerLeft;
+ outerLeft.UpdateAugmentedData ();
+ return;
+ }
+ InternalRemove (node);
+ }
+
+ void InternalRemove (TreeSegment node)
+ {
+ if (node.Left != null && node.Right != null) {
+ var outerLeft = node.Right.OuterLeft;
+ InternalRemove (outerLeft);
+ Replace (node, outerLeft);
+
+ outerLeft.Color = node.Color;
+ outerLeft.Left = node.Left;
+ if (outerLeft.Left != null)
+ outerLeft.Left.Parent = outerLeft;
+
+ outerLeft.Right = node.Right;
+ if (outerLeft.Right != null)
+ outerLeft.Right.Parent = outerLeft;
+ outerLeft.UpdateAugmentedData ();
+ return;
+ }
+ Count--;
+ // node has only one child
+ TreeSegment child = node.Left ?? node.Right;
+
+ Replace (node, child);
+
+ if (node.Color == Black && child != null) {
+ if (child.Color == Red) {
+ child.Color = Black;
+ } else {
+ DeleteOneChild (child);
+ }
+ }
+ }
+
+ static bool GetColorSafe (TreeSegment node)
+ {
+ return node != null ? node.Color : Black;
+ }
+
+ void DeleteOneChild (TreeSegment node)
+ {
+ // case 1
+ if (node == null || node.Parent == null)
+ return;
+
+ var parent = node.Parent;
+ var sibling = node.Sibling;
+ if (sibling == null)
+ return;
+
+ // case 2
+ if (sibling.Color == Red) {
+ parent.Color = Red;
+ sibling.Color = Black;
+ if (node == parent.Left) {
+ RotateLeft (parent);
+ } else {
+ RotateRight (parent);
+ }
+ sibling = node.Sibling;
+ if (sibling == null)
+ return;
+ }
+
+ // case 3
+ if (parent.Color == Black && sibling.Color == Black && GetColorSafe (sibling.Left) == Black && GetColorSafe (sibling.Right) == Black) {
+ sibling.Color = Red;
+ DeleteOneChild (parent);
+ return;
+ }
+
+ // case 4
+ if (parent.Color == Red && sibling.Color == Black && GetColorSafe (sibling.Left) == Black && GetColorSafe (sibling.Right) == Black) {
+ sibling.Color = Red;
+ parent.Color = Black;
+ return;
+ }
+
+ // case 5
+ if (node == parent.Left && sibling.Color == Black && GetColorSafe (sibling.Left) == Red && GetColorSafe (sibling.Right) == Black) {
+ sibling.Color = Red;
+ if (sibling.Left != null)
+ sibling.Left.Color = Black;
+ RotateRight (sibling);
+ } else if (node == parent.Right && sibling.Color == Black && GetColorSafe (sibling.Right) == Red && GetColorSafe (sibling.Left) == Black) {
+ sibling.Color = Red;
+ if (sibling.Right != null)
+ sibling.Right.Color = Black;
+ RotateLeft (sibling);
+ }
+
+ // case 6
+ sibling = node.Sibling;
+ if (sibling == null)
+ return;
+ sibling.Color = parent.Color;
+ parent.Color = Black;
+ if (node == parent.Left) {
+ if (sibling.Right != null)
+ sibling.Right.Color = Black;
+ RotateLeft (parent);
+ } else {
+ if (sibling.Left != null)
+ sibling.Left.Color = Black;
+ RotateRight (parent);
+ }
+ }
+
+ public int Count { get; set; }
+
+ public void Clear ()
+ {
+ Root = null;
+ Count = 0;
+ }
+
+ static string GetIndent (int level)
+ {
+ return new String ('\t', level);
+ }
+
+ static void AppendNode (StringBuilder builder, TreeSegment node, int indent)
+ {
+ builder.Append (GetIndent (indent) + "Node (" + (node.Color == Red ? "r" : "b") + "):" + node + Environment.NewLine);
+ builder.Append (GetIndent (indent) + "Left: ");
+ if (node.Left != null) {
+ builder.Append (Environment.NewLine);
+ AppendNode (builder, node.Left, indent + 1);
+ } else {
+ builder.Append ("null");
+ }
+
+ builder.Append (Environment.NewLine);
+ builder.Append (GetIndent (indent) + "Right: ");
+ if (node.Right != null) {
+ builder.Append (Environment.NewLine);
+ AppendNode (builder, node.Right, indent + 1);
+ } else {
+ builder.Append ("null");
+ }
+ }
+
+ public override string ToString ()
+ {
+ if (Root == null)
+ return "<null>";
+ var result = new StringBuilder ();
+ AppendNode (result, Root, 0);
+ return result.ToString ();
+ }
+ }
+ }
+
+ interface TextSegmentTree
+ {
+ void Add (TreeSegment segment);
+ bool Remove (TreeSegment segment);
+ }
+
+ public class TreeSegment : ISegment
+ {
+ public int Offset {
+ get {
+ if (segmentTree == null)
+ return DistanceToPrevNode;
+
+ var curNode = this;
+ int offset = curNode.DistanceToPrevNode;
+ if (curNode.Left != null)
+ offset += curNode.Left.TotalLength;
+ while (curNode.Parent != null) {
+ if (curNode == curNode.Parent.Right) {
+ if (curNode.Parent.Left != null)
+ offset += curNode.Parent.Left.TotalLength;
+ offset += curNode.Parent.DistanceToPrevNode;
+ }
+ curNode = curNode.Parent;
+ }
+ return offset;
+ }
+ set {
+ if (segmentTree != null)
+ segmentTree.Remove (this);
+ DistanceToPrevNode = value;
+ if (segmentTree != null)
+ segmentTree.Add (this);
+ }
+ }
+
+ public int Length {
+ get;
+ set;
+ }
+
+ public int EndOffset {
+ get {
+ return Offset + Length;
+ }
+ }
+
+ protected TreeSegment ()
+ {
+ }
+
+ public TreeSegment (int offset, int length)
+ {
+ Offset = offset;
+ Length = length;
+ }
+
+ public TreeSegment (ISegment segment) : this (segment.Offset, segment.Length)
+ {
+ }
+
+ #region Internal API
+ internal TextSegmentTree segmentTree;
+ internal TreeSegment Parent, Left, Right;
+ internal bool Color;
+
+ // TotalLength = DistanceToPrevNode + Left.DistanceToPrevNode + Right.DistanceToPrevNode
+ internal int TotalLength;
+
+ internal int DistanceToPrevNode;
+
+ // DistanceToMaxEnd = Max (Length, left.DistanceToMaxEnd + Max (left.Offset, right.Offset) - Offset)
+ internal int DistanceToMaxEnd;
+
+ internal void UpdateAugmentedData ()
+ {
+ int totalLength = DistanceToPrevNode;
+ int distanceToMaxEnd = Length;
+
+ if (Left != null) {
+ totalLength += Left.TotalLength;
+ int leftdistance = Left.DistanceToMaxEnd - DistanceToPrevNode;
+ if (Left.Right != null)
+ leftdistance -= Left.Right.TotalLength;
+ if (leftdistance > distanceToMaxEnd)
+ distanceToMaxEnd = leftdistance;
+ }
+
+ if (Right != null) {
+ totalLength += Right.TotalLength;
+ int rightdistance = Right.DistanceToMaxEnd + Right.DistanceToPrevNode;
+ if (Right.Left != null)
+ rightdistance += Right.Left.TotalLength;
+ if (rightdistance > distanceToMaxEnd)
+ distanceToMaxEnd = rightdistance;
+ }
+
+ if (TotalLength != totalLength || DistanceToMaxEnd != distanceToMaxEnd) {
+ TotalLength = totalLength;
+ DistanceToMaxEnd = distanceToMaxEnd;
+ if (Parent != null)
+ Parent.UpdateAugmentedData ();
+ }
+ }
+
+ internal TreeSegment Sibling {
+ get {
+ if (Parent == null)
+ return null;
+ return this == Parent.Left ? Parent.Right : Parent.Left;
+ }
+ }
+
+ internal TreeSegment OuterLeft {
+ get {
+ TreeSegment result = this;
+ while (result.Left != null)
+ result = result.Left;
+ return result;
+ }
+ }
+
+ internal TreeSegment OuterRight {
+ get {
+ TreeSegment result = this;
+ while (result.Right != null) {
+ result = result.Right;
+ }
+ return result;
+ }
+ }
+
+ internal TreeSegment Grandparent {
+ get {
+ return Parent != null ? Parent.Parent : null;
+ }
+ }
+
+ internal TreeSegment Uncle {
+ get {
+ TreeSegment grandparent = Grandparent;
+ if (grandparent == null)
+ return null;
+ return Parent == grandparent.Left ? grandparent.Right : grandparent.Left;
+ }
+ }
+
+ internal TreeSegment NextNode {
+ get {
+ if (Right == null) {
+ TreeSegment curNode = this;
+ TreeSegment oldNode;
+ do {
+ oldNode = curNode;
+ curNode = curNode.Parent;
+ } while (curNode != null && curNode.Right == oldNode);
+ return curNode;
+ }
+ return Right.OuterLeft;
+ }
+ }
+
+ internal TreeSegment PrevNode {
+ get {
+ if (Left == null) {
+ TreeSegment curNode = this;
+ TreeSegment oldNode;
+ do {
+ oldNode = curNode;
+ curNode = curNode.Parent;
+ } while (curNode != null && curNode.Left == oldNode);
+ return curNode;
+ }
+ return Left.OuterRight;
+ }
+ }
+ #endregion
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/SelectionMode.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/SelectionMode.cs
new file mode 100644
index 0000000000..71b981f189
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/SelectionMode.cs
@@ -0,0 +1,33 @@
+//
+// SelectionMode.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.
+
+namespace MonoDevelop.Ide.Editor
+{
+ public enum SelectionMode {
+ Normal,
+ Block
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditor.cs
new file mode 100644
index 0000000000..444f64fd04
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditor.cs
@@ -0,0 +1,1260 @@
+//
+// ITextEditor.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 MonoDevelop.Core.Text;
+using System.Collections.Generic;
+using System.Text;
+using MonoDevelop.Ide.Gui;
+using MonoDevelop.Ide.Editor.Extension;
+using System.IO;
+using MonoDevelop.Ide.Editor.Highlighting;
+using Mono.Addins;
+using MonoDevelop.Core;
+using MonoDevelop.Ide.Extensions;
+using System.Linq;
+using MonoDevelop.Components;
+using System.ComponentModel;
+using MonoDevelop.Ide.TypeSystem;
+using System.Threading;
+using MonoDevelop.Ide.Editor.Projection;
+
+namespace MonoDevelop.Ide.Editor
+{
+ public sealed class TextEditor : Control, ITextDocument, IDisposable
+ {
+ readonly ITextEditorImpl textEditorImpl;
+ IReadonlyTextDocument ReadOnlyTextDocument { get { return textEditorImpl.Document; } }
+ ITextDocument ReadWriteTextDocument { get { return (ITextDocument)textEditorImpl.Document; } }
+
+ public ITextSourceVersion Version {
+ get {
+ return ReadOnlyTextDocument.Version;
+ }
+ }
+
+ FileTypeCondition fileTypeCondition = new FileTypeCondition ();
+
+ List<TooltipExtensionNode> allProviders = new List<TooltipExtensionNode> ();
+
+ void OnTooltipProviderChanged (object s, ExtensionNodeEventArgs a)
+ {
+ TooltipProvider provider;
+ try {
+ var extensionNode = a.ExtensionNode as TooltipExtensionNode;
+ allProviders.Add (extensionNode);
+ if (extensionNode.IsValidFor (MimeType))
+ return;
+ provider = (TooltipProvider) extensionNode.CreateInstance ();
+ } catch (Exception e) {
+ LoggingService.LogError ("Can't create tooltip provider:"+ a.ExtensionNode, e);
+ return;
+ }
+ if (a.Change == ExtensionChange.Add) {
+ textEditorImpl.AddTooltipProvider (provider);
+ } else {
+ textEditorImpl.RemoveTooltipProvider (provider);
+ }
+ }
+
+ public event EventHandler SelectionChanged {
+ add { textEditorImpl.SelectionChanged += value; }
+ remove { textEditorImpl.SelectionChanged -= value; }
+ }
+
+ public event EventHandler CaretPositionChanged {
+ add { textEditorImpl.CaretPositionChanged += value; }
+ remove { textEditorImpl.CaretPositionChanged -= value; }
+ }
+
+ public event EventHandler BeginAtomicUndoOperation {
+ add { textEditorImpl.BeginAtomicUndoOperation += value; }
+ remove { textEditorImpl.BeginAtomicUndoOperation -= value; }
+ }
+
+ public event EventHandler EndAtomicUndoOperation {
+ add { textEditorImpl.EndAtomicUndoOperation += value; }
+ remove { textEditorImpl.EndAtomicUndoOperation -= value; }
+ }
+
+ public event EventHandler<TextChangeEventArgs> TextChanging {
+ add { ReadWriteTextDocument.TextChanging += value; }
+ remove { ReadWriteTextDocument.TextChanging -= value; }
+ }
+
+ public event EventHandler<TextChangeEventArgs> TextChanged {
+ add { ReadWriteTextDocument.TextChanged += value; }
+ remove { ReadWriteTextDocument.TextChanged -= value; }
+ }
+
+ public event EventHandler BeginMouseHover {
+ add { textEditorImpl.BeginMouseHover += value; }
+ remove { textEditorImpl.BeginMouseHover -= value; }
+ }
+
+ public event EventHandler VAdjustmentChanged {
+ add { textEditorImpl.VAdjustmentChanged += value; }
+ remove { textEditorImpl.VAdjustmentChanged -= value; }
+ }
+
+ public event EventHandler HAdjustmentChanged {
+ add { textEditorImpl.HAdjustmentChanged += value; }
+ remove { textEditorImpl.HAdjustmentChanged -= value; }
+ }
+ public char this[int offset] {
+ get {
+ return ReadOnlyTextDocument [offset];
+ }
+ set {
+ ReadWriteTextDocument [offset] = value;
+ }
+ }
+
+// public event EventHandler<LineEventArgs> LineChanged {
+// add { textEditorImpl.LineChanged += value; }
+// remove { textEditorImpl.LineChanged -= value; }
+// }
+//
+// public event EventHandler<LineEventArgs> LineInserted {
+// add { textEditorImpl.LineInserted += value; }
+// remove { textEditorImpl.LineInserted -= value; }
+// }
+//
+// public event EventHandler<LineEventArgs> LineRemoved {
+// add { textEditorImpl.LineRemoved += value; }
+// remove { textEditorImpl.LineRemoved -= value; }
+// }
+
+ public ITextEditorOptions Options {
+ get {
+ return textEditorImpl.Options;
+ }
+ set {
+ textEditorImpl.Options = value;
+ OnOptionsChanged (EventArgs.Empty);
+ }
+ }
+
+ public event EventHandler OptionsChanged;
+
+ void OnOptionsChanged (EventArgs e)
+ {
+ var handler = OptionsChanged;
+ if (handler != null)
+ handler (this, e);
+ }
+
+ public EditMode EditMode {
+ get {
+ return textEditorImpl.EditMode;
+ }
+ }
+
+ public DocumentLocation CaretLocation {
+ get {
+ return textEditorImpl.CaretLocation;
+ }
+ set {
+ textEditorImpl.CaretLocation = value;
+ }
+ }
+
+ public SemanticHighlighting SemanticHighlighting {
+ get {
+ return textEditorImpl.SemanticHighlighting;
+ }
+ set {
+ textEditorImpl.SemanticHighlighting = value;
+ }
+ }
+
+ public int CaretLine {
+ get {
+ return CaretLocation.Line;
+ }
+ set {
+ CaretLocation = new DocumentLocation (value, CaretColumn);
+ }
+ }
+
+ public int CaretColumn {
+ get {
+ return CaretLocation.Column;
+ }
+ set {
+ CaretLocation = new DocumentLocation (CaretLine, value);
+ }
+ }
+
+ public int CaretOffset {
+ get {
+ return textEditorImpl.CaretOffset;
+ }
+ set {
+ textEditorImpl.CaretOffset = value;
+ }
+ }
+
+ public bool IsReadOnly {
+ get {
+ return ReadOnlyTextDocument.IsReadOnly;
+ }
+ set {
+ ReadWriteTextDocument.IsReadOnly = value;
+ }
+ }
+
+ public bool IsSomethingSelected {
+ get {
+ return textEditorImpl.IsSomethingSelected;
+ }
+ }
+
+ public SelectionMode SelectionMode {
+ get {
+ return textEditorImpl.SelectionMode;
+ }
+ }
+
+ public ISegment SelectionRange {
+ get {
+ return textEditorImpl.SelectionRange;
+ }
+ set {
+ textEditorImpl.SelectionRange = value;
+ }
+ }
+
+ public DocumentRegion SelectionRegion {
+ get {
+ return textEditorImpl.SelectionRegion;
+ }
+ set {
+ textEditorImpl.SelectionRegion = value;
+ }
+ }
+
+ public int SelectionAnchorOffset {
+ get {
+ return textEditorImpl.SelectionAnchorOffset;
+ }
+ set {
+ textEditorImpl.SelectionAnchorOffset = value;
+ }
+ }
+
+ public int SelectionLeadOffset {
+ get {
+ return textEditorImpl.SelectionLeadOffset;
+ }
+ set {
+ textEditorImpl.SelectionLeadOffset = value;
+ }
+ }
+
+ public string SelectedText {
+ get {
+ return IsSomethingSelected ? ReadOnlyTextDocument.GetTextAt (SelectionRange) : null;
+ }
+ set {
+ var selection = SelectionRange;
+ ReplaceText (selection, value);
+ SelectionRange = new TextSegment (selection.Offset, value.Length);
+ }
+ }
+
+ public bool IsInAtomicUndo {
+ get {
+ return ReadWriteTextDocument.IsInAtomicUndo;
+ }
+ }
+
+ public double LineHeight {
+ get {
+ return textEditorImpl.LineHeight;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the type of the MIME.
+ /// </summary>
+ /// <value>The type of the MIME.</value>
+ public string MimeType {
+ get {
+ return ReadOnlyTextDocument.MimeType;
+ }
+ set {
+ ReadWriteTextDocument.MimeType = value;
+ }
+ }
+
+ public event EventHandler MimeTypeChanged {
+ add { ReadWriteTextDocument.MimeTypeChanged += value; }
+ remove { ReadWriteTextDocument.MimeTypeChanged -= value; }
+ }
+
+ public string Text {
+ get {
+ return ReadOnlyTextDocument.Text;
+ }
+ set {
+ ReadWriteTextDocument.Text = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets the eol marker. On a text editor always use that and not GetEolMarker.
+ /// The EOL marker of the document may get overwritten my the one from the options.
+ /// </summary>
+ public string EolMarker {
+ get {
+ if (Options.OverrideDocumentEolMarker)
+ return Options.DefaultEolMarker;
+ return ReadOnlyTextDocument.GetEolMarker ();
+ }
+ }
+
+ public bool UseBOM {
+ get {
+ return ReadOnlyTextDocument.UseBOM;
+ }
+ set {
+ ReadWriteTextDocument.UseBOM = value;
+ }
+ }
+
+ public Encoding Encoding {
+ get {
+ return ReadOnlyTextDocument.Encoding;
+ }
+ set {
+ ReadWriteTextDocument.Encoding = value;
+ }
+ }
+
+ public int LineCount {
+ get {
+ return ReadOnlyTextDocument.LineCount;
+ }
+ }
+
+ /// <summary>
+ /// Gets the name of the file the document is stored in.
+ /// Could also be a non-existent dummy file name or null if no name has been set.
+ /// </summary>
+ public FilePath FileName {
+ get {
+ return ReadOnlyTextDocument.FileName;
+ }
+ set {
+ ReadWriteTextDocument.FileName = value;
+ }
+ }
+
+ public event EventHandler FileNameChanged {
+ add {
+ ReadWriteTextDocument.FileNameChanged += value;
+ }
+ remove {
+ ReadWriteTextDocument.FileNameChanged -= value;
+ }
+ }
+
+ public int Length {
+ get {
+ return ReadOnlyTextDocument.Length;
+ }
+ }
+
+ public double ZoomLevel {
+ get {
+ return textEditorImpl.ZoomLevel;
+ }
+ set {
+ textEditorImpl.ZoomLevel = value;
+ }
+ }
+
+ public event EventHandler ZoomLevelChanged {
+ add {
+ textEditorImpl.ZoomLevelChanged += value;
+ }
+ remove {
+ textEditorImpl.ZoomLevelChanged -= value;
+ }
+ }
+
+ public IDisposable OpenUndoGroup ()
+ {
+ return ReadWriteTextDocument.OpenUndoGroup ();
+ }
+
+ public void SetSelection (int anchorOffset, int leadOffset)
+ {
+ textEditorImpl.SetSelection (anchorOffset, leadOffset);
+ }
+
+ public void SetSelection (DocumentLocation anchor, DocumentLocation lead)
+ {
+ SetSelection (LocationToOffset (anchor), LocationToOffset (lead));
+ }
+
+ public void SetCaretLocation (DocumentLocation location, bool usePulseAnimation = false)
+ {
+ CaretLocation = location;
+ ScrollTo (CaretLocation);
+ if (usePulseAnimation)
+ StartCaretPulseAnimation ();
+ }
+
+ public void SetCaretLocation (int line, int col, bool usePulseAnimation = false)
+ {
+ CaretLocation = new DocumentLocation (line, col);
+ CenterTo (CaretLocation);
+ if (usePulseAnimation)
+ StartCaretPulseAnimation ();
+ }
+
+ public void ClearSelection ()
+ {
+ textEditorImpl.ClearSelection ();
+ }
+
+ public void CenterToCaret ()
+ {
+ textEditorImpl.CenterToCaret ();
+ }
+
+ public void StartCaretPulseAnimation ()
+ {
+ textEditorImpl.StartCaretPulseAnimation ();
+ }
+
+ public int EnsureCaretIsNotVirtual ()
+ {
+ return textEditorImpl.EnsureCaretIsNotVirtual ();
+ }
+
+ public void FixVirtualIndentation ()
+ {
+ textEditorImpl.FixVirtualIndentation ();
+ }
+
+ public void RunWhenLoaded (Action action)
+ {
+ if (action == null)
+ throw new ArgumentNullException ("action");
+ textEditorImpl.RunWhenLoaded (action);
+ }
+
+ public string FormatString (DocumentLocation insertPosition, string code)
+ {
+ return textEditorImpl.FormatString (LocationToOffset (insertPosition), code);
+ }
+
+ public string FormatString (int offset, string code)
+ {
+ return textEditorImpl.FormatString (offset, code);
+ }
+
+ public void StartInsertionMode (InsertionModeOptions insertionModeOptions)
+ {
+ if (insertionModeOptions == null)
+ throw new ArgumentNullException ("insertionModeOptions");
+ textEditorImpl.StartInsertionMode (insertionModeOptions);
+ }
+
+ public void StartTextLinkMode (TextLinkModeOptions textLinkModeOptions)
+ {
+ if (textLinkModeOptions == null)
+ throw new ArgumentNullException ("textLinkModeOptions");
+ textEditorImpl.StartTextLinkMode (textLinkModeOptions);
+ }
+
+ public void InsertAtCaret (string text)
+ {
+ InsertText (CaretOffset, text);
+ }
+
+ public DocumentLocation PointToLocation (double xp, double yp, bool endAtEol = false)
+ {
+ return textEditorImpl.PointToLocation (xp, yp, endAtEol);
+ }
+
+ public Xwt.Point LocationToPoint (DocumentLocation location)
+ {
+ return textEditorImpl.LocationToPoint (location.Line, location.Column);
+ }
+
+ public Xwt.Point LocationToPoint (int line, int column)
+ {
+ return textEditorImpl.LocationToPoint (line, column);
+ }
+
+ public string GetLineText (int line, bool includeDelimiter = false)
+ {
+ var segment = GetLine (line);
+ return GetTextAt (includeDelimiter ? segment.SegmentIncludingDelimiter : segment);
+ }
+
+ public int LocationToOffset (int line, int column)
+ {
+ return ReadOnlyTextDocument.LocationToOffset (new DocumentLocation (line, column));
+ }
+
+ public int LocationToOffset (DocumentLocation location)
+ {
+ return ReadOnlyTextDocument.LocationToOffset (location);
+ }
+
+ public DocumentLocation OffsetToLocation (int offset)
+ {
+ return ReadOnlyTextDocument.OffsetToLocation (offset);
+ }
+
+ public void InsertText (int offset, string text)
+ {
+ ReadWriteTextDocument.InsertText (offset, text);
+ }
+
+ public void InsertText (int offset, ITextSource text)
+ {
+ ReadWriteTextDocument.InsertText (offset, text);
+ }
+
+ public void RemoveText (int offset, int count)
+ {
+ RemoveText (new TextSegment (offset, count));
+ }
+
+ public void RemoveText (ISegment segment)
+ {
+ if (segment == null)
+ throw new ArgumentNullException ("segment");
+ ReadWriteTextDocument.RemoveText (segment);
+ }
+
+ public void ReplaceText (int offset, int count, string value)
+ {
+ ReadWriteTextDocument.ReplaceText (offset, count, value);
+ }
+
+ public void ReplaceText (int offset, int count, ITextSource value)
+ {
+ ReadWriteTextDocument.ReplaceText (offset, count, value);
+ }
+
+ public void ReplaceText (ISegment segment, string value)
+ {
+ if (segment == null)
+ throw new ArgumentNullException ("segment");
+ ReadWriteTextDocument.ReplaceText (segment.Offset, segment.Length, value);
+ }
+
+ public void ReplaceText (ISegment segment, ITextSource value)
+ {
+ if (segment == null)
+ throw new ArgumentNullException ("segment");
+ ReadWriteTextDocument.ReplaceText (segment.Offset, segment.Length, value);
+ }
+
+ public IDocumentLine GetLine (int lineNumber)
+ {
+ return ReadOnlyTextDocument.GetLine (lineNumber);
+ }
+
+ public IDocumentLine GetLineByOffset (int offset)
+ {
+ return ReadOnlyTextDocument.GetLineByOffset (offset);
+ }
+
+ public int OffsetToLineNumber (int offset)
+ {
+ return ReadOnlyTextDocument.OffsetToLineNumber (offset);
+ }
+
+ public void AddMarker (IDocumentLine line, ITextLineMarker lineMarker)
+ {
+ if (line == null)
+ throw new ArgumentNullException ("line");
+ if (lineMarker == null)
+ throw new ArgumentNullException ("lineMarker");
+ textEditorImpl.AddMarker (line, lineMarker);
+ }
+
+ public void AddMarker (int lineNumber, ITextLineMarker lineMarker)
+ {
+ if (lineMarker == null)
+ throw new ArgumentNullException ("lineMarker");
+ AddMarker (GetLine (lineNumber), lineMarker);
+ }
+
+ public void RemoveMarker (ITextLineMarker lineMarker)
+ {
+ if (lineMarker == null)
+ throw new ArgumentNullException ("lineMarker");
+ textEditorImpl.RemoveMarker (lineMarker);
+ }
+
+ public IEnumerable<ITextLineMarker> GetLineMarkers (IDocumentLine line)
+ {
+ if (line == null)
+ throw new ArgumentNullException ("line");
+ return textEditorImpl.GetLineMarkers (line);
+ }
+
+ public IEnumerable<ITextSegmentMarker> GetTextSegmentMarkersAt (ISegment segment)
+ {
+ if (segment == null)
+ throw new ArgumentNullException ("segment");
+ return textEditorImpl.GetTextSegmentMarkersAt (segment);
+ }
+
+ public IEnumerable<ITextSegmentMarker> GetTextSegmentMarkersAt (int offset)
+ {
+ return textEditorImpl.GetTextSegmentMarkersAt (offset);
+ }
+
+ public void AddMarker (ITextSegmentMarker marker)
+ {
+ if (marker == null)
+ throw new ArgumentNullException ("marker");
+ textEditorImpl.AddMarker (marker);
+ }
+
+ public bool RemoveMarker (ITextSegmentMarker marker)
+ {
+ if (marker == null)
+ throw new ArgumentNullException ("marker");
+ return textEditorImpl.RemoveMarker (marker);
+ }
+
+ public void SetFoldings (IEnumerable<IFoldSegment> foldings)
+ {
+ if (foldings == null)
+ throw new ArgumentNullException ("foldings");
+ textEditorImpl.SetFoldings (foldings);
+ }
+
+
+ public IEnumerable<IFoldSegment> GetFoldingsContaining (int offset)
+ {
+ return textEditorImpl.GetFoldingsContaining (offset);
+ }
+
+ public IEnumerable<IFoldSegment> GetFoldingsIn (ISegment segment)
+ {
+ if (segment == null)
+ throw new ArgumentNullException ("segment");
+ return textEditorImpl.GetFoldingsIn (segment.Offset, segment.Length);
+ }
+
+ /// <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>
+ public char GetCharAt (int offset)
+ {
+ return ReadOnlyTextDocument.GetCharAt (offset);
+ }
+
+ public string GetTextAt (int offset, int length)
+ {
+ return ReadOnlyTextDocument.GetTextAt (offset, length);
+ }
+
+ public string GetTextAt (ISegment segment)
+ {
+ if (segment == null)
+ throw new ArgumentNullException ("segment");
+ return ReadOnlyTextDocument.GetTextAt (segment);
+ }
+
+ public IReadonlyTextDocument CreateDocumentSnapshot ()
+ {
+ return ReadWriteTextDocument.CreateDocumentSnapshot ();
+ }
+
+ public string GetVirtualIndentationString (int lineNumber)
+ {
+ if (lineNumber < 1 || lineNumber > LineCount)
+ throw new ArgumentOutOfRangeException ("lineNumber");
+ return textEditorImpl.GetVirtualIndentationString (lineNumber);
+ }
+
+ public string GetVirtualIndentationString (IDocumentLine line)
+ {
+ if (line == null)
+ throw new ArgumentNullException ("line");
+ return textEditorImpl.GetVirtualIndentationString (line.LineNumber);
+ }
+
+ public int GetVirtualIndentationColumn (int lineNumber)
+ {
+ if (lineNumber < 1 || lineNumber > LineCount)
+ throw new ArgumentOutOfRangeException ("lineNumber");
+ return 1 + textEditorImpl.GetVirtualIndentationString (lineNumber).Length;
+ }
+
+ public int GetVirtualIndentationColumn (IDocumentLine line)
+ {
+ if (line == null)
+ throw new ArgumentNullException ("line");
+ return 1 + textEditorImpl.GetVirtualIndentationString (line.LineNumber).Length;
+ }
+
+ public TextReader CreateReader ()
+ {
+ return ReadOnlyTextDocument.CreateReader ();
+ }
+
+ public TextReader CreateReader (int offset, int length)
+ {
+ return ReadOnlyTextDocument.CreateReader (offset, length);
+ }
+
+ public ITextSource CreateSnapshot ()
+ {
+ return ReadOnlyTextDocument.CreateSnapshot ();
+ }
+
+ public ITextSource CreateSnapshot (int offset, int length)
+ {
+ return ReadOnlyTextDocument.CreateSnapshot (offset, length);
+ }
+
+ public ITextSource CreateSnapshot (ISegment segment)
+ {
+ if (segment == null)
+ throw new ArgumentNullException ("segment");
+ return ReadOnlyTextDocument.CreateSnapshot (segment.Offset, segment.Length);
+ }
+
+ public void WriteTextTo (TextWriter writer)
+ {
+ if (writer == null)
+ throw new ArgumentNullException ("writer");
+ ReadOnlyTextDocument.WriteTextTo (writer);
+ }
+
+ public void WriteTextTo (TextWriter writer, int offset, int length)
+ {
+ if (writer == null)
+ throw new ArgumentNullException ("writer");
+ ReadOnlyTextDocument.WriteTextTo (writer, offset, length);
+ }
+
+ public void ScrollTo (int offset)
+ {
+ textEditorImpl.ScrollTo (offset);
+ }
+
+ public void ScrollTo (DocumentLocation loc)
+ {
+ ScrollTo (LocationToOffset (loc));
+ }
+
+ public void CenterTo (int offset)
+ {
+ textEditorImpl.CenterTo (offset);
+ }
+
+ public void CenterTo (DocumentLocation loc)
+ {
+ CenterTo (LocationToOffset (loc));
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ public void SetIndentationTracker (IndentationTracker indentationTracker)
+ {
+ textEditorImpl.SetIndentationTracker (indentationTracker);
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ public void SetSelectionSurroundingProvider (SelectionSurroundingProvider surroundingProvider)
+ {
+ textEditorImpl.SetSelectionSurroundingProvider (surroundingProvider);
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ public void SetTextPasteHandler (TextPasteHandler textPasteHandler)
+ {
+ textEditorImpl.SetTextPasteHandler (textPasteHandler);
+ }
+
+ public IList<SkipChar> SkipChars {
+ get {
+ return textEditorImpl.SkipChars;
+ }
+ }
+
+ /// <summary>
+ /// Skip chars are
+ /// </summary>
+ public void AddSkipChar (int offset, char ch)
+ {
+ textEditorImpl.AddSkipChar (offset, ch);
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ if (disposing) {
+ DetachExtensionChain ();
+ textEditorImpl.Dispose ();
+ }
+ base.Dispose (disposing);
+ }
+
+ protected override object CreateNativeWidget ()
+ {
+ return textEditorImpl.CreateNativeControl ();
+ }
+
+ #region Internal API
+ ExtensionContext extensionContext;
+
+ internal ExtensionContext ExtensionContext {
+ get {
+ return extensionContext;
+ }
+ set {
+ if (extensionContext != null) {
+ extensionContext.RemoveExtensionNodeHandler ("MonoDevelop/SourceEditor2/TooltipProviders", OnTooltipProviderChanged);
+// textEditorImpl.ClearTooltipProviders ();
+ }
+ extensionContext = value;
+ if (extensionContext != null)
+ extensionContext.AddExtensionNodeHandler ("MonoDevelop/SourceEditor2/TooltipProviders", OnTooltipProviderChanged);
+ }
+ }
+
+ internal IEditorActionHost EditorActionHost {
+ get {
+ return textEditorImpl.Actions;
+ }
+ }
+
+ internal ITextMarkerFactory TextMarkerFactory {
+ get {
+ return textEditorImpl.TextMarkerFactory;
+ }
+ }
+
+ internal TextEditor (ITextEditorImpl textEditorImpl)
+ {
+ if (textEditorImpl == null)
+ throw new ArgumentNullException ("textEditorImpl");
+ this.textEditorImpl = textEditorImpl;
+ commandRouter = new InternalCommandRouter (this);
+ fileTypeCondition.SetFileName (FileName);
+ ExtensionContext = AddinManager.CreateExtensionContext ();
+ ExtensionContext.RegisterCondition ("FileType", fileTypeCondition);
+
+ FileNameChanged += delegate {
+ fileTypeCondition.SetFileName (FileName);
+ };
+
+ MimeTypeChanged += delegate {
+ textEditorImpl.ClearTooltipProviders ();
+ foreach (var extensionNode in allProviders) {
+ if (extensionNode.IsValidFor (MimeType))
+ textEditorImpl.AddTooltipProvider ((TooltipProvider) extensionNode.CreateInstance ());
+ }
+ };
+ }
+
+ TextEditorViewContent viewContent;
+ internal IViewContent GetViewContent ()
+ {
+ if (viewContent == null) {
+ viewContent = new TextEditorViewContent (this, textEditorImpl);
+ }
+
+ return viewContent;
+ }
+
+ internal IFoldSegment CreateFoldSegment (int offset, int length, bool isFolded = false)
+ {
+ return textEditorImpl.CreateFoldSegment (offset, length, isFolded);
+ }
+ #endregion
+
+ #region Editor extensions
+ InternalCommandRouter commandRouter;
+ class InternalCommandRouter : MonoDevelop.Components.Commands.IMultiCastCommandRouter
+ {
+ readonly TextEditor editor;
+
+ public InternalCommandRouter (TextEditor editor)
+ {
+ this.editor = editor;
+ }
+
+ #region IMultiCastCommandRouter implementation
+
+ System.Collections.IEnumerable MonoDevelop.Components.Commands.IMultiCastCommandRouter.GetCommandTargets ()
+ {
+ yield return editor.textEditorImpl;
+ yield return editor.textEditorImpl.EditorExtension;
+ }
+ #endregion
+ }
+
+ internal object CommandRouter {
+ get {
+ return commandRouter;
+ }
+ }
+
+ DocumentContext documentContext;
+ internal DocumentContext DocumentContext {
+ get {
+ return documentContext;
+ }
+ set {
+ documentContext = value;
+ OnDocumentContextChanged (EventArgs.Empty);
+ }
+ }
+
+ public event EventHandler DocumentContextChanged;
+
+ void OnDocumentContextChanged (EventArgs e)
+ {
+ if (DocumentContext != null) {
+ textEditorImpl.SetQuickTaskProviders (DocumentContext.GetContents<IQuickTaskProvider> ());
+ textEditorImpl.SetUsageTaskProviders (DocumentContext.GetContents<UsageProviderEditorExtension> ());
+ } else {
+ textEditorImpl.SetQuickTaskProviders (Enumerable.Empty<IQuickTaskProvider> ());
+ textEditorImpl.SetUsageTaskProviders (Enumerable.Empty<UsageProviderEditorExtension> ());
+ }
+ var handler = DocumentContextChanged;
+ if (handler != null)
+ handler (this, e);
+ }
+
+ internal void InitializeExtensionChain (DocumentContext documentContext)
+ {
+ if (documentContext == null)
+ throw new ArgumentNullException ("documentContext");
+ DetachExtensionChain ();
+ var extensions = ExtensionContext.GetExtensionNodes ("/MonoDevelop/Ide/TextEditorExtensions", typeof(TextEditorExtensionNode));
+ TextEditorExtension last = null;
+ var mimetypeChain = DesktopService.GetMimeTypeInheritanceChainForFile (FileName).ToArray ();
+ foreach (TextEditorExtensionNode extNode in extensions) {
+ if (!extNode.Supports (FileName, mimetypeChain))
+ continue;
+ TextEditorExtension ext;
+ try {
+ var instance = extNode.CreateInstance ();
+ ext = instance as TextEditorExtension;
+ if (ext == null)
+ continue;
+ } catch (Exception e) {
+ LoggingService.LogError ("Error while creating text editor extension :" + extNode.Id + "(" + extNode.Type +")", e);
+ continue;
+ }
+ if (ext.IsValidInContext (documentContext)) {
+ if (last != null) {
+ last.Next = ext;
+ last = ext;
+ } else {
+ textEditorImpl.EditorExtension = last = ext;
+ }
+ ext.Initialize (this, documentContext);
+ }
+ }
+ this.DocumentContext = documentContext;
+ }
+
+ void DetachExtensionChain ()
+ {
+ var editorExtension = textEditorImpl.EditorExtension;
+ while (editorExtension != null) {
+ try {
+ editorExtension.Dispose ();
+ } catch (Exception ex) {
+ LoggingService.LogError ("Exception while disposing extension:" + editorExtension, ex);
+ }
+ editorExtension = editorExtension.Next;
+ }
+ textEditorImpl.EditorExtension = null;
+ }
+
+ public T GetContent<T> () where T : class
+ {
+ T result = textEditorImpl as T;
+ if (result != null)
+ return result;
+ var ext = textEditorImpl.EditorExtension;
+ while (ext != null) {
+ result = ext as T;
+ if (result != null)
+ return result;
+ ext = ext.Next;
+ }
+ return null;
+ }
+
+ public IEnumerable<T> GetContents<T> () where T : class
+ {
+ T result = textEditorImpl as T;
+ if (result != null)
+ yield return result;
+ var ext = textEditorImpl.EditorExtension;
+ while (ext != null) {
+ result = ext as T;
+ if (result != null)
+ yield return result;
+ ext = ext.Next;
+ }
+ }
+ #endregion
+
+ public string GetPangoMarkup (int offset, int length)
+ {
+ return textEditorImpl.GetPangoMarkup (offset, length);
+ }
+
+ public string GetPangoMarkup (ISegment segment)
+ {
+ if (segment == null)
+ throw new ArgumentNullException ("segment");
+ return textEditorImpl.GetPangoMarkup (segment.Offset, segment.Length);
+ }
+
+ public static implicit operator Microsoft.CodeAnalysis.Text.SourceText (TextEditor editor)
+ {
+ return new MonoDevelopSourceText (editor);
+ }
+
+
+ #region Annotations
+ // Annotations: points either null (no annotations), to the single annotation,
+ // or to an AnnotationList.
+ // Once it is pointed at an AnnotationList, it will never change (this allows thread-safety support by locking the list)
+
+ object annotations;
+ sealed class AnnotationList : List<object>, ICloneable
+ {
+ // There are two uses for this custom list type:
+ // 1) it's private, and thus (unlike List<object>) cannot be confused with real annotations
+ // 2) It allows us to simplify the cloning logic by making the list behave the same as a clonable annotation.
+ public AnnotationList (int initialCapacity) : base(initialCapacity)
+ {
+ }
+
+ public object Clone ()
+ {
+ lock (this) {
+ AnnotationList copy = new AnnotationList (this.Count);
+ for (int i = 0; i < this.Count; i++) {
+ object obj = this [i];
+ ICloneable c = obj as ICloneable;
+ copy.Add (c != null ? c.Clone () : obj);
+ }
+ return copy;
+ }
+ }
+ }
+
+ public void AddAnnotation (object annotation)
+ {
+ if (annotation == null)
+ throw new ArgumentNullException ("annotation");
+ retry: // Retry until successful
+ object oldAnnotation = Interlocked.CompareExchange (ref this.annotations, annotation, null);
+ if (oldAnnotation == null) {
+ return; // we successfully added a single annotation
+ }
+ AnnotationList list = oldAnnotation as AnnotationList;
+ if (list == null) {
+ // we need to transform the old annotation into a list
+ list = new AnnotationList (4);
+ list.Add (oldAnnotation);
+ list.Add (annotation);
+ if (Interlocked.CompareExchange (ref this.annotations, list, oldAnnotation) != oldAnnotation) {
+ // the transformation failed (some other thread wrote to this.annotations first)
+ goto retry;
+ }
+ } else {
+ // once there's a list, use simple locking
+ lock (list) {
+ list.Add (annotation);
+ }
+ }
+ }
+
+ public void RemoveAnnotations<T> () where T : class
+ {
+ retry: // Retry until successful
+ object oldAnnotations = this.annotations;
+ var list = oldAnnotations as AnnotationList;
+ if (list != null) {
+ lock (list)
+ list.RemoveAll (obj => obj is T);
+ } else if (oldAnnotations is T) {
+ if (Interlocked.CompareExchange (ref this.annotations, null, oldAnnotations) != oldAnnotations) {
+ // Operation failed (some other thread wrote to this.annotations first)
+ goto retry;
+ }
+ }
+ }
+
+ public T Annotation<T> () where T: class
+ {
+ object annotations = this.annotations;
+ var list = annotations as AnnotationList;
+ if (list != null) {
+ lock (list) {
+ foreach (object obj in list) {
+ T t = obj as T;
+ if (t != null)
+ return t;
+ }
+ return null;
+ }
+ }
+ return annotations as T;
+ }
+
+ /// <summary>
+ /// Gets all annotations stored on this AstNode.
+ /// </summary>
+ public IEnumerable<object> Annotations {
+ get {
+ object annotations = this.annotations;
+ AnnotationList list = annotations as AnnotationList;
+ if (list != null) {
+ lock (list) {
+ return list.ToArray ();
+ }
+ }
+ if (annotations != null)
+ return new [] { annotations };
+ return Enumerable.Empty<object> ();
+ }
+ }
+ #endregion
+
+ List<ProjectedTooltipProvider> projectedProviders = new List<ProjectedTooltipProvider> ();
+ IReadOnlyList<MonoDevelop.Ide.Editor.Projection.Projection> projections = null;
+
+ public void SetOrUpdateProjections (DocumentContext ctx, IReadOnlyList<MonoDevelop.Ide.Editor.Projection.Projection> projections, DisabledProjectionFeatures disabledFeatures = DisabledProjectionFeatures.None)
+ {
+ if (ctx == null)
+ throw new ArgumentNullException ("ctx");
+ if (this.projections != null) {
+ foreach (var projection in this.projections) {
+ projection.Dettach ();
+ }
+ }
+ this.projections = projections;
+ if (projections != null) {
+ foreach (var projection in projections) {
+ projection.Attach (this);
+ }
+ }
+
+ if ((disabledFeatures & DisabledProjectionFeatures.SemanticHighlighting) != DisabledProjectionFeatures.SemanticHighlighting) {
+ if (SemanticHighlighting is ProjectedSemanticHighlighting) {
+ ((ProjectedSemanticHighlighting)SemanticHighlighting).UpdateProjection (projections);
+ } else {
+ SemanticHighlighting = new ProjectedSemanticHighlighting (this, ctx, projections);
+ }
+ }
+
+ if ((disabledFeatures & DisabledProjectionFeatures.Tooltips) != DisabledProjectionFeatures.Tooltips) {
+ projectedProviders.ForEach (textEditorImpl.RemoveTooltipProvider);
+ projectedProviders = new List<ProjectedTooltipProvider> ();
+ foreach (var projection in projections) {
+ foreach (var tp in projection.ProjectedEditor.textEditorImpl.TooltipProvider) {
+ var newProvider = new ProjectedTooltipProvider (this, ctx, projection, tp);
+ projectedProviders.Add (newProvider);
+ textEditorImpl.AddTooltipProvider (newProvider);
+ }
+ }
+ }
+ InitializeProjectionExtensions (ctx, disabledFeatures);
+ }
+
+ bool projectionsAdded = false;
+ void InitializeProjectionExtensions (DocumentContext ctx, DisabledProjectionFeatures disabledFeatures)
+ {
+ if (projectionsAdded) {
+ TextEditorExtension ext = textEditorImpl.EditorExtension;
+ while (ext != null && ext.Next != null) {
+ var pext = ext as IProjectionExtension;
+ if (pext != null) {
+ pext.Projections = projections;
+ }
+ ext = ext.Next;
+ }
+ return;
+ }
+
+ if (projections.Count == 0)
+ return;
+
+ TextEditorExtension lastExtension = textEditorImpl.EditorExtension;
+ while (lastExtension != null && lastExtension.Next != null) {
+ var completionTextEditorExtension = lastExtension.Next as CompletionTextEditorExtension;
+ if (completionTextEditorExtension != null) {
+ var projectedFilterExtension = new ProjectedFilterCompletionTextEditorExtension (completionTextEditorExtension, projections) { Next = completionTextEditorExtension.Next };
+ lastExtension.Next = projectedFilterExtension;
+ projectedFilterExtension.Initialize (this, DocumentContext);
+ }
+ lastExtension = lastExtension.Next;
+ }
+
+ // no extensions -> no projections needed
+ if (textEditorImpl.EditorExtension == null)
+ return;
+
+ if ((disabledFeatures & DisabledProjectionFeatures.Completion) != DisabledProjectionFeatures.Completion) {
+ var projectedCompletionExtension = new ProjectedCompletionExtension (ctx, projections);
+ projectedCompletionExtension.Next = textEditorImpl.EditorExtension;
+
+ textEditorImpl.EditorExtension = projectedCompletionExtension;
+ projectedCompletionExtension.Initialize (this, DocumentContext);
+ }
+ projectionsAdded = true;
+ }
+
+ public void AddOverlay (Control messageOverlayContent, Func<int> sizeFunc)
+ {
+ textEditorImpl.AddOverlay (messageOverlayContent, sizeFunc);
+ }
+
+ public void RemoveOverlay (Control messageOverlayContent)
+ {
+ textEditorImpl.RemoveOverlay (messageOverlayContent);
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorDisplayBinding.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorDisplayBinding.cs
new file mode 100644
index 0000000000..1fe76c43a0
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorDisplayBinding.cs
@@ -0,0 +1,114 @@
+//
+// TextEditorDisplayBinding.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 MonoDevelop.Core;
+using MonoDevelop.Ide.Gui;
+using System.IO;
+using MonoDevelop.Projects;
+using System.ComponentModel;
+using MonoDevelop.Ide.Editor.Highlighting;
+
+namespace MonoDevelop.Ide.Editor
+{
+ public class TextEditorDisplayBinding : IViewDisplayBinding
+ {
+ static bool IsInitialized = false;
+
+ public static FilePath SyntaxModePath {
+ get {
+ return UserProfile.Current.UserDataRoot.Combine ("HighlightingSchemes");
+ }
+ }
+
+ static TextEditorDisplayBinding ()
+ {
+ InitSourceEditor ();
+ }
+
+ public static void InitSourceEditor ()
+ {
+ if (IsInitialized)
+ return;
+ IsInitialized = true;
+
+ // MonoDevelop.SourceEditor.Extension.TemplateExtensionNodeLoader.Init ();
+ DefaultSourceEditorOptions.Init ();
+ // SyntaxModeService.EnsureLoad ();
+ LoadCustomStylesAndModes ();
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void LoadCustomStylesAndModes ()
+ {
+ bool success = true;
+ if (!Directory.Exists (SyntaxModePath)) {
+ try {
+ Directory.CreateDirectory (SyntaxModePath);
+ } catch (Exception e) {
+ success = false;
+ LoggingService.LogError ("Can't create syntax mode directory", e);
+ }
+ }
+ if (success)
+ SyntaxModeService.LoadStylesAndModes (SyntaxModePath);
+ }
+
+ public string Name {
+ get {
+ return GettextCatalog.GetString ("Source Code Editor");
+ }
+ }
+
+ public bool CanHandle (FilePath fileName, string mimeType, Project ownerProject)
+ {
+ if (fileName != null)
+ return DesktopService.GetFileIsText (fileName, mimeType);
+
+ if (!string.IsNullOrEmpty (mimeType))
+ return DesktopService.GetMimeTypeIsText (mimeType);
+
+ return false;
+ }
+
+ public IViewContent CreateContent (FilePath fileName, string mimeType, Project ownerProject)
+ {
+ var editor = TextEditorFactory.CreateNewEditor ();
+ editor.MimeType = mimeType;
+ editor.GetViewContent ().Project = ownerProject;
+ editor.GetViewContent ().ContentName = fileName;
+ return editor.GetViewContent ();
+ }
+
+ public bool CanHandleFile (string fileName)
+ {
+ return DesktopService.GetFileIsText (fileName);
+ }
+
+ public bool CanUseAsDefault {
+ get { return true; }
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorFactory.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorFactory.cs
new file mode 100644
index 0000000000..6d54b8060b
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorFactory.cs
@@ -0,0 +1,95 @@
+//
+// DocumentFactory.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 MonoDevelop.Core.Text;
+using Mono.Addins;
+
+namespace MonoDevelop.Ide.Editor
+{
+ public static class TextEditorFactory
+ {
+ static ITextEditorFactory currentFactory;
+
+ static TextEditorFactory ()
+ {
+ AddinManager.AddExtensionNodeHandler ("/MonoDevelop/SourceEditor2/EditorFactory", delegate(object sender, ExtensionNodeEventArgs args) {
+ switch (args.Change) {
+ case ExtensionChange.Add:
+ if (currentFactory == null)
+ currentFactory = (ITextEditorFactory)args.ExtensionObject;
+ break;
+ }
+ });
+ }
+
+ public static ITextDocument CreateNewDocument ()
+ {
+ return currentFactory.CreateNewDocument ();
+ }
+
+ public static ITextDocument CreateNewDocument (ITextSource textSource, string fileName, string mimeType = null)
+ {
+ if (textSource == null)
+ throw new System.ArgumentNullException ("textSource");
+ return currentFactory.CreateNewDocument (textSource, fileName, mimeType);
+ }
+
+ public static ITextDocument LoadDocument (string fileName, string mimeType = null)
+ {
+ if (fileName == null)
+ throw new System.ArgumentNullException ("fileName");
+ return currentFactory.CreateNewDocument (StringTextSource.ReadFrom (fileName), fileName, mimeType);
+ }
+
+ public static IReadonlyTextDocument CreateNewReadonlyDocument (ITextSource textSource, string fileName, string mimeType = null)
+ {
+ if (textSource == null)
+ throw new System.ArgumentNullException ("textSource");
+ return currentFactory.CreateNewDocument (textSource, fileName, mimeType);
+ }
+
+ public static TextEditor CreateNewEditor ()
+ {
+ return new TextEditor (currentFactory.CreateNewEditor ());
+ }
+
+ public static TextEditor CreateNewEditor (IReadonlyTextDocument document)
+ {
+ if (document == null)
+ throw new System.ArgumentNullException ("document");
+ return new TextEditor (currentFactory.CreateNewEditor (document));
+ }
+
+ public static string[] GetSyntaxProperties (string mimeType, string name)
+ {
+ if (mimeType == null)
+ throw new System.ArgumentNullException ("mimeType");
+ if (name == null)
+ throw new System.ArgumentNullException ("name");
+ return currentFactory.GetSyntaxProperties (mimeType, name);
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorViewContent.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorViewContent.cs
new file mode 100644
index 0000000000..2305687ba2
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorViewContent.cs
@@ -0,0 +1,1233 @@
+//
+// TextEditorViewContent.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 MonoDevelop.Ide.Gui;
+using MonoDevelop.Projects;
+using MonoDevelop.Ide.Gui.Content;
+using MonoDevelop.Components.Commands;
+using MonoDevelop.Ide.Commands;
+using System.Collections;
+using System.Collections.Generic;
+using MonoDevelop.Ide.TypeSystem;
+using System.IO;
+using MonoDevelop.Core.Text;
+using System.Text;
+using Gtk;
+using ICSharpCode.NRefactory.TypeSystem;
+using System.Linq;
+using MonoDevelop.Ide.Editor.Extension;
+using ICSharpCode.NRefactory.Refactoring;
+using MonoDevelop.Components;
+using MonoDevelop.Core;
+using System.Threading.Tasks;
+using System.Threading;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Gdk;
+using MonoDevelop.Ide.CodeFormatting;
+
+namespace MonoDevelop.Ide.Editor
+{
+ /// <summary>
+ /// The TextEditor object needs to be available through IBaseViewContent.GetContent therefore we need to insert a
+ /// decorator in between.
+ /// </summary>
+ class TextEditorViewContent : IViewContent, ICommandRouter, IQuickTaskProvider
+ {
+ readonly TextEditor textEditor;
+ readonly ITextEditorImpl textEditorImpl;
+
+ DocumentContext currentContext;
+ MonoDevelop.Projects.Policies.PolicyContainer policyContainer;
+
+ public TextEditorViewContent (TextEditor textEditor, ITextEditorImpl textEditorImpl)
+ {
+ if (textEditor == null)
+ throw new ArgumentNullException ("textEditor");
+ if (textEditorImpl == null)
+ throw new ArgumentNullException ("textEditorImpl");
+ this.textEditor = textEditor;
+ this.textEditorImpl = textEditorImpl;
+ this.textEditor.MimeTypeChanged += UpdateTextEditorOptions;
+ DefaultSourceEditorOptions.Instance.Changed += UpdateTextEditorOptions;
+ this.textEditor.DocumentContextChanged += delegate {
+ if (currentContext != null)
+ currentContext.DocumentParsed -= HandleDocumentParsed;
+ currentContext = textEditor.DocumentContext;
+ currentContext.DocumentParsed += HandleDocumentParsed;
+ };
+ }
+
+ void HandleDirtyChanged (object sender, EventArgs e)
+ {
+ InformAutoSave ();
+ }
+
+ void HandleTextChanged (object sender, MonoDevelop.Core.Text.TextChangeEventArgs e)
+ {
+ InformAutoSave ();
+ }
+
+ void UpdateTextEditorOptions (object sender, EventArgs e)
+ {
+ UpdateStyleParent (Project, textEditor.MimeType);
+ }
+
+ uint autoSaveTimer = 0;
+
+ void InformAutoSave ()
+ {
+ RemoveAutoSaveTimer ();
+ autoSaveTimer = GLib.Timeout.Add (500, delegate {
+ AutoSave.InformAutoSaveThread (textEditor.CreateSnapshot (), textEditor.FileName, textEditorImpl.IsDirty);
+ autoSaveTimer = 0;
+ return false;
+ });
+ }
+
+
+ void RemoveAutoSaveTimer ()
+ {
+ if (autoSaveTimer == 0)
+ return;
+ GLib.Source.Remove (autoSaveTimer);
+ autoSaveTimer = 0;
+ }
+
+ void RemovePolicyChangeHandler ()
+ {
+ if (policyContainer != null)
+ policyContainer.PolicyChanged -= HandlePolicyChanged;
+ }
+
+ void UpdateStyleParent (MonoDevelop.Projects.Project styleParent, string mimeType)
+ {
+ RemovePolicyChangeHandler ();
+
+ if (string.IsNullOrEmpty (mimeType))
+ mimeType = "text/plain";
+
+ var mimeTypes = DesktopService.GetMimeTypeInheritanceChain (mimeType);
+
+ if (styleParent != null)
+ policyContainer = styleParent.Policies;
+ else
+ policyContainer = MonoDevelop.Projects.Policies.PolicyService.DefaultPolicies;
+ var currentPolicy = policyContainer.Get<TextStylePolicy> (mimeTypes);
+
+ policyContainer.PolicyChanged += HandlePolicyChanged;
+ textEditor.Options = DefaultSourceEditorOptions.Instance.WithTextStyle (currentPolicy);
+ }
+
+ void HandlePolicyChanged (object sender, MonoDevelop.Projects.Policies.PolicyChangedEventArgs args)
+ {
+ var mimeTypes = DesktopService.GetMimeTypeInheritanceChain (textEditor.MimeType);
+ var currentPolicy = policyContainer.Get<TextStylePolicy> (mimeTypes);
+ textEditor.Options = DefaultSourceEditorOptions.Instance.WithTextStyle (currentPolicy);
+ }
+
+ void HandleDocumentParsed (object sender, EventArgs e)
+ {
+ var ctx = (DocumentContext)sender;
+ src.Cancel ();
+ src = new CancellationTokenSource ();
+ var token = src.Token;
+ Task.Run (() => {
+ try {
+ UpdateErrorUndelines (ctx.ParsedDocument, token);
+ UpdateQuickTasks (ctx.ParsedDocument, token);
+ UpdateFoldings (ctx.ParsedDocument, false, token);
+ } catch (OperationCanceledException) {
+ // ignore
+ }
+ }, token);
+ }
+
+ #region Error handling
+ List<IErrorMarker> errors = new List<IErrorMarker> ();
+ uint resetTimerId;
+
+ void RemoveErrorUndelinesResetTimerId ()
+ {
+ if (resetTimerId > 0) {
+ GLib.Source.Remove (resetTimerId);
+ resetTimerId = 0;
+ }
+ }
+
+ void RemoveErrorUnderlines ()
+ {
+ errors.ForEach (err => textEditor.RemoveMarker (err));
+ errors.Clear ();
+ }
+
+ void UnderLineError (MonoDevelop.Ide.TypeSystem.Error info)
+ {
+ var error = TextMarkerFactory.CreateErrorMarker (textEditor, info);
+ textEditor.AddMarker (error);
+ errors.Add (error);
+ }
+
+ async void UpdateErrorUndelines (ParsedDocument parsedDocument, CancellationToken token)
+ {
+ if (!DefaultSourceEditorOptions.Instance.UnderlineErrors || parsedDocument == null)
+ return;
+ try {
+ var errors = await parsedDocument.GetErrorsAsync(token).ConfigureAwait (false);
+ Application.Invoke (delegate {
+ if (token.IsCancellationRequested)
+ return;
+ RemoveErrorUndelinesResetTimerId ();
+ const uint timeout = 500;
+ resetTimerId = GLib.Timeout.Add (timeout, delegate {
+ if (token.IsCancellationRequested) {
+ resetTimerId = 0;
+ return false;
+ }
+ RemoveErrorUnderlines ();
+ // Else we underline the error
+ if (errors != null) {
+ foreach (var error in errors) {
+ UnderLineError (error);
+ }
+ }
+ resetTimerId = 0;
+ return false;
+ });
+ });
+ } catch (OperationCanceledException) {
+ // ignore
+ }
+ }
+ #endregion
+ CancellationTokenSource src = new CancellationTokenSource ();
+ void UpdateFoldings (ParsedDocument parsedDocument, bool firstTime = false, CancellationToken token = default (CancellationToken))
+ {
+ if (parsedDocument == null || !textEditor.Options.ShowFoldMargin)
+ return;
+ // don't update parsed documents that contain errors - the foldings from there may be invalid.
+ if (parsedDocument.HasErrors)
+ return;
+ var caretLocation = textEditor.CaretLocation;
+ try {
+ var foldSegments = new List<IFoldSegment> ();
+
+ foreach (FoldingRegion region in parsedDocument.GetFoldingsAsync(token).Result) {
+ if (token.IsCancellationRequested)
+ return;
+ var type = FoldingType.Unknown;
+ bool setFolded = false;
+ bool folded = false;
+ //decide whether the regions should be folded by default
+ switch (region.Type) {
+ case FoldType.Member:
+ type = FoldingType.TypeMember;
+ break;
+ case FoldType.Type:
+ type = FoldingType.TypeDefinition;
+ break;
+ case FoldType.UserRegion:
+ type = FoldingType.Region;
+ setFolded = DefaultSourceEditorOptions.Instance.DefaultRegionsFolding;
+ folded = true;
+ break;
+ case FoldType.Comment:
+ type = FoldingType.Comment;
+ setFolded = DefaultSourceEditorOptions.Instance.DefaultCommentFolding;
+ folded = true;
+ break;
+ case FoldType.CommentInsideMember:
+ type = FoldingType.Comment;
+ setFolded = DefaultSourceEditorOptions.Instance.DefaultCommentFolding;
+ folded = false;
+ break;
+ case FoldType.Undefined:
+ setFolded = true;
+ folded = region.IsFoldedByDefault;
+ break;
+ }
+ var start = textEditor.LocationToOffset (region.Region.Begin);
+ var end = textEditor.LocationToOffset (region.Region.End);
+ var marker = textEditor.CreateFoldSegment (start, end - start);
+ foldSegments.Add (marker);
+ marker.CollapsedText = region.Name;
+ marker.FoldingType = type;
+ //and, if necessary, set its fold state
+ if (marker != null && setFolded && firstTime) {
+ // only fold on document open, later added folds are NOT folded by default.
+ marker.IsCollapsed = folded;
+ continue;
+ }
+ if (marker != null && region.Region.Contains (caretLocation.Line, caretLocation.Column))
+ marker.IsCollapsed = false;
+ }
+ if (firstTime) {
+ textEditor.SetFoldings (foldSegments);
+ } else {
+ Application.Invoke (delegate {
+ if (!token.IsCancellationRequested)
+ textEditor.SetFoldings (foldSegments);
+ });
+ }
+ } catch (Exception ex) {
+ LoggingService.LogError ("Unhandled exception in ParseInformationUpdaterWorkerThread", ex);
+ }
+ }
+
+ void RunFirstTimeFoldUpdate (string text)
+ {
+ if (string.IsNullOrEmpty (text))
+ return;
+ ParsedDocument parsedDocument = null;
+
+ var foldingParser = TypeSystemService.GetFoldingParser (textEditor.MimeType);
+ if (foldingParser != null) {
+ parsedDocument = foldingParser.Parse (textEditor.FileName, text);
+ } else {
+ var normalParser = TypeSystemService.GetParser (textEditor.MimeType);
+ if (normalParser != null) {
+ parsedDocument = normalParser.Parse (new MonoDevelop.Ide.TypeSystem.ParseOptions { FileName = textEditor.FileName, Content = new StringTextSource (text) }).Result;
+ }
+ }
+ if (parsedDocument != null) {
+ UpdateFoldings (parsedDocument, true);
+ }
+ }
+
+
+ #region IViewFContent implementation
+
+ event EventHandler IViewContent.ContentNameChanged {
+ add {
+ textEditorImpl.ContentNameChanged += value;
+ }
+ remove {
+ textEditorImpl.ContentNameChanged -= value;
+ }
+ }
+
+ event EventHandler IViewContent.ContentChanged {
+ add {
+ textEditorImpl.ContentChanged += value;
+ }
+ remove {
+ textEditorImpl.ContentChanged -= value;
+ }
+ }
+
+ event EventHandler IViewContent.DirtyChanged {
+ add {
+ textEditorImpl.DirtyChanged += value;
+ }
+ remove {
+ textEditorImpl.DirtyChanged -= value;
+ }
+ }
+
+ event EventHandler IViewContent.BeforeSave {
+ add {
+ textEditorImpl.BeforeSave += value;
+ }
+ remove {
+ textEditorImpl.BeforeSave -= value;
+ }
+ }
+
+ void IViewContent.Load (FileOpenInformation fileOpenInformation)
+ {
+ this.textEditorImpl.DirtyChanged -= HandleDirtyChanged;
+ this.textEditor.TextChanged -= HandleTextChanged;
+ textEditorImpl.Load (fileOpenInformation);
+ RunFirstTimeFoldUpdate (textEditor.Text);
+ this.textEditor.TextChanged += HandleTextChanged;
+ this.textEditorImpl.DirtyChanged += HandleDirtyChanged;
+ }
+
+ void IViewContent.Load (string fileName)
+ {
+ this.textEditorImpl.DirtyChanged -= HandleDirtyChanged;
+ this.textEditor.TextChanged -= HandleTextChanged;
+ textEditorImpl.Load (new FileOpenInformation (fileName));
+ RunFirstTimeFoldUpdate (textEditor.Text);
+ this.textEditor.TextChanged += HandleTextChanged;
+ this.textEditorImpl.DirtyChanged += HandleDirtyChanged;
+ }
+
+ void IViewContent.LoadNew (System.IO.Stream content, string mimeType)
+ {
+ textEditor.MimeType = mimeType;
+ string text = null;
+ if (content != null) {
+ Encoding encoding;
+ bool hadBom;
+ text = TextFileUtility.GetText (content, out encoding, out hadBom);
+ textEditor.Text = text;
+ textEditor.Encoding = encoding;
+ textEditor.UseBOM = hadBom;
+ }
+ RunFirstTimeFoldUpdate (text);
+ textEditorImpl.InformLoadComplete ();
+ }
+
+ void IViewContent.Save (FileSaveInformation fileSaveInformation)
+ {
+ if (!string.IsNullOrEmpty (fileSaveInformation.FileName))
+ AutoSave.RemoveAutoSaveFile (fileSaveInformation.FileName);
+ textEditorImpl.Save (fileSaveInformation);
+ }
+
+ void IViewContent.Save (string fileName)
+ {
+ if (!string.IsNullOrEmpty (fileName))
+ AutoSave.RemoveAutoSaveFile (fileName);
+ textEditorImpl.Save (new FileSaveInformation (fileName));
+ }
+
+ void IViewContent.Save ()
+ {
+ if (!string.IsNullOrEmpty (textEditorImpl.ContentName))
+ AutoSave.RemoveAutoSaveFile (textEditorImpl.ContentName);
+ textEditorImpl.Save ();
+ }
+
+ void IViewContent.DiscardChanges ()
+ {
+ if (!string.IsNullOrEmpty (textEditorImpl.ContentName))
+ AutoSave.RemoveAutoSaveFile (textEditorImpl.ContentName);
+ textEditorImpl.DiscardChanges ();
+ }
+
+ public MonoDevelop.Projects.Project Project {
+ get {
+ return textEditorImpl.Project;
+ }
+ set {
+ textEditorImpl.Project = value;
+ UpdateTextEditorOptions (null, null);
+ }
+ }
+
+ string IViewContent.PathRelativeToProject {
+ get {
+ return textEditorImpl.PathRelativeToProject;
+ }
+ }
+
+ string IViewContent.ContentName {
+ get {
+ return textEditorImpl.ContentName;
+ }
+ set {
+ textEditorImpl.ContentName = value;
+ }
+ }
+
+ string IViewContent.UntitledName {
+ get {
+ return textEditorImpl.UntitledName;
+ }
+ set {
+ textEditorImpl.UntitledName = value;
+ }
+ }
+
+ string IViewContent.StockIconId {
+ get {
+ return textEditorImpl.StockIconId;
+ }
+ }
+
+ bool IViewContent.IsUntitled {
+ get {
+ return textEditorImpl.IsUntitled;
+ }
+ }
+
+ bool IViewContent.IsViewOnly {
+ get {
+ return textEditorImpl.IsViewOnly;
+ }
+ }
+
+ bool IViewContent.IsFile {
+ get {
+ return textEditorImpl.IsFile;
+ }
+ }
+
+ bool IViewContent.IsDirty {
+ get {
+ return textEditorImpl.IsDirty;
+ }
+ set {
+ textEditorImpl.IsDirty = value;
+ }
+ }
+
+ bool IViewContent.IsReadOnly {
+ get {
+ return textEditorImpl.IsReadOnly;
+ }
+ }
+
+ #endregion
+
+ #region IBaseViewContent implementation
+ object IBaseViewContent.GetContent (Type type)
+ {
+ if (type.IsAssignableFrom (typeof(TextEditor)))
+ return textEditor;
+ var ext = textEditorImpl.EditorExtension;
+ while (ext != null) {
+ if (type.IsInstanceOfType (ext))
+ return ext;
+ ext = ext.Next;
+ }
+ return textEditorImpl.GetContent (type);
+ }
+
+ public virtual IEnumerable<T> GetContents<T> () where T : class
+ {
+ if (typeof(T) == typeof(TextEditor)) {
+ yield return (T)(object)textEditor;
+ yield break;
+ }
+ var result = this as T;
+ if (result != null) {
+ yield return result;
+ }
+ var ext = textEditorImpl.EditorExtension;
+ while (ext != null) {
+ result = ext as T;
+ if (result != null) {
+ yield return result;
+ }
+ ext = ext.Next;
+ }
+ foreach (var cnt in textEditorImpl.GetContents<T> ()) {
+ yield return cnt;
+ }
+ }
+
+ bool IBaseViewContent.CanReuseView (string fileName)
+ {
+ return textEditorImpl.CanReuseView (fileName);
+ }
+
+ void IBaseViewContent.RedrawContent ()
+ {
+ textEditorImpl.RedrawContent ();
+ }
+
+ IWorkbenchWindow IBaseViewContent.WorkbenchWindow {
+ get {
+ return textEditorImpl.WorkbenchWindow;
+ }
+ set {
+ textEditorImpl.WorkbenchWindow = value;
+ }
+ }
+
+ Gtk.Widget IBaseViewContent.Control {
+ get {
+ return textEditor;
+ }
+ }
+
+ string IBaseViewContent.TabPageLabel {
+ get {
+ return textEditorImpl.TabPageLabel;
+ }
+ }
+
+ #endregion
+
+ #region IDisposable implementation
+
+ void IDisposable.Dispose ()
+ {
+ DefaultSourceEditorOptions.Instance.Changed -= UpdateTextEditorOptions;
+ RemovePolicyChangeHandler ();
+ RemoveAutoSaveTimer ();
+ RemoveErrorUndelinesResetTimerId ();
+ textEditorImpl.Dispose ();
+ }
+
+ #endregion
+
+ #region ICommandRouter implementation
+
+ object ICommandRouter.GetNextCommandTarget ()
+ {
+ return textEditorImpl;
+ }
+
+ #endregion
+
+ #region Commands
+ void ToggleCodeCommentWithBlockComments ()
+ {
+ var blockStarts = TextEditorFactory.GetSyntaxProperties (textEditor.MimeType, "BlockCommentStart");
+ var blockEnds = TextEditorFactory.GetSyntaxProperties (textEditor.MimeType, "BlockCommentEnd");
+ if (blockStarts == null || blockEnds == null || blockStarts.Length == 0 || blockEnds.Length == 0)
+ return;
+
+ string blockStart = blockStarts[0];
+ string blockEnd = blockEnds[0];
+
+ using (var undo = textEditor.OpenUndoGroup ()) {
+ IDocumentLine startLine;
+ IDocumentLine endLine;
+
+ if (textEditor.IsSomethingSelected) {
+ startLine = textEditor.GetLineByOffset (textEditor.SelectionRange.Offset);
+ endLine = textEditor.GetLineByOffset (textEditor.SelectionRange.EndOffset);
+ } else {
+ startLine = endLine = textEditor.GetLine (textEditor.CaretLine);
+ }
+ string startLineText = textEditor.GetTextAt (startLine.Offset, startLine.Length);
+ string endLineText = textEditor.GetTextAt (endLine.Offset, endLine.Length);
+ if (startLineText.StartsWith (blockStart, StringComparison.Ordinal) && endLineText.EndsWith (blockEnd, StringComparison.Ordinal)) {
+ textEditor.RemoveText (endLine.Offset + endLine.Length - blockEnd.Length, blockEnd.Length);
+ textEditor.RemoveText (startLine.Offset, blockStart.Length);
+ if (textEditor.IsSomethingSelected) {
+ textEditor.SelectionAnchorOffset -= blockEnd.Length;
+ }
+ } else {
+ textEditor.InsertText (endLine.Offset + endLine.Length, blockEnd);
+ textEditor.InsertText (startLine.Offset, blockStart);
+ if (textEditor.IsSomethingSelected) {
+ textEditor.SelectionAnchorOffset += blockEnd.Length;
+ }
+ }
+ }
+ }
+
+ bool TryGetLineCommentTag (out string commentTag)
+ {
+ var lineComments = TextEditorFactory.GetSyntaxProperties (textEditor.MimeType, "LineComment");
+ if (lineComments == null || lineComments.Length == 0) {
+ commentTag = null;
+ return false;
+ }
+ commentTag = lineComments [0];
+ return true;
+ }
+
+ [CommandUpdateHandler (EditCommands.AddCodeComment)]
+ [CommandUpdateHandler (EditCommands.RemoveCodeComment)]
+ [CommandUpdateHandler (EditCommands.ToggleCodeComment)]
+ void OnUpdateToggleComment (CommandInfo info)
+ {
+ var lineComments = TextEditorFactory.GetSyntaxProperties (textEditor.MimeType, "LineComment");
+ if (lineComments != null && lineComments.Length > 0) {
+ info.Visible = true;
+ return;
+ }
+ var blockStarts = TextEditorFactory.GetSyntaxProperties (textEditor.MimeType, "BlockCommentStart");
+ var blockEnds = TextEditorFactory.GetSyntaxProperties (textEditor.MimeType, "BlockCommentEnd");
+ info.Visible = blockStarts != null && blockStarts.Length > 0 && blockEnds != null && blockEnds.Length > 0;
+ }
+
+ [CommandHandler (EditCommands.ToggleCodeComment)]
+ internal void ToggleCodeComment ()
+ {
+ string commentTag;
+ if (!TryGetLineCommentTag (out commentTag))
+ return;
+ bool comment = false;
+ foreach (var line in GetSelectedLines (textEditor)) {
+ int startOffset;
+ int offset = line.Offset;
+ if (!StartsWith (textEditor, offset, line.Length, commentTag, out startOffset)) {
+ if (startOffset - offset == line.Length) // case: line consists only of white spaces
+ continue;
+ comment = true;
+ break;
+ }
+ }
+
+ if (comment) {
+ AddCodeComment ();
+ } else {
+ RemoveCodeComment ();
+ }
+ }
+
+ static bool StartsWith (ITextSource text, int offset, int length, string commentTag, out int startOffset)
+ {
+ int max = Math.Min (offset + length, text.Length);
+ int i = offset;
+ for (; i < max; i++) {
+ char ch = text.GetCharAt (i);
+ if (ch != ' ' && ch != '\t')
+ break;
+ }
+ startOffset = i;
+ for (int j = 0; j < commentTag.Length; j++) {
+ if (text.GetCharAt (i) != commentTag [j])
+ return false;
+ i++;
+ }
+
+ return true;
+ }
+
+ static IEnumerable<IDocumentLine> GetSelectedLines (TextEditor textEditor)
+ {
+ var selection = textEditor.SelectionRange;
+ var line = textEditor.GetLineByOffset (selection.EndOffset);
+ do {
+ yield return line;
+ line = line.PreviousLine;
+ } while (line != null && line.EndOffset > selection.Offset);
+ }
+
+ [CommandHandler (EditCommands.AddCodeComment)]
+ internal void AddCodeComment ()
+ {
+ string commentTag;
+ if (!TryGetLineCommentTag (out commentTag))
+ return;
+
+ using (var undo = textEditor.OpenUndoGroup ()) {
+ var wasSelected = textEditor.IsSomethingSelected;
+ var lead = textEditor.SelectionLeadOffset;
+ var anchor = textEditor.SelectionAnchorOffset;
+ var lineAndIndents = new List<Tuple<IDocumentLine, string>>();
+ string indent = null;
+ var oldVersion = textEditor.Version;
+ foreach (var line in GetSelectedLines (textEditor)) {
+ var curIndent = line.GetIndentation (textEditor);
+ if (line.Length == curIndent.Length) {
+ lineAndIndents.Add (Tuple.Create ((IDocumentLine)null, ""));
+ continue;
+ }
+ if (indent == null || curIndent.Length < indent.Length)
+ indent = curIndent;
+ lineAndIndents.Add (Tuple.Create (line, curIndent));
+ }
+
+ foreach (var line in lineAndIndents) {
+ if (line.Item1 == null)
+ continue;
+ textEditor.InsertText (line.Item1.Offset + indent.Length, commentTag);
+ }
+ if (wasSelected) {
+ textEditor.SelectionAnchorOffset = oldVersion.MoveOffsetTo (textEditor.Version, anchor);
+ textEditor.SelectionLeadOffset = oldVersion.MoveOffsetTo (textEditor.Version, lead);
+ }
+ }
+ }
+
+ [CommandHandler (EditCommands.RemoveCodeComment)]
+ internal void RemoveCodeComment ()
+ {
+ string commentTag;
+ if (!TryGetLineCommentTag (out commentTag))
+ return;
+
+ using (var undo = textEditor.OpenUndoGroup ()) {
+ var wasSelected = textEditor.IsSomethingSelected;
+ var lead = textEditor.SelectionLeadOffset;
+ var anchor = textEditor.SelectionAnchorOffset;
+ int lines = 0;
+
+ IDocumentLine first = null;
+ IDocumentLine last = null;
+ var oldVersion = textEditor.Version;
+ foreach (var line in GetSelectedLines (textEditor)) {
+ int startOffset;
+ if (StartsWith (textEditor, line.Offset, line.Length, commentTag, out startOffset)) {
+ textEditor.RemoveText (startOffset, commentTag.Length);
+ lines++;
+ }
+
+ first = line;
+ if (last == null)
+ last = line;
+ }
+
+ if (wasSelected) {
+// if (IdeApp.Workbench != null)
+// CodeFormatterService.Format (textEditor, IdeApp.Workbench.ActiveDocument, TextSegment.FromBounds (first.Offset, last.EndOffset));
+
+ textEditor.SelectionAnchorOffset = oldVersion.MoveOffsetTo (textEditor.Version, anchor);
+ textEditor.SelectionLeadOffset = oldVersion.MoveOffsetTo (textEditor.Version, lead);
+ }
+ }
+ }
+
+ [CommandHandler (EditCommands.InsertGuid)]
+ void InsertGuid ()
+ {
+ textEditor.InsertAtCaret (Guid.NewGuid ().ToString ());
+ }
+
+ [CommandUpdateHandler (MessageBubbleCommands.Toggle)]
+ public void OnUpdateToggleErrorTextMarker (CommandInfo info)
+ {
+ var line = textEditor.GetLine (textEditor.CaretLine);
+ if (line == null) {
+ info.Visible = false;
+ return;
+ }
+
+ var marker = (IMessageBubbleLineMarker)textEditor.GetLineMarkers (line).FirstOrDefault (m => m is IMessageBubbleLineMarker);
+ info.Visible = marker != null;
+ }
+
+ [CommandHandler (MessageBubbleCommands.Toggle)]
+ public void OnToggleErrorTextMarker ()
+ {
+ var line = textEditor.GetLine (textEditor.CaretLine);
+ if (line == null)
+ return;
+ var marker = (IMessageBubbleLineMarker)textEditor.GetLineMarkers (line).FirstOrDefault (m => m is IMessageBubbleLineMarker);
+ if (marker != null) {
+ marker.IsVisible = !marker.IsVisible;
+ }
+ }
+ #endregion
+
+ #region IQuickTaskProvider implementation
+ List<QuickTask> tasks = new List<QuickTask> ();
+
+ public event EventHandler TasksUpdated;
+
+ protected virtual void OnTasksUpdated (EventArgs e)
+ {
+ EventHandler handler = this.TasksUpdated;
+ if (handler != null)
+ handler (this, e);
+ }
+
+ public IEnumerable<QuickTask> QuickTasks {
+ get {
+ return tasks;
+ }
+ }
+
+ async void UpdateQuickTasks (ParsedDocument doc, CancellationToken token)
+ {
+ var newTasks = new List<QuickTask> ();
+ if (doc != null) {
+ foreach (var cmt in await doc.GetTagCommentsAsync(token).ConfigureAwait (false)) {
+ var newTask = new QuickTask (cmt.Text, textEditor.LocationToOffset (cmt.Region.Begin.Line, cmt.Region.Begin.Column), DiagnosticSeverity.Info);
+ newTasks.Add (newTask);
+ }
+
+ foreach (var error in await doc.GetErrorsAsync(token).ConfigureAwait (false)) {
+ var newTask = new QuickTask (error.Message, textEditor.LocationToOffset (error.Region.Begin.Line, error.Region.Begin.Column), error.ErrorType == MonoDevelop.Ide.TypeSystem.ErrorType.Error ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning);
+ newTasks.Add (newTask);
+ }
+ }
+ Application.Invoke (delegate {
+ if (token.IsCancellationRequested)
+ return;
+ tasks = newTasks;
+ OnTasksUpdated (EventArgs.Empty);
+ });
+ }
+ #endregion
+
+ #region Key bindings
+
+ [CommandHandler (TextEditorCommands.LineEnd)]
+ void OnLineEnd ()
+ {
+ EditActions.MoveCaretToLineEnd (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.LineStart)]
+ void OnLineStart ()
+ {
+ EditActions.MoveCaretToLineStart (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.DeleteLeftChar)]
+ void OnDeleteLeftChar ()
+ {
+ EditActions.Backspace (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.DeleteRightChar)]
+ void OnDeleteRightChar ()
+ {
+ EditActions.Delete (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.CharLeft)]
+ void OnCharLeft ()
+ {
+ EditActions.MoveCaretLeft (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.CharRight)]
+ void OnCharRight ()
+ {
+ EditActions.MoveCaretRight (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.LineUp)]
+ void OnLineUp ()
+ {
+ EditActions.MoveCaretUp (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.LineDown)]
+ void OnLineDown ()
+ {
+ EditActions.MoveCaretDown (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.DocumentStart)]
+ void OnDocumentStart ()
+ {
+ EditActions.MoveCaretToDocumentStart (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.DocumentEnd)]
+ void OnDocumentEnd ()
+ {
+ EditActions.MoveCaretToDocumentEnd (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.PageUp)]
+ void OnPageUp ()
+ {
+ EditActions.PageUp (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.PageDown)]
+ void OnPageDown ()
+ {
+ EditActions.PageDown (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.DeleteLine)]
+ void OnDeleteLine ()
+ {
+ EditActions.DeleteCurrentLine (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.DeleteToLineEnd)]
+ void OnDeleteToLineEnd ()
+ {
+ EditActions.DeleteCurrentLineToEnd (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.ScrollLineUp)]
+ void OnScrollLineUp ()
+ {
+ EditActions.ScrollLineUp (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.ScrollLineDown)]
+ void OnScrollLineDown ()
+ {
+ EditActions.ScrollLineDown (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.ScrollPageUp)]
+ void OnScrollPageUp ()
+ {
+ EditActions.ScrollPageUp (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.ScrollPageDown)]
+ void OnScrollPageDown ()
+ {
+ EditActions.ScrollPageDown (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.GotoMatchingBrace)]
+ void OnGotoMatchingBrace ()
+ {
+ EditActions.GotoMatchingBrace (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.SelectionMoveLeft)]
+ void OnSelectionMoveLeft ()
+ {
+ EditActions.SelectionMoveLeft (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.SelectionMoveRight)]
+ void OnSelectionMoveRight ()
+ {
+ EditActions.SelectionMoveRight (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.MovePrevWord)]
+ void OnMovePrevWord ()
+ {
+ EditActions.MovePrevWord (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.MoveNextWord)]
+ void OnMoveNextWord ()
+ {
+ EditActions.MoveNextWord (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.SelectionMovePrevWord)]
+ void OnSelectionMovePrevWord ()
+ {
+ EditActions.SelectionMovePrevWord (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.SelectionMoveNextWord)]
+ void OnSelectionMoveNextWord ()
+ {
+ EditActions.SelectionMoveNextWord (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.MovePrevSubword)]
+ void OnMovePrevSubword ()
+ {
+ EditActions.MovePrevSubWord (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.MoveNextSubword)]
+ void OnMoveNextSubword ()
+ {
+ EditActions.MoveNextSubWord (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.SelectionMovePrevSubword)]
+ void OnSelectionMovePrevSubword ()
+ {
+ EditActions.SelectionMovePrevSubWord (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.SelectionMoveNextSubword)]
+ void OnSelectionMoveNextSubword ()
+ {
+ EditActions.SelectionMoveNextSubWord (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.SelectionMoveUp)]
+ void OnSelectionMoveUp ()
+ {
+ EditActions.SelectionMoveUp (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.SelectionMoveDown)]
+ void OnSelectionMoveDown ()
+ {
+ EditActions.SelectionMoveDown (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.SelectionMoveHome)]
+ void OnSelectionMoveHome ()
+ {
+ EditActions.SelectionMoveLineStart (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.SelectionMoveEnd)]
+ void OnSelectionMoveEnd ()
+ {
+ EditActions.SelectionMoveLineEnd (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.SelectionMoveToDocumentStart)]
+ void OnSelectionMoveToDocumentStart ()
+ {
+ EditActions.SelectionMoveToDocumentStart (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.ExpandSelectionToLine)]
+ void OnExpandSelectionToLine ()
+ {
+ EditActions.ExpandSelectionToLine (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.SelectionMoveToDocumentEnd)]
+ void OnSelectionMoveToDocumentEnd ()
+ {
+ EditActions.SelectionMoveToDocumentEnd (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.SwitchCaretMode)]
+ void OnSwitchCaretMode ()
+ {
+ EditActions.SwitchCaretMode (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.InsertTab)]
+ void OnInsertTab ()
+ {
+ EditActions.InsertTab (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.RemoveTab)]
+ void OnRemoveTab ()
+ {
+ EditActions.RemoveTab (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.InsertNewLine)]
+ void OnInsertNewLine ()
+ {
+ EditActions.InsertNewLine (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.InsertNewLineAtEnd)]
+ void OnInsertNewLineAtEnd ()
+ {
+ EditActions.InsertNewLineAtEnd (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.InsertNewLinePreserveCaretPosition)]
+ void OnInsertNewLinePreserveCaretPosition ()
+ {
+ EditActions.InsertNewLinePreserveCaretPosition (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.CompleteStatement)]
+ void OnCompleteStatement ()
+ {
+ var doc = IdeApp.Workbench.ActiveDocument;
+ var generator = CodeGenerator.CreateGenerator (doc);
+ if (generator != null) {
+ generator.CompleteStatement (doc);
+ }
+ }
+
+ [CommandHandler (TextEditorCommands.DeletePrevWord)]
+ void OnDeletePrevWord ()
+ {
+ EditActions.DeletePreviousWord (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.DeleteNextWord)]
+ void OnDeleteNextWord ()
+ {
+ EditActions.DeleteNextWord (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.DeletePrevSubword)]
+ void OnDeletePrevSubword ()
+ {
+ EditActions.DeletePreviousSubword (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.DeleteNextSubword)]
+ void OnDeleteNextSubword ()
+ {
+ EditActions.DeleteNextSubword (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.SelectionPageDownAction)]
+ void OnSelectionPageDownAction ()
+ {
+ EditActions.SelectionPageDown (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.SelectionPageUpAction)]
+ void OnSelectionPageUpAction ()
+ {
+ EditActions.SelectionPageUp (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.PulseCaret)]
+ void OnPulseCaretCommand ()
+ {
+ EditActions.StartCaretPulseAnimation (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.TransposeCharacters)]
+ void TransposeCharacters ()
+ {
+ EditActions.TransposeCharacters (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.DuplicateLine)]
+ void DuplicateLine ()
+ {
+ EditActions.DuplicateCurrentLine (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.RecenterEditor)]
+ void RecenterEditor ()
+ {
+ EditActions.RecenterEditor (textEditor);
+ }
+
+ [CommandHandler (EditCommands.JoinWithNextLine)]
+ void JoinLines ()
+ {
+ EditActions.JoinLines (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.MoveBlockUp)]
+ void OnMoveBlockUp ()
+ {
+ EditActions.MoveBlockUp (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.MoveBlockDown)]
+ void OnMoveBlockDown ()
+ {
+ EditActions.MoveBlockDown (textEditor);
+ }
+
+ [CommandHandler (TextEditorCommands.ToggleBlockSelectionMode)]
+ void OnToggleBlockSelectionMode ()
+ {
+ EditActions.ToggleBlockSelectionMode (textEditor);
+ }
+
+ [CommandHandler (EditCommands.IndentSelection)]
+ void IndentSelection ()
+ {
+ EditActions.IndentSelection (textEditor);
+ }
+
+ [CommandHandler (EditCommands.UnIndentSelection)]
+ void UnIndentSelection ()
+ {
+ EditActions.UnIndentSelection (textEditor);
+ }
+
+
+ [CommandHandler (EditCommands.SortSelectedLines)]
+ void SortSelectedLines ()
+ {
+ EditActions.SortSelectedLines (textEditor);
+ }
+
+ [CommandUpdateHandler (EditCommands.SortSelectedLines)]
+ void UpdateSortSelectedLines (CommandInfo ci)
+ {
+ var region = textEditor.SelectionRegion;
+ ci.Enabled = region.BeginLine != region.EndLine;
+ }
+ #endregion
+
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextLink.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextLink.cs
new file mode 100644
index 0000000000..f5f25604bb
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextLink.cs
@@ -0,0 +1,136 @@
+//
+// TextLink.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 MonoDevelop.Ide.CodeTemplates;
+using MonoDevelop.Core.Text;
+using System.Collections.Generic;
+
+namespace MonoDevelop.Ide.Editor
+{
+ public sealed class TextLink : IListDataProvider<string>
+ {
+ public ISegment PrimaryLink {
+ get {
+ if (links.Count == 0)
+ return TextSegment.Invalid;
+ return links [0];
+ }
+ }
+
+ List<ISegment> links = new List<ISegment> ();
+
+ public List<ISegment> Links {
+ get {
+ return links;
+ }
+ set {
+ links = value;
+ }
+ }
+
+ public bool IsIdentifier {
+ get;
+ set;
+ }
+
+ public bool IsEditable {
+ get;
+ set;
+ }
+
+ public string Name {
+ get;
+ set;
+ }
+
+ public string CurrentText {
+ get;
+ set;
+ }
+
+ public string Tooltip {
+ get;
+ set;
+ }
+
+ public IListDataProvider<string> Values {
+ get;
+ set;
+ }
+
+ public Func<Func<string, string>, IListDataProvider<string>> GetStringFunc {
+ get;
+ set;
+ }
+
+ public TextLink (string name)
+ {
+ IsEditable = true;
+ this.Name = name;
+ this.IsIdentifier = false;
+ }
+
+ public override string ToString ()
+ {
+ return string.Format ("[TextLink: Name={0}, Links={1}, IsEditable={2}, Tooltip={3}, CurrentText={4}, Values=({5})]",
+ Name,
+ Links.Count,
+ IsEditable,
+ Tooltip,
+ CurrentText,
+ Values.Count);
+ }
+
+ public void AddLink (ISegment segment)
+ {
+ links.Add (segment);
+ }
+ #region IListDataProvider implementation
+ public string GetText (int n)
+ {
+ return Values != null ? Values.GetText (n) : "";
+ }
+
+ public string this [int n] {
+ get {
+ return Values != null ? Values [n] : "";
+ }
+ }
+
+ public Xwt.Drawing.Image GetIcon (int n)
+ {
+ return Values != null ? Values.GetIcon (n) : null;
+ }
+
+ public int Count {
+ get {
+ return Values != null ? Values.Count : 0;
+ }
+ }
+ #endregion
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextLinkModeEventArgs.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextLinkModeEventArgs.cs
new file mode 100644
index 0000000000..f32d8596c9
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextLinkModeEventArgs.cs
@@ -0,0 +1,43 @@
+//
+// TextLinkModeEventArgs.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;
+
+namespace MonoDevelop.Ide.Editor
+{
+ [Serializable]
+ public sealed class TextLinkModeEventArgs : EventArgs
+ {
+ public bool Success {
+ get;
+ private set;
+ }
+
+ public TextLinkModeEventArgs (bool success)
+ {
+ this.Success = success;
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextLinkModeOptions.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextLinkModeOptions.cs
new file mode 100644
index 0000000000..ef20516cf9
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextLinkModeOptions.cs
@@ -0,0 +1,66 @@
+//
+// ITextEditorImpl.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.Collections.Generic;
+
+namespace MonoDevelop.Ide.Editor
+{
+ /// <summary>
+ /// This class contains information the editor needs to initiate the text link mode.
+ /// </summary>
+ public sealed class TextLinkModeOptions
+ {
+ /// <summary>
+ /// The text links. Note that this property will change to IReadOnlyList in future versions.
+ /// </summary>
+ public IList<TextLink> Links {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// That's the action that is started after the text link mode ended.
+ /// This may be null (in that case no action is started).
+ /// </summary>
+ public Action<TextLinkModeEventArgs> ModeExitedAction {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MonoDevelop.Ide.Editor.TextLinkModeOptions"/> class.
+ /// </summary>
+ /// <param name="links">The text links. </param>
+ /// <param name="modeExitedAction">That's the action that is started after the text link mode ended.</param>
+ public TextLinkModeOptions (IList<TextLink> links, Action<TextLinkModeEventArgs> modeExitedAction = null)
+ {
+ if (links == null)
+ throw new ArgumentNullException ("links");
+ Links = links;
+ ModeExitedAction = modeExitedAction;
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextMarkerFactory.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextMarkerFactory.cs
new file mode 100644
index 0000000000..a185dd93ba
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextMarkerFactory.cs
@@ -0,0 +1,121 @@
+//
+// TextMarkerFactory.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 MonoDevelop.Core.Text;
+using MonoDevelop.Ide.Editor.Extension;
+using MonoDevelop.Ide.TypeSystem;
+
+namespace MonoDevelop.Ide.Editor
+{
+ /// <summary>
+ /// The text marker factory creates line and segment markers for the text editor.
+ /// Note that this is the only valid way of creating markers for the editor.
+ /// </summary>
+ public static class TextMarkerFactory
+ {
+ #region Line marker
+ public static IUrlTextLineMarker CreateUrlTextMarker (TextEditor editor, IDocumentLine line, string value, UrlType url, string syntax, int startCol, int endCol)
+ {
+ return editor.TextMarkerFactory.CreateUrlTextMarker (editor, line, value, url, syntax, startCol, endCol);
+ }
+
+ public static ICurrentDebugLineTextMarker CreateCurrentDebugLineTextMarker (TextEditor editor)
+ {
+ return editor.TextMarkerFactory.CreateCurrentDebugLineTextMarker (editor);
+ }
+
+ public static ITextLineMarker CreateAsmLineMarker (TextEditor editor)
+ {
+ return editor.TextMarkerFactory.CreateAsmLineMarker (editor);
+ }
+
+ public static IUnitTestMarker CreateUnitTestMarker (TextEditor editor, UnitTestMarkerHost host, UnitTestLocation unitTestLocation)
+ {
+ return editor.TextMarkerFactory.CreateUnitTestMarker (editor, host, unitTestLocation);
+ }
+
+ public static IMessageBubbleLineMarker CreateMessageBubbleLineMarker (TextEditor editor)
+ {
+ return editor.TextMarkerFactory.CreateMessageBubbleLineMarker (editor);
+ }
+
+
+ #endregion
+
+ #region Segment marker
+ public static ITextSegmentMarker CreateUsageMarker (TextEditor editor, Usage usage)
+ {
+ return editor.TextMarkerFactory.CreateUsageMarker (editor, usage);
+ }
+
+ public static ITextSegmentMarker CreateLinkMarker (TextEditor editor, int offset, int length, Action<LinkRequest> activateLink)
+ {
+ return editor.TextMarkerFactory.CreateLinkMarker (editor, offset, length, activateLink);
+ }
+
+ public static ITextSegmentMarker CreateLinkMarker (TextEditor editor, ISegment segment, Action<LinkRequest> activateLink)
+ {
+ if (segment == null)
+ throw new ArgumentNullException ("segment");
+ return editor.TextMarkerFactory.CreateLinkMarker (editor, segment.Offset, segment.Length, activateLink);
+ }
+
+ public static IGenericTextSegmentMarker CreateGenericTextSegmentMarker (TextEditor editor, TextSegmentMarkerEffect effect, int offset, int length)
+ {
+ return editor.TextMarkerFactory.CreateGenericTextSegmentMarker (editor, effect, offset, length);
+ }
+
+ public static IGenericTextSegmentMarker CreateGenericTextSegmentMarker (TextEditor editor, TextSegmentMarkerEffect effect, ISegment segment)
+ {
+ if (segment == null)
+ throw new ArgumentNullException ("segment");
+ return editor.TextMarkerFactory.CreateGenericTextSegmentMarker (editor, effect, segment.Offset, segment.Length);
+ }
+
+ public static ISmartTagMarker CreateSmartTagMarker (TextEditor editor, int offset, DocumentLocation realLocation)
+ {
+ return editor.TextMarkerFactory.CreateSmartTagMarker (editor, offset, realLocation);
+ }
+
+ static bool IsIdentifierPart (char ch)
+ {
+ return char.IsLetterOrDigit (ch) || ch == '_';
+ }
+
+ public static IErrorMarker CreateErrorMarker (TextEditor editor, Error info)
+ {
+ int offset = editor.LocationToOffset (info.Region.BeginLine, info.Region.BeginColumn);
+ int endOffset = editor.LocationToOffset (info.Region.EndLine, info.Region.EndColumn);
+ if (endOffset < offset) {
+ endOffset = offset + 1;
+ while (endOffset < editor.Length && IsIdentifierPart (editor.GetCharAt (endOffset)))
+ endOffset++;
+ }
+ return editor.TextMarkerFactory.CreateErrorMarker (editor, info, offset, endOffset - offset);
+ }
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextMarkerMouseEventArgs.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextMarkerMouseEventArgs.cs
new file mode 100644
index 0000000000..7a66ca5924
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextMarkerMouseEventArgs.cs
@@ -0,0 +1,49 @@
+//
+// ITextSegmentMarker.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 MonoDevelop.Core.Text;
+using MonoDevelop.Ide.Editor;
+
+namespace MonoDevelop.Ide.Editor
+{
+ public abstract class TextMarkerMouseEventArgs : EventArgs
+ {
+ public abstract double X {
+ get;
+ }
+
+ public abstract double Y {
+ get;
+ }
+
+ public abstract object OverwriteCursor { get; set; }
+
+ public abstract string TooltipMarkup { get; set; }
+
+ public abstract bool TriggersContextMenu ();
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TooltipExtensionNode.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TooltipExtensionNode.cs
new file mode 100644
index 0000000000..d47513e925
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TooltipExtensionNode.cs
@@ -0,0 +1,49 @@
+//
+// TooltipExtensionNode.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2015 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 MonoDevelop.Core.Text;
+using MonoDevelop.Components;
+using MonoDevelop.Ide.CodeCompletion;
+using Mono.Addins;
+
+namespace MonoDevelop.Ide.Editor
+{
+ sealed class TooltipExtensionNode : TypeExtensionNode
+ {
+ [NodeAttribute("mimeType", false, "The mimetype that this tooltip provider can handle.")]
+ string mimeType;
+
+ public string MimeType {
+ get { return mimeType; }
+ }
+
+ internal bool IsValidFor (string mimeType)
+ {
+ return string.IsNullOrEmpty (this.mimeType) || this.mimeType == mimeType;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TooltipProvider.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TooltipProvider.cs
new file mode 100644
index 0000000000..e397e54664
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TooltipProvider.cs
@@ -0,0 +1,177 @@
+//
+// TooltipProvider.cs
+//
+// Author:
+// Lluis Sanchez <lluis@xamarin.com>
+//
+// Copyright (c) 2012 Xamarin Inc
+//
+// 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 MonoDevelop.Core.Text;
+using MonoDevelop.Components;
+using MonoDevelop.Ide.CodeCompletion;
+
+namespace MonoDevelop.Ide.Editor
+{
+ public sealed class TooltipItem : ISegment
+ {
+ int offset;
+ int length;
+
+ #region ISegment implementation
+
+ public int Offset {
+ get {
+ return offset;
+ }
+ internal set {
+ offset = value;
+ }
+ }
+
+ public int Length {
+ get {
+ return length;
+ }
+ internal set {
+ length = value;
+ }
+ }
+
+ public int EndOffset {
+ get {
+ return offset + length;
+ }
+ }
+
+ #endregion
+
+ public object Item { get; set; }
+
+ public TooltipItem (object item, ISegment itemSegment)
+ {
+ if (itemSegment == null)
+ throw new ArgumentNullException ("itemSegment");
+ Item = item;
+ this.offset = itemSegment.Offset;
+ this.length = itemSegment.Length;
+ }
+
+ public TooltipItem (object item, int offset, int length)
+ {
+ Item = item;
+ this.offset = offset;
+ this.length = length;
+ }
+ }
+
+ // TODO: Improve tooltip API - that really looks messy
+ public abstract class TooltipProvider
+ {
+ public abstract TooltipItem GetItem (TextEditor editor, DocumentContext ctx, int offset);
+
+ public virtual bool IsInteractive (TextEditor editor, Control tipWindow)
+ {
+ return false;
+ }
+
+ public virtual void GetRequiredPosition (TextEditor editor, Control tipWindow, out int requiredWidth, out double xalign)
+ {
+ requiredWidth = ((Gtk.Widget)tipWindow).SizeRequest ().Width;
+ xalign = 0.5;
+ }
+
+ public virtual Control CreateTooltipWindow (TextEditor editor, DocumentContext ctx, TooltipItem item, int offset, Gdk.ModifierType modifierState)
+ {
+ return null;
+ }
+
+ protected Xwt.Rectangle GetAllocation (TextEditor editor)
+ {
+ return editor.GetContent<ITextEditorImpl> ().GetEditorAllocation ();
+ }
+
+ void ShowTipInfoWindow (TextEditor editor, TooltipInformationWindow tipWindow, TooltipItem item, Gdk.ModifierType modifierState, int mouseX, int mouseY)
+ {
+ Gtk.Widget editorWidget = editor;
+
+ var startLoc = editor.OffsetToLocation (item.Offset);
+ var endLoc = editor.OffsetToLocation (item.EndOffset);
+ var p1 = editor.LocationToPoint (startLoc);
+ var p2 = editor.LocationToPoint (endLoc);
+
+ int w = (int)(p2.X - p1.X);
+
+ var caret = new Gdk.Rectangle (
+ (int)p1.X,
+ (int)p1.Y,
+ (int)w,
+ (int)editor.LineHeight
+ );
+
+ tipWindow.ShowPopup (editorWidget, caret, PopupPosition.Top);
+ }
+
+ public virtual void ShowTooltipWindow (TextEditor editor, Control tipWindow, TooltipItem item, Gdk.ModifierType modifierState, int mouseX, int mouseY)
+ {
+ if (tipWindow == null)
+ return;
+
+ var tipInfoWindow = ((Gtk.Widget)tipWindow) as TooltipInformationWindow;
+ if (tipInfoWindow != null) {
+ ShowTipInfoWindow (editor, tipInfoWindow, item, modifierState, mouseX, mouseY);
+ return;
+ }
+
+ var origin = editor.GetContent<ITextEditorImpl> ().GetEditorWindowOrigin ();
+
+ int w;
+ double xalign;
+ GetRequiredPosition (editor, tipWindow, out w, out xalign);
+ w += 10;
+
+ var allocation = GetAllocation (editor);
+ int x = (int)(mouseX + origin.X + allocation.X);
+ int y = (int)(mouseY + origin.Y + allocation.Y);
+ Gtk.Widget widget = editor;
+ var geometry = widget.Screen.GetUsableMonitorGeometry (widget.Screen.GetMonitorAtPoint (x, y));
+
+ x -= (int) ((double) w * xalign);
+ y += 10;
+
+ if (x + w >= geometry.X + geometry.Width)
+ x = geometry.X + geometry.Width - w;
+ if (x < geometry.Left)
+ x = geometry.Left;
+
+ var gtkWindow = (Gtk.Window)tipWindow;
+ int h = gtkWindow.SizeRequest ().Height;
+ if (y + h >= geometry.Y + geometry.Height)
+ y = geometry.Y + geometry.Height - h;
+ if (y < geometry.Top)
+ y = geometry.Top;
+
+ gtkWindow.Move (x, y);
+
+ gtkWindow.ShowAll ();
+ }
+ }
+}
+
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/WordFindStrategy.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/WordFindStrategy.cs
new file mode 100644
index 0000000000..d850856538
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/WordFindStrategy.cs
@@ -0,0 +1,38 @@
+//
+// WordFindStrategy.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;
+
+namespace MonoDevelop.Ide.Editor
+{
+ public enum WordFindStrategy
+ {
+ MonoDevelop,
+ Emacs,
+ SharpDevelop,
+ Vim
+ }
+}
+