diff options
Diffstat (limited to 'main/src/core/MonoDevelop.Ide')
293 files changed, 23121 insertions, 8815 deletions
diff --git a/main/src/core/MonoDevelop.Ide/AssemblyInfo.cs b/main/src/core/MonoDevelop.Ide/AssemblyInfo.cs index df2563993f..6d7c8abadf 100644 --- a/main/src/core/MonoDevelop.Ide/AssemblyInfo.cs +++ b/main/src/core/MonoDevelop.Ide/AssemblyInfo.cs @@ -14,7 +14,17 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("MonoDevelop.DesignerSupport")] [assembly: InternalsVisibleTo("Ide.Tests")] [assembly: InternalsVisibleTo("UnitTests")] +[assembly: InternalsVisibleTo("MonoDevelop.SourceEditor")] +[assembly: InternalsVisibleTo("Xamarin.OSXEditor")] [assembly: InternalsVisibleTo("MonoDevelop.Refactoring")] +[assembly: InternalsVisibleTo("MonoDevelop.CSharpBinding")] +[assembly: InternalsVisibleTo("FSharpBinding")] +[assembly: InternalsVisibleTo("MonoDevelop.CBinding")] +[assembly: InternalsVisibleTo("MonoDevelop.Xml")] +[assembly: InternalsVisibleTo("MonoDevelop.AspNet")] [assembly: InternalsVisibleTo("Xamarin.Ide")] [assembly: InternalsVisibleTo("MonoDevelop.SourceEditor")] [assembly: InternalsVisibleTo("MonoDevelop.SourceEditor2")] +[assembly: InternalsVisibleTo("MonoDevelop.AssemblyBrowser")] +[assembly: InternalsVisibleTo("MonoDevelop.AspNet")] +[assembly: InternalsVisibleTo("Xamarin.Sketches")] diff --git a/main/src/core/MonoDevelop.Ide/ExtensionModel/Commands.addin.xml b/main/src/core/MonoDevelop.Ide/ExtensionModel/Commands.addin.xml index 7697bc917a..f98ef0fbde 100644 --- a/main/src/core/MonoDevelop.Ide/ExtensionModel/Commands.addin.xml +++ b/main/src/core/MonoDevelop.Ide/ExtensionModel/Commands.addin.xml @@ -1021,8 +1021,28 @@ _label = "Toggle block selection mode"/> <Command id = "MonoDevelop.Ide.Commands.TextEditorCommands.DuplicateLine" _label = "Duplicate line"/> + <Command id = "MonoDevelop.Ide.Commands.TextEditorCommands.DynamicAbbrev" + defaultHandler = "MonoDevelop.Ide.Editor.DynamicAbbrevHandler" + _label = "Dynamic abbrev" + _description = "Cycles completing the current word from matching words in all open files" + macShortcut = "Ctrl|/" + shortcut = "Alt|/" /> + <Command id = "MonoDevelop.Ide.Commands.TextEditorCommands.PulseCaret" + _label = "Find caret" + _description = "Animates the text editor caret to help find it" + shortcut = "Ctrl||" + macShortcut = "Meta||" /> + + <Command id = "MonoDevelop.Ide.Editor.MessageBubbleCommands.Toggle" + _label = "Toggle message bubble" + _description = "Toggles message bubble on/off"/> + <Command id = "MonoDevelop.Ide.Editor.MessageBubbleCommands.HideIssues" + defaultHandler = "MonoDevelop.Ide.Editor.HideIssuesHandler" + _label = "_Hide Issues"/> + <Command id = "MonoDevelop.Ide.Editor.MessageBubbleCommands.ToggleIssues" + defaultHandler = "MonoDevelop.Ide.Editor.ToggleIssuesHandler" + _label = "Toggle Issues" + type="array"/> </Category> - </Extension> - </ExtensionModel> diff --git a/main/src/core/MonoDevelop.Ide/ExtensionModel/MainMenu.addin.xml b/main/src/core/MonoDevelop.Ide/ExtensionModel/MainMenu.addin.xml index 25cda3ed16..75c69530f4 100644 --- a/main/src/core/MonoDevelop.Ide/ExtensionModel/MainMenu.addin.xml +++ b/main/src/core/MonoDevelop.Ide/ExtensionModel/MainMenu.addin.xml @@ -115,6 +115,12 @@ <CommandItem id = "MonoDevelop.Ide.Commands.ViewCommands.DeleteCurrentLayout" /> <SeparatorItem id = "ViewSeparator2" /> <CommandItem id = "MonoDevelop.Ide.Commands.ViewCommands.ViewList" /> + <ItemSet id = "MessageBubbles" _label = "_Message Bubbles"> + <CommandItem id = "MonoDevelop.Ide.Editor.MessageBubbleCommands.Toggle" /> + <CommandItem id = "MonoDevelop.Ide.Editor.MessageBubbleCommands.HideIssues" /> + <SeparatorItem id = "Separator1" /> + <CommandItem id = "MonoDevelop.Ide.Editor.MessageBubbleCommands.ToggleIssues" /> + </ItemSet> <SeparatorItem id = "ViewSeparator3" /> <SeparatorItem id = "ViewItemsSeparator" /> <CommandItem id = "MonoDevelop.Ide.Commands.ViewCommands.ZoomIn" /> @@ -128,6 +134,7 @@ <CommandItem id = "MonoDevelop.Ide.Commands.ViewCommands.FocusCurrentDocument" /> <CommandItem id = "MonoDevelop.Ide.Commands.ViewCommands.ShowWelcomePage" /> <CommandItem id = "MonoDevelop.Ide.Commands.ViewCommands.FullScreen" /> + </ItemSet> <ItemSet id = "Search" _label = "_Search"> diff --git a/main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml b/main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml index c510afb657..6cdd5cb768 100644 --- a/main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml +++ b/main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml @@ -155,15 +155,43 @@ <ExtensionNode name="Parser" type="MonoDevelop.Ide.Extensions.MimeTypeExtensionNode"/> </ExtensionPoint> - <ExtensionPoint path = "/MonoDevelop/TypeSystem/Ambiences" name = "Language ambiences"> + <ExtensionPoint path = "/MonoDevelop/TypeSystem/AmbienceTooltipProviders" name = "Language ambiences"> <Description>Language ambiences. Specified classes must implement IAmbience.</Description> - <ExtensionNode name="Ambience" type="MonoDevelop.Ide.Extensions.MimeTypeExtensionNode" /> + <ExtensionNode name="Provider" type="MonoDevelop.Ide.Extensions.MimeTypeExtensionNode" /> </ExtensionPoint> <ExtensionPoint path = "/MonoDevelop/TypeSystem/CodeGenerators"> <ExtensionNode name="Generator" type="MonoDevelop.Ide.Extensions.MimeTypeExtensionNode"/> </ExtensionPoint> + <ExtensionPoint path = "/MonoDevelop/SourceEditor2/SyntaxModes" name = "Text editor syntax modes"> + <Description>Syntax modes</Description> + <ExtensionNode name="Templates" type="MonoDevelop.Ide.Editor.Highlighting.TemplateCodon"/> + </ExtensionPoint> + + <ExtensionPoint path = "/MonoDevelop/SourceEditor2/Styles" name = "Text editor styles"> + <Description>Styles</Description> + <ExtensionNode name="Templates" type="MonoDevelop.Ide.Editor.Highlighting.TemplateCodon"/> + </ExtensionPoint> + + <ExtensionPoint path = "/MonoDevelop/SourceEditor2/CustomModes" name = "Text editor custom syntax modes"> + <Description>Styles</Description> + <ExtensionNode name="SyntaxMode" type="MonoDevelop.Ide.Editor.Highlighting.SyntaxModeCodon"/> + </ExtensionPoint> + + <ExtensionPoint path = "/MonoDevelop/SourceEditor2/EditorFactory" name = "Editor factories"> + <Description>Allows providing a platform-specific implementation for the text editor</Description> + <ExtensionNode name="Class" /> + </ExtensionPoint> + + <ExtensionPoint path = "/MonoDevelop/SourceEditor2/TooltipProviders" name = "Tooltip providers"> + <Description>Tooltip providers. Classes must implement ITooltipProvider.</Description> + <ExtensionNode name="Class" type="MonoDevelop.Ide.Editor.TooltipExtensionNode" /> + <ConditionType id="FileType" type="MonoDevelop.Ide.Extensions.FileTypeCondition"> + <Description>Type of the file being edited.</Description> + </ConditionType> + </ExtensionPoint> + <!-- Extensions --> <Extension path = "/MonoDevelop/Core/Applications"> @@ -179,6 +207,8 @@ <Extension path = "/MonoDevelop/Ide/DisplayBindings"> <DisplayBinding id = "DefaultDisplayBinding" class = "MonoDevelop.Ide.Gui.DefaultDisplayBinding"/> + <DisplayBinding id = "TextEditor" + class = "MonoDevelop.Ide.Editor.TextEditorDisplayBinding" /> </Extension> <Extension path = "/MonoDevelop/Ide/StartupHandlers"> @@ -228,9 +258,9 @@ </Extension> <Extension path = "/MonoDevelop/Ide/TextEditorExtensions"> - <Class id="InitialStep" class = "MonoDevelop.Ide.Gui.Content.TextEditorExtensionMarker" /> - <Class id="MidStep" class = "MonoDevelop.Ide.Gui.Content.TextEditorExtensionMarker" /> - <Class id="FinalStep" class = "MonoDevelop.Ide.Gui.Content.TextEditorExtensionMarker" /> + <Class id="InitialStep" class = "MonoDevelop.Ide.Editor.Extension.TextEditorExtensionMarker" /> + <Class id="MidStep" class = "MonoDevelop.Ide.Editor.Extension.TextEditorExtensionMarker" /> + <Class id="FinalStep" class = "MonoDevelop.Ide.Editor.Extension.TextEditorExtensionMarker" /> </Extension> <Extension path = "/MonoDevelop/Ide/ContextMenu/DocumentTab"> diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/BasicChart.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/BasicChart.cs index 28366ed5eb..6ed67e3ac5 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/BasicChart.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Chart/BasicChart.cs @@ -28,10 +28,8 @@ using System; using System.Collections; -using System.Collections.Generic; using Gtk; using Gdk; -using Mono.TextEditor; namespace MonoDevelop.Components.Chart { @@ -577,9 +575,9 @@ namespace MonoDevelop.Components.Chart win.DrawLine (gc, px, top + height, px, top + height - tick); } } - else {
- if (Math.Abs ((long)lastPos - (long)py) < minTickStep || py < top || py > top + height)
- continue;
+ else { + if (Math.Abs ((long)lastPos - (long)py) < minTickStep || py < top || py > top + height) + continue; lastPos = py; bool labelFits = false; @@ -780,32 +778,32 @@ namespace MonoDevelop.Components.Chart } void GetPoint (double wx, double wy, out double x, out double y) - {
- unchecked {
- if (reverseXAxis)
- x = left + width - (((wx - startX) * ((double) width)) / (endX - startX));
- else
- x = left + (((wx - startX) * ((double) width)) / (endX - startX));
-
- if (reverseYAxis)
- y = top + ((wy - startY) * ((double) height) / (endY - startY));
- else
- y = top + height - ((wy - startY) * ((double) height) / (endY - startY));
+ { + unchecked { + if (reverseXAxis) + x = left + width - (((wx - startX) * ((double) width)) / (endX - startX)); + else + x = left + (((wx - startX) * ((double) width)) / (endX - startX)); + + if (reverseYAxis) + y = top + ((wy - startY) * ((double) height) / (endY - startY)); + else + y = top + height - ((wy - startY) * ((double) height) / (endY - startY)); } } void GetValue (int x, int y, out double wx, out double wy) - {
- unchecked {
- if (reverseXAxis)
- wx = startX + ((double) (left + width - 1 - x)) * (endX - startX) / (double) width;
- else
- wx = startX + ((double) (x - left)) * (endX - startX) / (double) width;
-
- if (reverseYAxis)
- wy = startY + ((double) (top + y)) * (endY - startY) / (double) height;
- else
- wy = startY + ((double) (top + height - y - 1)) * (endY - startY) / (double) height;
+ { + unchecked { + if (reverseXAxis) + wx = startX + ((double) (left + width - 1 - x)) * (endX - startX) / (double) width; + else + wx = startX + ((double) (x - left)) * (endX - startX) / (double) width; + + if (reverseYAxis) + wy = startY + ((double) (top + y)) * (endY - startY) / (double) height; + else + wy = startY + ((double) (top + height - y - 1)) * (endY - startY) / (double) height; } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandFrame.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandFrame.cs index 836250a967..266d6f83af 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandFrame.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandFrame.cs @@ -26,10 +26,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -using System; using Mono.Addins; using MonoDevelop.Components.DockToolbars; -using Mono.TextEditor; namespace MonoDevelop.Components.Commands { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandManager.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandManager.cs index c7064e52e8..aefb043257 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandManager.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandManager.cs @@ -34,7 +34,6 @@ using System.Collections.Generic; using System.Linq; using MonoDevelop.Components.Commands.ExtensionNodes; -using Mono.TextEditor; using Mono.Addins; using MonoDevelop.Core; using MonoDevelop.Ide; @@ -847,8 +846,8 @@ namespace MonoDevelop.Components.Commands if (menu is CommandMenu) { ((CommandMenu)menu).InitialCommandTarget = initialCommandTarget ?? parent; } - - Mono.TextEditor.GtkWorkarounds.ShowContextMenu (menu, parent, evt); + + GtkWorkarounds.ShowContextMenu (menu, parent, evt); } /// <summary> diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandMenuBar.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandMenuBar.cs index 3030a5dcbc..b7da31e1be 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandMenuBar.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandMenuBar.cs @@ -26,11 +26,7 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -using System; using Gtk; -using Gdk; -using MonoDevelop.Ide.Gui; -using Mono.TextEditor; namespace MonoDevelop.Components.Commands { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/KeyBindingManager.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/KeyBindingManager.cs index b4a590202b..ec563318db 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/KeyBindingManager.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/KeyBindingManager.cs @@ -26,10 +26,8 @@ // using System; -using System.Collections; using System.Collections.Generic; -using Mono.TextEditor; // Terminology: // chord: A 'chord' is a key binding prefix / modifier meant to allow a diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockWindow.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockWindow.cs index d525e048d8..e74496696c 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockWindow.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockWindow.cs @@ -27,7 +27,6 @@ using Gdk; using Gtk; using MonoDevelop.Components.Docking; -using Mono.TextEditor; using MonoDevelop.Ide; using System.Collections.Generic; using MonoDevelop.Ide.Gui; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/PlaceholderWindow.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/PlaceholderWindow.cs index bf874ce3a7..397d413170 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/PlaceholderWindow.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/PlaceholderWindow.cs @@ -27,7 +27,6 @@ using Gdk; using Gtk; using MonoDevelop.Components.Docking; -using Mono.TextEditor; using MonoDevelop.Ide; using System.Collections.Generic; using MonoDevelop.Ide.Gui; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/TabStrip.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/TabStrip.cs index 4437cfeff1..245c0a2e86 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/TabStrip.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/TabStrip.cs @@ -28,7 +28,6 @@ using System.Linq; using Gdk; using Gtk; using System; -using Mono.TextEditor; using System.Collections.Generic; using Cairo; using MonoDevelop.Components; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbar.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbar.cs index 4e7c905e0f..3e3f6a4b21 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbar.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbar.cs @@ -30,7 +30,6 @@ using System; using System.Collections.Generic; using Gtk; using Gdk; -using Mono.TextEditor; namespace MonoDevelop.Components.DockToolbars { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarFrame.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarFrame.cs index 791bb12428..e1efdd06a6 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarFrame.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/DockToolbarFrame.cs @@ -26,14 +26,11 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -using System; using Gtk; using Gdk; -using System.Collections; using System.Xml; using System.Xml.Serialization; using System.Collections.Generic; -using Mono.TextEditor; namespace MonoDevelop.Components.DockToolbars { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/FixedPanel.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/FixedPanel.cs index ab3daa2305..90ca29dfe6 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/FixedPanel.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockToolbars/FixedPanel.cs @@ -45,7 +45,7 @@ namespace MonoDevelop.Components.DockToolbars public FixedPanel () { - Mono.TextEditor.GtkWorkarounds.FixContainerLeak (this); + GtkWorkarounds.FixContainerLeak (this); WidgetFlags |= WidgetFlags.NoWindow; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/AutoHideBox.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/AutoHideBox.cs index b3d5cf4779..0ad3d05024 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/AutoHideBox.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/AutoHideBox.cs @@ -30,10 +30,8 @@ //#define ANIMATE_DOCKING -using System; using Gtk; using Gdk; -using Mono.TextEditor; namespace MonoDevelop.Components.Docking { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockBar.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockBar.cs index c3d7ae3c1a..2466ff5e6b 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockBar.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockBar.cs @@ -31,9 +31,7 @@ using System; using Gtk; -using System.Collections.Generic; using MonoDevelop.Ide.Gui; -using Mono.TextEditor; namespace MonoDevelop.Components.Docking { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockBarItem.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockBarItem.cs index 4efd300406..b1a04940f1 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockBarItem.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockBarItem.cs @@ -31,7 +31,6 @@ using System; using Gtk; -using Mono.TextEditor; using MonoDevelop.Ide.Gui; using MonoDevelop.Components; using Xwt.Motion; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockContainer.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockContainer.cs index b02b654ba6..aa58869f97 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockContainer.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockContainer.cs @@ -56,7 +56,7 @@ namespace MonoDevelop.Components.Docking public DockContainer (DockFrame frame) { - Mono.TextEditor.GtkWorkarounds.FixContainerLeak (this); + GtkWorkarounds.FixContainerLeak (this); this.Events = EventMask.ButtonPressMask | EventMask.ButtonReleaseMask | EventMask.PointerMotionMask | EventMask.LeaveNotifyMask; this.frame = frame; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockFrame.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockFrame.cs index 3938cb3d6d..98b7666572 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockFrame.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockFrame.cs @@ -68,7 +68,7 @@ namespace MonoDevelop.Components.Docking public DockFrame () { - Mono.TextEditor.GtkWorkarounds.FixContainerLeak (this); + GtkWorkarounds.FixContainerLeak (this); dockBarTop = new DockBar (this, Gtk.PositionType.Top); dockBarBottom = new DockBar (this, Gtk.PositionType.Bottom); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItem.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItem.cs index 4aace5dc41..94febd4940 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItem.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItem.cs @@ -32,7 +32,6 @@ using System; using System.Xml; using Gtk; using Mono.Unix; -using Mono.TextEditor; namespace MonoDevelop.Components.Docking { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemContainer.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemContainer.cs index 6fef6b1d98..d6a332b165 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemContainer.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemContainer.cs @@ -30,9 +30,6 @@ using System; using Gtk; -using Mono.Unix; -using Mono.TextEditor; -using MonoDevelop.Ide.Gui; namespace MonoDevelop.Components.Docking { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemTitleTab.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemTitleTab.cs index 652b1f725b..6d0c73a1f8 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemTitleTab.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemTitleTab.cs @@ -32,7 +32,6 @@ using MonoDevelop.Ide.Gui; using System.Linq; using MonoDevelop.Core; using MonoDevelop.Ide; -using Mono.TextEditor; using MonoDevelop.Components; namespace MonoDevelop.Components.Docking diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/PlaceholderWindow.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/PlaceholderWindow.cs index ff99bf3c61..faf3966c12 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/PlaceholderWindow.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/PlaceholderWindow.cs @@ -110,7 +110,7 @@ namespace MonoDevelop.Components.Docking public void Relocate (int x, int y, int w, int h, bool animate) { - Gdk.Rectangle geometry = Mono.TextEditor.GtkWorkarounds.GetUsableMonitorGeometry (Screen, Screen.GetMonitorAtPoint (x, y)); + Gdk.Rectangle geometry = GtkWorkarounds.GetUsableMonitorGeometry (Screen, Screen.GetMonitorAtPoint (x, y)); if (x < geometry.X) x = geometry.X; if (x + w > geometry.Right) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/TextQuestionDialog.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/TextQuestionDialog.cs index 43d814ebe4..a0c8af95be 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/TextQuestionDialog.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Extensions/TextQuestionDialog.cs @@ -86,7 +86,7 @@ namespace MonoDevelop.Components.Extensions }; md.VBox.PackStart (responseEntry, false, true, 6); - md.AddActionWidget (new Button (Gtk.Stock.Cancel), ResponseType.Cancel); + md.AddActionWidget (new Button (Gtk.Stock.Cancel) { CanDefault = true }, ResponseType.Cancel); md.AddActionWidget (new Button (Gtk.Stock.Ok), ResponseType.Ok); md.DefaultResponse = ResponseType.Cancel; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/FileSearchCategory.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/FileSearchCategory.cs index 34b1968ba3..9e502170ca 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/FileSearchCategory.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/FileSearchCategory.cs @@ -32,11 +32,12 @@ using MonoDevelop.Core.Instrumentation; using MonoDevelop.Projects; using MonoDevelop.Ide.Gui; using MonoDevelop.Ide; -using ICSharpCode.NRefactory.TypeSystem; using MonoDevelop.Ide.TypeSystem; using MonoDevelop.Core.Text; using Gtk; using System.Linq; +using Microsoft.CodeAnalysis; +using ICSharpCode.NRefactory6.CSharp; namespace MonoDevelop.Components.MainToolbar { @@ -51,7 +52,7 @@ namespace MonoDevelop.Components.MainToolbar IEnumerable<ProjectFile> files { get { - foreach (Document doc in IdeApp.Workbench.Documents) { + foreach (var doc in IdeApp.Workbench.Documents) { // We only want to check it here if it's not part // of the open combine. Otherwise, it will get // checked down below. @@ -61,7 +62,7 @@ namespace MonoDevelop.Components.MainToolbar var projects = IdeApp.Workspace.GetAllProjects (); - foreach (Project p in projects) { + foreach (var p in projects) { foreach (ProjectFile file in p.Files) { if (file.Subtype != Subtype.Directory && (file.Flags & ProjectItemFlags.Hidden) != ProjectItemFlags.Hidden) yield return file; @@ -126,9 +127,7 @@ namespace MonoDevelop.Components.MainToolbar class WorkerResult { public List<ProjectFile> filteredFiles = null; - public List<ITypeDefinition> filteredTypes = null; - public List<IMember> filteredMembers = null; - + public string pattern = null; public bool isGotoFilePattern; public ResultsDataSource results; @@ -137,8 +136,6 @@ namespace MonoDevelop.Components.MainToolbar public bool IncludeFiles, IncludeTypes, IncludeMembers; - public Ambience ambience; - public StringMatcher matcher = null; public WorkerResult (Widget widget) @@ -162,18 +159,6 @@ namespace MonoDevelop.Components.MainToolbar return null; } - internal SearchResult CheckType (ITypeDefinition type) - { - int rank; - if (MatchName (type.Name, out rank)) - return new TypeSearchResult (pattern, type.Name, rank, type, false) { Ambience = ambience }; - if (!FullSearch) - return null; - if (MatchName (type.FullName, out rank)) - return new TypeSearchResult (pattern, type.FullName, rank, type, true) { Ambience = ambience }; - return null; - } - Dictionary<string, MatchResult> savedMatches = new Dictionary<string, MatchResult> (); bool MatchName (string name, out int matchRank) { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/ISearchDataSource.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/ISearchDataSource.cs index b1a90a607b..4648be4d3a 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/ISearchDataSource.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/ISearchDataSource.cs @@ -26,8 +26,8 @@ using System.Threading; using System.Threading.Tasks; -using ICSharpCode.NRefactory.TypeSystem; using MonoDevelop.Ide.CodeCompletion; +using MonoDevelop.Core.Text; namespace MonoDevelop.Components.MainToolbar { @@ -42,7 +42,9 @@ namespace MonoDevelop.Components.MainToolbar TooltipInformation GetTooltip (int item); double GetWeight (int item); - DomRegion GetRegion (int item); + ISegment GetRegion (int item); + string GetFileName (int item); + bool CanActivate (int item); void Activate (int item); } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbar.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbar.cs index 0c2f09ee5c..bad71a8f9c 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbar.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbar.cs @@ -30,7 +30,6 @@ using MonoDevelop.Ide; using MonoDevelop.Ide.Commands; using MonoDevelop.Core; using System.Linq; -using MonoDevelop.Core.Assemblies; using MonoDevelop.Components; using Cairo; using MonoDevelop.Projects; @@ -38,15 +37,12 @@ using System.Collections.Generic; using Mono.Addins; using MonoDevelop.Components.Commands.ExtensionNodes; using MonoDevelop.Ide.Gui; -using MonoDevelop.Ide.Execution; using MonoDevelop.Core.Execution; using MonoDevelop.Ide.TypeSystem; using System.Threading; -using ICSharpCode.NRefactory.TypeSystem; -using Mono.TextEditor; +using MonoDevelop.Ide.Editor; using System.Text; - namespace MonoDevelop.Components.MainToolbar { class MainToolbar: Gtk.EventBox, IMainToolbarView diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbarController.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbarController.cs index 123893630e..76d9eaea10 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbarController.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbarController.cs @@ -35,6 +35,7 @@ using Mono.Addins; using MonoDevelop.Projects; using MonoDevelop.Core.Execution; using System.Text; +using MonoDevelop.Ide.TypeSystem; namespace MonoDevelop.Components.MainToolbar { @@ -107,6 +108,10 @@ namespace MonoDevelop.Components.MainToolbar IdeApp.ProjectOperations.CurrentSelectedSolutionChanged += HandleCurrentSelectedSolutionChanged; AddinManager.ExtensionChanged += OnExtensionChanged; + MonoDevelopWorkspace.LoadingFinished += delegate { + ProjectSearchCategory.UpdateSymbolInfos (); + HandleSearchEntryChanged (null, EventArgs.Empty); + }; } public void Initialize () @@ -533,7 +538,6 @@ namespace MonoDevelop.Components.MainToolbar PositionPopup (); popup.ShowAll (); } - popup.Update (pattern); } @@ -545,9 +549,9 @@ namespace MonoDevelop.Components.MainToolbar var doc = IdeApp.Workbench.ActiveDocument; if (doc != null && doc.Editor != null) { doc.Select (); - doc.Editor.Caret.Location = new Mono.TextEditor.DocumentLocation (pattern.LineNumber, pattern.Column > 0 ? pattern.Column : 1); + doc.Editor.CaretLocation = new MonoDevelop.Ide.Editor.DocumentLocation (pattern.LineNumber, pattern.Column > 0 ? pattern.Column : 1); doc.Editor.CenterToCaret (); - doc.Editor.Parent.StartCaretPulseAnimation (); + doc.Editor.StartCaretPulseAnimation (); } return; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/ProjectSearchCategory.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/ProjectSearchCategory.cs index 3af7fa2545..1731f08255 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/ProjectSearchCategory.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/ProjectSearchCategory.cs @@ -32,64 +32,89 @@ using MonoDevelop.Core.Instrumentation; using MonoDevelop.Projects; using MonoDevelop.Ide.Gui; using MonoDevelop.Ide; -using ICSharpCode.NRefactory.TypeSystem; using MonoDevelop.Ide.TypeSystem; using MonoDevelop.Core.Text; using Gtk; using System.Linq; +using ICSharpCode.NRefactory6.CSharp; +using Microsoft.CodeAnalysis; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Collections.Concurrent; namespace MonoDevelop.Components.MainToolbar { class ProjectSearchCategory : SearchCategory { - SearchPopupWindow widget; + static SearchPopupWindow widget; - public ProjectSearchCategory (SearchPopupWindow widget) : base (GettextCatalog.GetString("Solution")) + public ProjectSearchCategory (SearchPopupWindow widget) : base (GettextCatalog.GetString ("Solution")) { - this.widget = widget; - this.lastResult = new WorkerResult (widget); + ProjectSearchCategory.widget = widget; + lastResult = new WorkerResult (widget); } + internal static Task<ImmutableList<DeclaredSymbolInfo>> SymbolInfoTask; + static TimerCounter getMembersTimer = InstrumentationService.CreateTimerCounter ("Time to get all members", "NavigateToDialog"); + static TimerCounter getTypesTimer = InstrumentationService.CreateTimerCounter ("Time to get all types", "NavigateToDialog"); + static CancellationTokenSource symbolInfoTokenSrc = new CancellationTokenSource(); + public static void UpdateSymbolInfos () + { + symbolInfoTokenSrc.Cancel (); + symbolInfoTokenSrc = new CancellationTokenSource(); + CancellationToken token = symbolInfoTokenSrc.Token; + lastResult = new WorkerResult (widget); + SymbolInfoTask = Task.Run (delegate { + return GetSymbolInfos (token); + }, token); + } - static TimerCounter getTypesTimer = InstrumentationService.CreateTimerCounter ("Time to get all types", "NavigateToDialog"); + static ImmutableList<DeclaredSymbolInfo> GetSymbolInfos (CancellationToken token) + { + getTypesTimer.BeginTiming (); + try { + var result = ImmutableList<DeclaredSymbolInfo>.Empty; + Stopwatch sw = new Stopwatch(); + sw.Start (); + foreach (var workspace in TypeSystemService.AllWorkspaces) { + result = result.AddRange (workspace.CurrentSolution.Projects.Select (p => SearchAsync (p, token)).SelectMany (i => i)); + } + sw.Stop (); + return result; + } catch (AggregateException ae) { + ae.Flatten ().Handle (ex => ex is TaskCanceledException); + return ImmutableList<DeclaredSymbolInfo>.Empty; + } catch (TaskCanceledException) { + return ImmutableList<DeclaredSymbolInfo>.Empty; + } finally { + getTypesTimer.EndTiming (); + } + } - IEnumerable<ITypeDefinition> types { - get { - getTypesTimer.BeginTiming (); + static IEnumerable<DeclaredSymbolInfo> SearchAsync(Microsoft.CodeAnalysis.Project project, CancellationToken cancellationToken) + { + var result = new ConcurrentBag<DeclaredSymbolInfo> (); + Parallel.ForEach (project.Documents, async delegate (Microsoft.CodeAnalysis.Document document) { try { - foreach (Document doc in IdeApp.Workbench.Documents) { - // We only want to check it here if it's not part - // of the open combine. Otherwise, it will get - // checked down below. - if (doc.Project == null && doc.IsFile) { - var info = doc.ParsedDocument; - if (info != null) { - var ctx = doc.Compilation; - foreach (var type in ctx.MainAssembly.GetAllTypeDefinitions ()) { - yield return type; - } - } + cancellationToken.ThrowIfCancellationRequested (); + var root = await document.GetSyntaxRootAsync (cancellationToken).ConfigureAwait (false); + foreach (var current in root.DescendantNodesAndSelf (CSharpSyntaxFactsService.DescentIntoSymbolForDeclarationSearch)) { + DeclaredSymbolInfo declaredSymbolInfo; + if (current.TryGetDeclaredSymbolInfo (out declaredSymbolInfo)) { + result.Add (declaredSymbolInfo); } } - - var projects = IdeApp.Workspace.GetAllProjects (); - - foreach (Project p in projects) { - var pctx = TypeSystemService.GetCompilation (p); - foreach (var type in pctx.MainAssembly.GetAllTypeDefinitions ()) - yield return type; - } - } finally { - getTypesTimer.EndTiming (); + } catch (OperationCanceledException) { } - } + }); + return (IEnumerable<DeclaredSymbolInfo>)result; } - WorkerResult lastResult; - string[] typeTags = new [] { "type", "c", "s", "i", "e", "d"}; - string[] memberTags = new [] { "member", "m", "p", "f", "evt"}; + static WorkerResult lastResult; + string[] typeTags = new [] { "type", "c", "s", "i", "e", "d" }; + string[] memberTags = new [] { "member", "m", "p", "f", "evt" }; public override bool IsValidTag (string tag) { @@ -98,7 +123,7 @@ namespace MonoDevelop.Components.MainToolbar public override Task<ISearchDataSource> GetResults (SearchPopupSearchPattern searchPattern, int resultsCount, CancellationToken token) { - return Task.Factory.StartNew (delegate { + return Task.Run (delegate { if (searchPattern.Tag != null && !(typeTags.Contains (searchPattern.Tag) || memberTags.Contains (searchPattern.Tag)) || searchPattern.HasLineNumber) return null; try { @@ -106,11 +131,12 @@ namespace MonoDevelop.Components.MainToolbar newResult.pattern = searchPattern.Pattern; newResult.IncludeFiles = true; newResult.Tag = searchPattern.Tag; - newResult.IncludeTypes = searchPattern.Tag == null || typeTags.Contains (searchPattern.Tag) ; + newResult.IncludeTypes = searchPattern.Tag == null || typeTags.Contains (searchPattern.Tag); newResult.IncludeMembers = searchPattern.Tag == null || memberTags.Contains (searchPattern.Tag); - var firstType = types.FirstOrDefault (); - newResult.ambience = firstType != null ? AmbienceService.GetAmbienceForFile (firstType.Region.FileName) : AmbienceService.DefaultAmbience; - + ImmutableList<DeclaredSymbolInfo> allTypes; + if (SymbolInfoTask == null) + SymbolInfoTask = Task.FromResult(GetSymbolInfos (token)); + allTypes = SymbolInfoTask.Result; string toMatch = searchPattern.Pattern; newResult.matcher = StringMatcher.GetMatcher (toMatch, false); newResult.FullSearch = toMatch.IndexOf ('.') > 0; @@ -119,7 +145,7 @@ namespace MonoDevelop.Components.MainToolbar oldLastResult = new WorkerResult (widget); // var now = DateTime.Now; - AllResults (oldLastResult, newResult, token); + AllResults (oldLastResult, newResult, allTypes, token); newResult.results.SortUpToN (new DataItemComparer (token), resultsCount); lastResult = newResult; // Console.WriteLine ((now - DateTime.Now).TotalMilliseconds); @@ -131,104 +157,58 @@ namespace MonoDevelop.Components.MainToolbar }, token); } - void AllResults (WorkerResult lastResult, WorkerResult newResult, CancellationToken token) + void AllResults (WorkerResult lastResult, WorkerResult newResult, IReadOnlyList<DeclaredSymbolInfo> completeTypeList, CancellationToken token) { if (newResult.isGotoFilePattern) return; uint x = 0; // Search Types if (newResult.IncludeTypes && (newResult.Tag == null || typeTags.Any (t => t == newResult.Tag))) { - newResult.filteredTypes = new List<ITypeDefinition> (); - bool startsWithLastFilter = lastResult.pattern != null && newResult.pattern.StartsWith (lastResult.pattern, StringComparison.Ordinal) && lastResult.filteredTypes != null; - var allTypes = startsWithLastFilter ? lastResult.filteredTypes : types; + newResult.filteredSymbols = new List<DeclaredSymbolInfo> (); + bool startsWithLastFilter = lastResult.pattern != null && newResult.pattern.StartsWith (lastResult.pattern, StringComparison.Ordinal) && lastResult.filteredSymbols != null; + var allTypes = startsWithLastFilter ? lastResult.filteredSymbols : completeTypeList; foreach (var type in allTypes) { - if (unchecked(x++) % 100 == 0 && token.IsCancellationRequested) + if (unchecked(x++) % 100 == 0 && token.IsCancellationRequested) { + newResult.filteredSymbols = null; return; + } + if (type.Kind == DeclaredSymbolInfoKind.Constructor || + type.Kind == DeclaredSymbolInfoKind.Module || + type.Kind == DeclaredSymbolInfoKind.Indexer) + continue; + if (newResult.Tag != null) { - if (newResult.Tag == "c" && type.Kind != TypeKind.Class) + if (newResult.Tag == "c" && type.Kind != DeclaredSymbolInfoKind.Class) continue; - if (newResult.Tag == "s" && type.Kind != TypeKind.Struct) + if (newResult.Tag == "s" && type.Kind != DeclaredSymbolInfoKind.Struct) continue; - if (newResult.Tag == "i" && type.Kind != TypeKind.Interface) + if (newResult.Tag == "i" && type.Kind != DeclaredSymbolInfoKind.Interface) continue; - if (newResult.Tag == "e" && type.Kind != TypeKind.Enum) + if (newResult.Tag == "e" && type.Kind != DeclaredSymbolInfoKind.Enum) + continue; + if (newResult.Tag == "d" && type.Kind != DeclaredSymbolInfoKind.Delegate) + continue; + + if (newResult.Tag == "m" && type.Kind != DeclaredSymbolInfoKind.Method) continue; - if (newResult.Tag == "d" && type.Kind != TypeKind.Delegate) + if (newResult.Tag == "p" && type.Kind != DeclaredSymbolInfoKind.Property) continue; + if (newResult.Tag == "f" && type.Kind != DeclaredSymbolInfoKind.Field) + continue; + if (newResult.Tag == "evt" && type.Kind != DeclaredSymbolInfoKind.Event) + continue; + } SearchResult curResult = newResult.CheckType (type); if (curResult != null) { - newResult.filteredTypes.Add (type); + newResult.filteredSymbols.Add (type); newResult.results.AddResult (curResult); } } } - - // Search members - if (newResult.IncludeMembers && (newResult.Tag == null || memberTags.Any (t => t == newResult.Tag))) { - newResult.filteredMembers = new List<Tuple<ITypeDefinition, IUnresolvedMember>> (); - bool startsWithLastFilter = lastResult.pattern != null && newResult.pattern.StartsWith (lastResult.pattern, StringComparison.Ordinal) && lastResult.filteredMembers != null; - if (startsWithLastFilter) { - foreach (var t in lastResult.filteredMembers) { - if (unchecked(x++) % 100 == 0 && token.IsCancellationRequested) - return; - var member = t.Item2; - if (newResult.Tag != null) { - if (newResult.Tag == "m" && member.SymbolKind != SymbolKind.Method) - continue; - if (newResult.Tag == "p" && member.SymbolKind != SymbolKind.Property) - continue; - if (newResult.Tag == "f" && member.SymbolKind != SymbolKind.Field) - continue; - if (newResult.Tag == "evt" && member.SymbolKind != SymbolKind.Event) - continue; - } - SearchResult curResult = newResult.CheckMember (t.Item1, member); - if (curResult != null) { - newResult.filteredMembers.Add (t); - newResult.results.AddResult (curResult); - } - } - } else { - Func<IUnresolvedMember, bool> mPred = member => { - if (newResult.Tag != null) { - if (newResult.Tag == "m" && member.SymbolKind != SymbolKind.Method) - return false; - if (newResult.Tag == "p" && member.SymbolKind != SymbolKind.Property) - return false; - if (newResult.Tag == "f" && member.SymbolKind != SymbolKind.Field) - return false; - if (newResult.Tag == "evt" && member.SymbolKind != SymbolKind.Event) - return false; - } - return newResult.IsMatchingMember (member); - }; - - getMembersTimer.BeginTiming (); - try { - foreach (var type in types) { - if (type.Kind == TypeKind.Delegate) - continue; - foreach (var p in type.Parts) { - foreach (var member in p.Members.Where (mPred)) { - if (unchecked(x++) % 100 == 0 && token.IsCancellationRequested) - return; - SearchResult curResult = newResult.CheckMember (type, member); - if (curResult != null) { - newResult.filteredMembers.Add (Tuple.Create (type, member)); - newResult.results.AddResult (curResult); - } - } - } - } - } finally { - getMembersTimer.EndTiming (); - } - } - } } - + class WorkerResult { public string Tag { @@ -236,12 +216,12 @@ namespace MonoDevelop.Components.MainToolbar set; } - public List<ProjectFile> filteredFiles; - public List<ITypeDefinition> filteredTypes; - public List<Tuple<ITypeDefinition, IUnresolvedMember>> filteredMembers; + public List<DeclaredSymbolInfo> filteredSymbols; + string pattern2; char firstChar; char[] firstChars; + public string pattern { get { return pattern2; @@ -249,76 +229,42 @@ namespace MonoDevelop.Components.MainToolbar set { pattern2 = value; if (pattern2.Length == 1) { - firstChar = pattern2[0]; + firstChar = pattern2 [0]; firstChars = new [] { char.ToUpper (firstChar), char.ToLower (firstChar) }; } else { firstChars = null; } } } + public bool isGotoFilePattern; public ResultsDataSource results; public bool FullSearch; public bool IncludeFiles, IncludeTypes, IncludeMembers; - public Ambience ambience; public StringMatcher matcher; - + public WorkerResult (Widget widget) { results = new ResultsDataSource (widget); } - - internal SearchResult CheckFile (ProjectFile file) - { - int rank; - string matchString = System.IO.Path.GetFileName (file.FilePath); - if (MatchName (matchString, out rank)) - return new FileSearchResult (pattern, matchString, rank, file, true); - - if (!FullSearch) - return null; - matchString = FileSearchResult.GetRelProjectPath (file); - if (MatchName (matchString, out rank)) - return new FileSearchResult (pattern, matchString, rank, file, false); - - return null; - } - - internal SearchResult CheckType (ITypeDefinition type) + + internal SearchResult CheckType (DeclaredSymbolInfo symbol) { int rank; - if (MatchName (TypeSearchResult.GetPlainText (type, false), out rank)) { - if (type.DeclaringType != null) - rank--; - return new TypeSearchResult (pattern, TypeSearchResult.GetPlainText (type, false), rank, type, false) { Ambience = ambience }; + if (MatchName (symbol.Name, out rank)) { +// if (type.ContainerDisplayName != null) +// rank--; + return new DeclaredSymbolInfoResult (pattern, symbol.Name, rank, symbol, false); } if (!FullSearch) return null; - if (MatchName (TypeSearchResult.GetPlainText (type, true), out rank)) { - if (type.DeclaringType != null) - rank--; - return new TypeSearchResult (pattern, TypeSearchResult.GetPlainText (type, true), rank, type, true) { Ambience = ambience }; + if (MatchName (symbol.FullyQualifiedContainerName, out rank)) { +// if (type.ContainingType != null) +// rank--; + return new DeclaredSymbolInfoResult (pattern, symbol.FullyQualifiedContainerName, rank, symbol, true); } return null; } - - internal SearchResult CheckMember (ITypeDefinition declaringType, IUnresolvedMember member) - { - int rank; - bool useDeclaringTypeName = member is IUnresolvedMethod && (((IUnresolvedMethod)member).IsConstructor || ((IUnresolvedMethod)member).IsDestructor); - string memberName = useDeclaringTypeName ? member.DeclaringTypeDefinition.Name : member.Name; - if (MatchName (memberName, out rank)) - return new MemberSearchResult (pattern, memberName, rank, declaringType, member, false) { Ambience = ambience }; - return null; - } - - internal bool IsMatchingMember (IUnresolvedMember member) - { - int rank; - bool useDeclaringTypeName = member is IUnresolvedMethod && (((IUnresolvedMethod)member).IsConstructor || ((IUnresolvedMethod)member).IsDestructor); - string memberName = useDeclaringTypeName ? member.DeclaringTypeDefinition.Name : member.Name; - return MatchName (memberName, out rank); - } Dictionary<string, MatchResult> savedMatches = new Dictionary<string, MatchResult> (StringComparer.Ordinal); @@ -335,7 +281,7 @@ namespace MonoDevelop.Components.MainToolbar doesMatch = idx >= 0; if (doesMatch) { matchRank = int.MaxValue - (name.Length - 1) * 10 - idx; - if (name[idx] != firstChar) + if (name [idx] != firstChar) matchRank /= 2; return true; } else { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/ResultsDataSource.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/ResultsDataSource.cs index 5de97bdd00..d11204909a 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/ResultsDataSource.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/ResultsDataSource.cs @@ -38,7 +38,7 @@ using MonoDevelop.Core.Instrumentation; using MonoDevelop.Ide.Gui; using MonoDevelop.Ide.CodeCompletion; using MonoDevelop.Components.MainToolbar; -using ICSharpCode.NRefactory.TypeSystem; +using MonoDevelop.Core.Text; namespace MonoDevelop.Components.MainToolbar { @@ -134,10 +134,16 @@ namespace MonoDevelop.Components.MainToolbar return this [item].GetDescriptionMarkupText (widget); } - ICSharpCode.NRefactory.TypeSystem.DomRegion ISearchDataSource.GetRegion (int item) + ISegment ISearchDataSource.GetRegion (int item) { var result = this [item]; - return new DomRegion (result.File, result.Row, result.Column, result.Row, result.Column); + return new TextSegment (result.Offset, result.Length); + } + + string ISearchDataSource.GetFileName (int item) + { + var result = this [item]; + return result.File; } bool ISearchDataSource.CanActivate (int item) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/RoundButton.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/RoundButton.cs index 4a7bd9074a..f7ea8eb214 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/RoundButton.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/RoundButton.cs @@ -30,8 +30,6 @@ using MonoDevelop.Components; using Cairo; using MonoDevelop.Ide; using System.Reflection; -using Mono.TextEditor; - namespace MonoDevelop.Components.MainToolbar { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchCategory.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchCategory.cs index 7c15d26c6f..a2b06f8020 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchCategory.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchCategory.cs @@ -28,7 +28,6 @@ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using ICSharpCode.NRefactory.TypeSystem; namespace MonoDevelop.Components.MainToolbar { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchInSolutionSearchCategory.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchInSolutionSearchCategory.cs index c204647762..82f5bdbaea 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchInSolutionSearchCategory.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchInSolutionSearchCategory.cs @@ -31,6 +31,7 @@ using ICSharpCode.NRefactory.TypeSystem; using MonoDevelop.Ide.FindInFiles; using System.Linq; using MonoDevelop.Ide.Gui; +using MonoDevelop.Core.Text; namespace MonoDevelop.Components.MainToolbar { @@ -88,9 +89,14 @@ namespace MonoDevelop.Components.MainToolbar return 0; } - DomRegion ISearchDataSource.GetRegion (int item) + ISegment ISearchDataSource.GetRegion (int item) { - return DomRegion.Empty; + return TextSegment.Invalid; + } + + string ISearchDataSource.GetFileName (int item) + { + return null; } bool ISearchDataSource.CanActivate (int item) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchPopupWindow.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchPopupWindow.cs index b996ba099c..b062f6ca28 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchPopupWindow.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchPopupWindow.cs @@ -30,11 +30,11 @@ using MonoDevelop.Core; using System.Collections.Generic; using Gtk; using System.Linq; -using ICSharpCode.NRefactory.TypeSystem; using MonoDevelop.Ide; using MonoDevelop.Ide.CodeCompletion; using Mono.Addins; -using Mono.TextEditor; +using MonoDevelop.Ide.Gui; +using MonoDevelop.Core.Text; namespace MonoDevelop.Components.MainToolbar { @@ -84,7 +84,13 @@ namespace MonoDevelop.Components.MainToolbar { throw new NotImplementedException (); } - DomRegion ISearchDataSource.GetRegion (int item) + + ISegment ISearchDataSource.GetRegion (int item) + { + throw new NotImplementedException (); + } + + string ISearchDataSource.GetFileName (int item) { throw new NotImplementedException (); } @@ -176,16 +182,18 @@ namespace MonoDevelop.Components.MainToolbar var region = SelectedItemRegion; Destroy (); - if (string.IsNullOrEmpty (region.FileName)) + if (string.IsNullOrEmpty (SelectedItemFileName)) return; - if (region.Begin.IsEmpty) { + if (region.Length <= 0) { if (Pattern.LineNumber == 0) { - IdeApp.Workbench.OpenDocument (region.FileName); + IdeApp.Workbench.OpenDocument (SelectedItemFileName); } else { - IdeApp.Workbench.OpenDocument (region.FileName, Pattern.LineNumber, Pattern.HasColumn ? Pattern.Column : 1); + IdeApp.Workbench.OpenDocument (SelectedItemFileName, Pattern.LineNumber, Pattern.HasColumn ? Pattern.Column : 1); } } else { - IdeApp.Workbench.OpenDocument (region.FileName, region.BeginLine, region.BeginColumn); + IdeApp.Workbench.OpenDocument (new FileOpenInformation (SelectedItemFileName, null) { + Offset = region.Offset + }); } } } @@ -226,6 +234,8 @@ namespace MonoDevelop.Components.MainToolbar LoggingService.LogError ("Error getting search results", t.Exception); } else { Application.Invoke (delegate { + if (token.IsCancellationRequested) + return; ShowResult (cat, t.Result ?? new NullDataSource ()); }); } @@ -344,13 +354,13 @@ namespace MonoDevelop.Components.MainToolbar return new ItemIdentifier (category, dataSrc, i); } - var region = dataSrc.GetRegion (i); - if (!region.Begin.IsEmpty) { - layout.SetMarkup (region.BeginLine.ToString ()); - int w2, h2; - layout.GetPixelSize (out w2, out h2); - w += w2; - } +// var region = dataSrc.GetRegion (i); +// if (!region.IsEmpty) { +// layout.SetMarkup (region.BeginLine.ToString ()); +// int w2, h2; +// layout.GetPixelSize (out w2, out h2); +// w += w2; +// } itemsAdded++; } if (itemsAdded > 0) @@ -743,14 +753,22 @@ namespace MonoDevelop.Components.MainToolbar handler (this, e); } - public DomRegion SelectedItemRegion { + public ISegment SelectedItemRegion { get { if (selectedItem == null || selectedItem.Item < 0 || selectedItem.Item >= selectedItem.DataSource.ItemCount) - return DomRegion.Empty; + return TextSegment.Invalid; return selectedItem.DataSource.GetRegion (selectedItem.Item); } } + public string SelectedItemFileName { + get { + if (selectedItem == null || selectedItem.Item < 0 || selectedItem.Item >= selectedItem.DataSource.ItemCount) + return null; + return selectedItem.DataSource.GetFileName (selectedItem.Item); + } + } + class ItemIdentifier { public SearchCategory Category { get; private set; } public ISearchDataSource DataSource { get; private set; } @@ -820,13 +838,13 @@ namespace MonoDevelop.Components.MainToolbar return new Cairo.Rectangle (0, y, Allocation.Width, h + itemSeparatorHeight); y += h + itemSeparatorHeight; - var region = dataSrc.GetRegion (i); - if (!region.Begin.IsEmpty) { - layout.SetMarkup (region.BeginLine.ToString ()); - int w2, h2; - layout.GetPixelSize (out w2, out h2); - w += w2; - } +// var region = dataSrc.GetRegion (i); +// if (!region.IsEmpty) { +// layout.SetMarkup (region.BeginLine.ToString ()); +// int w2, h2; +// layout.GetPixelSize (out w2, out h2); +// w += w2; +// } itemsAdded++; } if (itemsAdded > 0) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchResult.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchResult.cs index 4d67e179ff..525016a618 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchResult.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchResult.cs @@ -31,11 +31,14 @@ using Gtk; using MonoDevelop.Core; using MonoDevelop.Core.Text; using MonoDevelop.Projects; -using ICSharpCode.NRefactory.TypeSystem; using MonoDevelop.Ide.TypeSystem; using MonoDevelop.Components.Commands; using MonoDevelop.Ide.CodeCompletion; using MonoDevelop.Ide; +using Microsoft.CodeAnalysis; +using System.Linq; +using ICSharpCode.NRefactory6.CSharp; +using System.Threading; namespace MonoDevelop.Components.MainToolbar { @@ -58,7 +61,7 @@ namespace MonoDevelop.Components.MainToolbar public virtual string GetDescriptionMarkupText (Widget widget) { - return AmbienceService.EscapeText (Description); + return Ambience.EscapeText (Description); } @@ -67,8 +70,8 @@ namespace MonoDevelop.Components.MainToolbar public int Rank { get; private set; } - public virtual int Row { get { return -1; } } - public virtual int Column { get { return -1; } } + public virtual int Offset { get { return -1; } } + public virtual int Length { get { return -1; } } public abstract string File { get; } public abstract Xwt.Drawing.Image Icon { get; } @@ -117,54 +120,49 @@ namespace MonoDevelop.Components.MainToolbar } } - class TypeSearchResult : MemberSearchResult + class DeclaredSymbolInfoResult : SearchResult { - ITypeDefinition type; + bool useFullName; + + DeclaredSymbolInfo type; public override SearchResultType SearchResultType { get { return SearchResultType.Type; } } public override string File { - get { return type.Region.FileName; } + get { return type.Node.GetLocation ().SourceTree.FilePath; } } public override Xwt.Drawing.Image Icon { get { - return ImageService.GetIcon (type.GetStockIcon (false), IconSize.Menu); + return ImageService.GetIcon (type.GetStockIconForSymbolInfo(), IconSize.Menu); } } + - public override int Row { - get { return type.Region.BeginLine; } - } - - public override int Column { - get { return type.Region.BeginColumn; } + public override int Offset { + get { return type.Span.Start; } } - public static string GetPlainText (ITypeDefinition type, bool useFullName) - { - if (type.TypeParameterCount == 0) - return useFullName ? type.FullName : type.Name; - StringBuilder sb = new StringBuilder (useFullName ? type.FullName : type.Name); - sb.Append ("<"); - for (int i = 0; i < type.TypeParameterCount; i++) { - if (i > 0) - sb.Append (", "); - sb.Append (type.TypeParameters [i].Name); - } - sb.Append (">"); - return sb.ToString (); + public override int Length { + get { return type.Span.Length; } } - + public override string PlainText { get { - return GetPlainText (type, false); + return type.Name; } } - public override MonoDevelop.Ide.CodeCompletion.TooltipInformation TooltipInformation { + public override TooltipInformation TooltipInformation { get { - return Ambience.GetTooltip (type); + var docId = TypeSystemService.GetDocuments (type.Node.SyntaxTree.FilePath).FirstOrDefault (); + if (docId == null) { + return new TooltipInformation (); + } + var cancellationToken = default(CancellationToken); + var task = type.GetSymbolAsync (TypeSystemService.GetCodeAnysisDocument (docId, cancellationToken), cancellationToken); + var tooltipInformation = Ambience.GetTooltip (task.Result); + return tooltipInformation; } } @@ -172,34 +170,47 @@ namespace MonoDevelop.Components.MainToolbar get { string loc; MonoDevelop.Projects.Project project; - if (type.TryGetSourceProject (out project)) { - loc = GettextCatalog.GetString ("project {0}", project.Name); - } else { - loc = GettextCatalog.GetString ("file {0}", type.Region.FileName); - } +// if (type.TryGetSourceProject (out project)) { +// loc = GettextCatalog.GetString ("project {0}", project.Name); +// } else { + loc = GettextCatalog.GetString ("file {0}", File); +// } switch (type.Kind) { - case TypeKind.Interface: + case DeclaredSymbolInfoKind.Interface: return GettextCatalog.GetString ("interface ({0})", loc); - case TypeKind.Struct: + case DeclaredSymbolInfoKind.Struct: return GettextCatalog.GetString ("struct ({0})", loc); - case TypeKind.Delegate: + case DeclaredSymbolInfoKind.Delegate: return GettextCatalog.GetString ("delegate ({0})", loc); - case TypeKind.Enum: + case DeclaredSymbolInfoKind.Enum: return GettextCatalog.GetString ("enumeration ({0})", loc); - default: + case DeclaredSymbolInfoKind.Class: return GettextCatalog.GetString ("class ({0})", loc); + + case DeclaredSymbolInfoKind.Field: + return GettextCatalog.GetString ("field ({0})", loc); + case DeclaredSymbolInfoKind.Property: + return GettextCatalog.GetString ("property ({0})", loc); + case DeclaredSymbolInfoKind.Indexer: + return GettextCatalog.GetString ("indexer ({0})", loc); + case DeclaredSymbolInfoKind.Event: + return GettextCatalog.GetString ("event ({0})", loc); + case DeclaredSymbolInfoKind.Method: + return GettextCatalog.GetString ("method ({0})", loc); } + return GettextCatalog.GetString ("symbol ({0})", loc); } } public override string GetMarkupText (Widget widget) { - return HighlightMatch (widget, GetPlainText (type, useFullName), match); + return HighlightMatch (widget, useFullName ? type.FullyQualifiedContainerName : type.Name, match); } - public TypeSearchResult (string match, string matchedString, int rank, ITypeDefinition type, bool useFullName) : base (match, matchedString, rank, null, null, useFullName) + public DeclaredSymbolInfoResult (string match, string matchedString, int rank, DeclaredSymbolInfo type, bool useFullName) : base (match, matchedString, rank) { + this.useFullName = useFullName; this.type = type; } } @@ -261,99 +272,7 @@ namespace MonoDevelop.Components.MainToolbar return file.FilePath; } } - - class MemberSearchResult : SearchResult - { - protected bool useFullName; - protected IUnresolvedMember member; - protected ITypeDefinition declaringType; - - public override SearchResultType SearchResultType { get { return SearchResultType.Member; } } - - protected virtual OutputFlags Flags { - get { - return OutputFlags.IncludeParameters | OutputFlags.IncludeGenerics - | (useFullName ? OutputFlags.UseFullName : OutputFlags.None); - } - } - - public override string PlainText { - get { - return member.Name; - } - } - - public override MonoDevelop.Ide.CodeCompletion.TooltipInformation TooltipInformation { - get { - var ctx = member.DeclaringTypeDefinition.CreateResolveContext (new SimpleTypeResolveContext (declaringType)); - return Ambience.GetTooltip (member.Resolve (ctx)); - } - } - public override string File { - get { return member.DeclaringTypeDefinition.Region.FileName; } - } - - public override Xwt.Drawing.Image Icon { - get { - return ImageService.GetIcon (member.GetStockIcon (false), IconSize.Menu); - } - } - - public override int Row { - get { return member.Region.BeginLine; } - } - - public override int Column { - get { return member.Region.BeginColumn; } - } - - public override string Description { - get { - string loc = GettextCatalog.GetString ("type \"{0}\"", member.DeclaringTypeDefinition.Name); - - switch (member.SymbolKind) { - case SymbolKind.Field: - return GettextCatalog.GetString ("field ({0})", loc); - case SymbolKind.Property: - return GettextCatalog.GetString ("property ({0})", loc); - case SymbolKind.Indexer: - return GettextCatalog.GetString ("indexer ({0})", loc); - case SymbolKind.Event: - return GettextCatalog.GetString ("event ({0})", loc); - case SymbolKind.Method: - return GettextCatalog.GetString ("method ({0})", loc); - case SymbolKind.Operator: - return GettextCatalog.GetString ("operator ({0})", loc); - case SymbolKind.Constructor: - return GettextCatalog.GetString ("constructor ({0})", loc); - case SymbolKind.Destructor: - return GettextCatalog.GetString ("destructor ({0})", loc); - default: - throw new NotSupportedException (member.SymbolKind + " is not supported."); - } - } - } - - public MemberSearchResult (string match, string matchedString, int rank, ITypeDefinition declaringType, IUnresolvedMember member, bool useFullName) : base (match, matchedString, rank) - { - this.declaringType = declaringType; - this.member = member; - this.useFullName = useFullName; - } - - public override string GetMarkupText (Widget widget) - { - if (useFullName) - return HighlightMatch (widget, member.SymbolKind == SymbolKind.Constructor ? member.DeclaringTypeDefinition.FullName : member.FullName, match); - return HighlightMatch (widget, member.SymbolKind == SymbolKind.Constructor ? member.DeclaringTypeDefinition.Name : member.Name, match); - } - - internal Ambience Ambience { - get; - set; - } - } class CommandResult: SearchResult { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/StatusArea.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/StatusArea.cs index a57d05cf83..cfdf239aa3 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/StatusArea.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/StatusArea.cs @@ -35,8 +35,6 @@ using System.Collections.Generic; using MonoDevelop.Ide.CodeCompletion; using MonoDevelop.Core; using MonoDevelop.Ide.Gui.Components; -using Mono.TextEditor; - using StockIcons = MonoDevelop.Ide.Gui.Stock; using Xwt.Motion; using MonoDevelop.Ide.Fonts; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/StatusAreaBuildTheme.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/StatusAreaBuildTheme.cs index a86dc68ade..ce58e8d373 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/StatusAreaBuildTheme.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/StatusAreaBuildTheme.cs @@ -27,7 +27,6 @@ using MonoDevelop.Components; using Cairo;
using StockIcons = MonoDevelop.Ide.Gui.Stock;
-using Mono.TextEditor; namespace MonoDevelop.Components.MainToolbar
{
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/StatusAreaTheme.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/StatusAreaTheme.cs index 8de5c8ebff..2434d1b7df 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/StatusAreaTheme.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/StatusAreaTheme.cs @@ -37,7 +37,6 @@ using MonoDevelop.Core; using MonoDevelop.Ide.Gui.Components; using StockIcons = MonoDevelop.Ide.Gui.Stock; -using Mono.TextEditor; namespace MonoDevelop.Components.MainToolbar { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/ExpandableObjectEditor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/ExpandableObjectEditor.cs new file mode 100644 index 0000000000..a172e7b4d3 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/ExpandableObjectEditor.cs @@ -0,0 +1,61 @@ +/*
+ * ExpandableObjectEditor.cs - Temporary editor until we get expandable object support in main grid
+ *
+ * Part of PropertyGrid - A Gtk# widget that displays and allows
+ * editing of all of an object's public properties
+ *
+ * Authors:
+ * Michael Hutchinson <m.j.hutchinson@gmail.com>
+ * Lluis Sanchez Gual
+ *
+ * Copyright (C) 2005 Michael Hutchinson
+ *
+ * This sourcecode is licenced under The MIT License:
+ *
+ * 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 Gtk;
+
+namespace MonoDevelop.Components.PropertyGrid.PropertyEditors
+{
+ class ExpandableObjectEditor : PropertyEditorCell
+ {
+ protected override string GetValueMarkup ()
+ {
+ string val;
+ if (Property.Converter.CanConvertTo (Context, typeof(string)))
+ val = Property.Converter.ConvertToString (Context, Value);
+ else
+ val = Value != null ? Value.ToString () : "";
+
+ return "<b>" + GLib.Markup.EscapeText (val) + "</b>";
+ }
+
+ protected override IPropertyEditor CreateEditor (Gdk.Rectangle cell_area, StateType state)
+ {
+ if (Property.Converter.CanConvertTo (Context, typeof(string)) && Property.Converter.CanConvertFrom (Context, typeof(string)))
+ return new PropertyTextEditor ();
+ else
+ return null;
+ }
+
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/TextEditor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/PropertyTextEditor.cs index 60734be428..841494977e 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/TextEditor.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/PropertyTextEditor.cs @@ -35,8 +35,8 @@ using Gdk; namespace MonoDevelop.Components.PropertyGrid.PropertyEditors { - [PropertyEditorType (typeof (string))]
- public class TextEditor: HBox, IPropertyEditor + [PropertyEditorType (typeof (string))] + public class PropertyTextEditor: Gtk.HBox, IPropertyEditor { EditSession session; bool disposed; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/EditorManager.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/EditorManager.cs index 918ec9865a..19202cda26 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/EditorManager.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/EditorManager.cs @@ -5,7 +5,7 @@ * editing of all of an object's public properties
*
* Authors:
- * Michael Hutchinson <m.j.hutchinson@gmail.com> + * Michael Hutchinson <m.j.hutchinson@gmail.com>
* Lluis Sanchez Gual
*
* Copyright (C) 2005 Michael Hutchinson
@@ -42,11 +42,11 @@ namespace MonoDevelop.Components.PropertyGrid {
class EditorManager
{
- readonly Dictionary<Type,Type> editors = new Dictionary<Type,Type> (); + readonly Dictionary<Type,Type> editors = new Dictionary<Type,Type> ();
readonly Dictionary<Type,Type> inheritingEditors = new Dictionary<Type, Type>();
readonly Dictionary<Type,Type> surrogates = new Dictionary<Type,Type> ();
- static readonly PropertyEditorCell Default = new PropertyEditorCell (); - static readonly Dictionary<Type,PropertyEditorCell> cellCache = new Dictionary<Type,PropertyEditorCell> (); + static readonly PropertyEditorCell Default = new PropertyEditorCell ();
+ static readonly Dictionary<Type,PropertyEditorCell> cellCache = new Dictionary<Type,PropertyEditorCell> ();
public EditorManager ()
{
@@ -57,12 +57,12 @@ namespace MonoDevelop.Components.PropertyGrid {
foreach (Type t in editorAssembly.GetTypes ()) {
foreach (Attribute currentAttribute in Attribute.GetCustomAttributes (t)) {
- if (currentAttribute.GetType() == typeof (PropertyEditorTypeAttribute)) { + if (currentAttribute.GetType() == typeof (PropertyEditorTypeAttribute)) {
var peta = (PropertyEditorTypeAttribute)currentAttribute;
Type editsType = peta.Type;
- if (t.IsSubclassOf (typeof (PropertyEditorCell))) - if (peta.Inherits) - inheritingEditors.Add (editsType, t); + if (t.IsSubclassOf (typeof (PropertyEditorCell)))
+ if (peta.Inherits)
+ inheritingEditors.Add (editsType, t);
else
editors.Add (editsType, t);
}
@@ -75,105 +75,105 @@ namespace MonoDevelop.Components.PropertyGrid }
public PropertyEditorCell GetEditor (ITypeDescriptorContext context)
- { - var cell = context.PropertyDescriptor.GetEditor (typeof(PropertyEditorCell)) as PropertyEditorCell; - if (cell != null) - return cell; - - Type editorType = GetEditorType (context); - if (editorType == null) - return Default; - - if (typeof(IPropertyEditor).IsAssignableFrom (editorType)) { - if (!typeof(Gtk.Widget).IsAssignableFrom (editorType)) - throw new Exception ("The property editor '" + editorType + "' must be a Gtk Widget"); - return Default; + {
+ var cell = context.PropertyDescriptor.GetEditor (typeof(PropertyEditorCell)) as PropertyEditorCell;
+ if (cell != null)
+ return cell;
+
+ Type editorType = GetEditorType (context);
+ if (editorType == null)
+ return Default;
+
+ if (typeof(IPropertyEditor).IsAssignableFrom (editorType)) {
+ if (!typeof(Gtk.Widget).IsAssignableFrom (editorType))
+ throw new Exception ("The property editor '" + editorType + "' must be a Gtk Widget");
+ return Default;
}
- - if (cellCache.TryGetValue (editorType, out cell)) { +
+ if (cellCache.TryGetValue (editorType, out cell)) {
return cell;
- } - - if (!typeof(PropertyEditorCell).IsAssignableFrom (editorType)) - throw new Exception ("The property editor '" + editorType + "' must be a subclass of Stetic.PropertyEditorCell or implement Stetic.IPropertyEditor"); - - cell = (PropertyEditorCell) Activator.CreateInstance (editorType); - cellCache [editorType] = cell; - return cell; + }
+
+ if (!typeof(PropertyEditorCell).IsAssignableFrom (editorType))
+ throw new Exception ("The property editor '" + editorType + "' must be a subclass of Stetic.PropertyEditorCell or implement Stetic.IPropertyEditor");
+
+ cell = (PropertyEditorCell) Activator.CreateInstance (editorType);
+ cellCache [editorType] = cell;
+ return cell;
}
- +
public Type GetEditorType (ITypeDescriptorContext context)
{
var pd = context.PropertyDescriptor;
- +
//try to find a custom editor
//TODO: Find a way to provide a IWindowsFormsEditorService so this can work directly
//for now, substitute GTK#-based editors
- /* + /*
UITypeEditor UITypeEd = (UITypeEditor) pd.GetEditor(typeof (System.Drawing.Design.UITypeEditor));//first, does it have custom editors?
if (UITypeEd != null)
if (surrogates.Contains(UITypeEd.GetType ()))
- return instantiateEditor((Type) surrogates[UITypeEd.GetType()], parentRow); + return instantiateEditor((Type) surrogates[UITypeEd.GetType()], parentRow);
*/
//does a registered GTK# editor support this natively?
Type editType = pd.PropertyType;
if (editors.ContainsKey (editType))
- return editors [editType]; - + return editors [editType];
+
//editors that edit derived types
- //TODO: find most derived type? - foreach (var kvp in inheritingEditors) + //TODO: find most derived type?
+ foreach (var kvp in inheritingEditors)
if (editType.IsSubclassOf (kvp.Key))
return kvp.Value;
- - if (pd.PropertyType.IsEnum) { +
+ if (pd.PropertyType.IsEnum) {
if (pd.PropertyType.IsDefined (typeof(FlagsAttribute), true))
- return typeof(FlagsEditorCell); - return typeof(EnumerationEditorCell); - } - + return typeof(FlagsEditorCell);
+ return typeof(EnumerationEditorCell);
+ }
+
//collections with items of single type that aren't just objects
- if (typeof(System.Collections.IList).IsAssignableFrom (editType)) { - // Iterate through all properties since there may be more than one indexer. - if (GetCollectionItemType (editType) != null) - return typeof (CollectionEditor); - } + if (typeof(System.Collections.IList).IsAssignableFrom (editType)) {
+ // Iterate through all properties since there may be more than one indexer.
+ if (GetCollectionItemType (editType) != null)
+ return typeof (CollectionEditor);
+ }
//TODO: support simple SWF collection editor derivatives that just override Types available
// and reflect protected Type[] NewItemTypes {get;} to get types
//if (UITypeEd is System.ComponentModel.Design.CollectionEditor)
- // ((System.ComponentModel.Design.CollectionEditor)UITypeEd). - - //can we use a type converter with a built-in editor? + // ((System.ComponentModel.Design.CollectionEditor)UITypeEd).
+
+ //can we use a type converter with a built-in editor?
TypeConverter tc = pd.Converter;
//TODO: find best match, not first
foreach (var kvp in editors)
if (tc.CanConvertFrom (kvp.Key) && tc.CanConvertTo (kvp.Key))
- return kvp.Value; - - foreach (var kvp in inheritingEditors) + return kvp.Value;
+
+ foreach (var kvp in inheritingEditors)
if (tc.CanConvertFrom (kvp.Key) && tc.CanConvertTo (kvp.Key))
return kvp.Value;
if (tc.CanConvertTo (typeof(string)) || tc.GetStandardValuesSupported (context)) {
- return typeof(TextEditor);
+ return typeof(PropertyTextEditor);
}
//nothing found - just display type
return null;
- } - - public static Type GetCollectionItemType (Type colType) - { - foreach (PropertyInfo member in colType.GetProperties ()) { - if (member.Name == "Item") { + }
+
+ public static Type GetCollectionItemType (Type colType)
+ {
+ foreach (PropertyInfo member in colType.GetProperties ()) {
+ if (member.Name == "Item") {
if (member.PropertyType != typeof (object))
- return member.PropertyType; - } - } + return member.PropertyType;
+ }
+ }
return null;
}
}
-} +}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyEditorCell.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyEditorCell.cs index c727deb565..7aa4c1407a 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyEditorCell.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyEditorCell.cs @@ -30,7 +30,6 @@ using System; using System.ComponentModel; using Gdk; using Gtk; -using Mono.TextEditor; namespace MonoDevelop.Components.PropertyGrid { @@ -161,27 +160,27 @@ namespace MonoDevelop.Components.PropertyGrid } } - /// <summary>
- /// Whether the editor should show a button.
- /// </summary>
- public virtual bool DialogueEdit {
- get { return false; }
- }
-
- /// <summary>
- /// If the property is read-only, is is usually not edited. If the editor
- /// can edit sub-properties of a read-only complex object, this must return true.
- /// <remarks>The default value is false.</remarks>
- /// </summary>
- /// <returns>True if the editor can edit read-only properties</returns>
- public virtual bool EditsReadOnlyObject {
- get { return false; }
- }
- - public virtual void LaunchDialogue ()
- {
+ /// <summary> + /// Whether the editor should show a button. + /// </summary> + public virtual bool DialogueEdit { + get { return false; } + } + + /// <summary> + /// If the property is read-only, is is usually not edited. If the editor + /// can edit sub-properties of a read-only complex object, this must return true. + /// <remarks>The default value is false.</remarks> + /// </summary> + /// <returns>True if the editor can edit read-only properties</returns> + public virtual bool EditsReadOnlyObject { + get { return false; } + } + + public virtual void LaunchDialogue () + { if (DialogueEdit) - throw new NotImplementedException ();
+ throw new NotImplementedException (); } } @@ -358,15 +357,15 @@ namespace MonoDevelop.Components.PropertyGrid this.cell = cell; Spacing = 3; PackStart (new CellRendererWidget (cell, context), true, true, 0); - Label buttonLabel = new Label ();
- buttonLabel.UseMarkup = true;
- buttonLabel.Xpad = 0; buttonLabel.Ypad = 0;
- buttonLabel.Markup = "<span size=\"small\">...</span>";
- Button dialogueButton = new Button (buttonLabel);
- dialogueButton.Clicked += DialogueButtonClicked;
+ Label buttonLabel = new Label (); + buttonLabel.UseMarkup = true; + buttonLabel.Xpad = 0; buttonLabel.Ypad = 0; + buttonLabel.Markup = "<span size=\"small\">...</span>"; + Button dialogueButton = new Button (buttonLabel); + dialogueButton.Clicked += DialogueButtonClicked; PackStart (dialogueButton, false, false, 0); this.ModifyBg (StateType.Normal, this.Style.White); - ShowAll ();
+ ShowAll (); } void DialogueButtonClicked (object s, EventArgs args) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyGridTable.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyGridTable.cs index dbccc8252f..bbbdc96e16 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyGridTable.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyGridTable.cs @@ -30,7 +30,6 @@ using System.ComponentModel; using System.Collections.Generic; using Cairo; using System.Linq; -using Mono.TextEditor; using MonoDevelop.Core; namespace MonoDevelop.Components.PropertyGrid diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Theming/GtkTheme.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Theming/GtkTheme.cs index 95af7f02ab..8dba0ef095 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Theming/GtkTheme.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Theming/GtkTheme.cs @@ -29,7 +29,6 @@ using System; using Cairo; using Gtk; -using Mono.TextEditor; namespace MonoDevelop.Components.Theming { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/CairoExtensions.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/CairoExtensions.cs index 0b948cd7d0..3a21270833 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/CairoExtensions.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/CairoExtensions.cs @@ -33,7 +33,6 @@ using System.Runtime.InteropServices; using Gdk; using Cairo; using MonoDevelop.Core; -using Mono.TextEditor; namespace MonoDevelop.Components { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/CompactScrolledWindow.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/CompactScrolledWindow.cs index aef0dcf873..e6f0c23f4b 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/CompactScrolledWindow.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/CompactScrolledWindow.cs @@ -25,7 +25,6 @@ // THE SOFTWARE. using System; -using Mono.TextEditor; namespace MonoDevelop.Components { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuExtensionsGtk.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuExtensionsGtk.cs index 52607bf131..035a36cb79 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuExtensionsGtk.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuExtensionsGtk.cs @@ -50,7 +50,7 @@ namespace MonoDevelop.Components if (menu == null) throw new ArgumentNullException ("menu"); - Mono.TextEditor.GtkWorkarounds.ShowContextMenu (menu, parent, evt); + GtkWorkarounds.ShowContextMenu (menu, parent, evt); } static Gtk.MenuItem CreateMenuItem (ContextMenuItem item) @@ -93,7 +93,7 @@ namespace MonoDevelop.Components var img = new ImageView (item.Image); img.ShowAll (); imageItem.Image = img; - GtkWorkarounds.ForceImageOnMenuItem (imageItem); + Xwt.GtkBackend.GtkWorkarounds.ForceImageOnMenuItem (imageItem); } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuTreeView.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuTreeView.cs index a0e32d669e..c6ea099579 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuTreeView.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuTreeView.cs @@ -24,7 +24,6 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using System; -using Mono.TextEditor; namespace MonoDevelop.Components { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Control.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Control.cs index dda73fc351..2cd9d466c5 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Control.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Control.cs @@ -43,6 +43,8 @@ namespace MonoDevelop.Components public Control (object widget) { + if (widget == null) + throw new ArgumentNullException ("widget"); this.nativeWidget = widget; } @@ -63,7 +65,9 @@ namespace MonoDevelop.Components if (!(w is T)) w = ConvertToType (typeof(T), w); if (w is Gtk.Widget) { - var c = new CommandRouterContainer ((Gtk.Widget)w, this, true); + var gtkWidget = (Gtk.Widget)w; + var c = new CommandRouterContainer (gtkWidget, this, true); + c.FocusChain = new [] { gtkWidget }; c.Show (); nativeWidget = c; c.Destroyed += delegate { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/DropDownBox.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/DropDownBox.cs index 1f02fdce8a..df6e49c424 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/DropDownBox.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/DropDownBox.cs @@ -29,7 +29,6 @@ using System.ComponentModel; using Gtk; using MonoDevelop.Ide; -using Mono.TextEditor; namespace MonoDevelop.Components { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkGestures.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkGestures.cs new file mode 100644 index 0000000000..a819fc0f74 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkGestures.cs @@ -0,0 +1,252 @@ +// +// GtkGestures.cs +// +// Author: +// Michael Hutchinson <mhutch@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 System.Runtime.InteropServices; +using Gdk; + +namespace MonoDevelop.Components +{ + public static class GtkGestures + { + const int GDK_GESTURE_MAGNIFY = 37; + const int GDK_GESTURE_ROTATE = 38; + const int GDK_GESTURE_SWIPE = 39;
+
+ [DllImport (PangoUtil.LIBGDK, CallingConvention = CallingConvention.Cdecl)] + static extern bool gdk_quartz_supports_gesture_events (); + + static bool isSupported; + + static GtkGestures () + { + if (MonoDevelop.Core.Platform.IsMac) { + try { + isSupported = gdk_quartz_supports_gesture_events (); + } catch (EntryPointNotFoundException) { + } + } + } + + public static bool IsSupported { get { return isSupported; } } + + public static void AddGestureMagnifyHandler (this Gtk.Widget widget, EventHandler<GestureMagnifyEventArgs> handler) + { + if (!isSupported) + throw new NotSupportedException (); + var signal = GLib.Signal.Lookup (widget, "gesture-magnify-event", typeof(GestureMagnifyEventArgs)); + signal.AddDelegate (new EventHandler<GestureMagnifyEventArgs> (handler)); + } + + public static void AddGestureRotateHandler (this Gtk.Widget widget, EventHandler<GestureRotateEventArgs> handler) + { + if (!isSupported) + throw new NotSupportedException (); + var signal = GLib.Signal.Lookup (widget, "gesture-rotate-event", typeof(GestureRotateEventArgs)); + signal.AddDelegate (new EventHandler<GestureRotateEventArgs> (handler)); + } + + public static void AddGestureSwipeHandler (this Gtk.Widget widget, EventHandler<GestureSwipeEventArgs> handler) + { + if (!isSupported) + throw new NotSupportedException (); + var signal = GLib.Signal.Lookup (widget, "gesture-swipe-event", typeof(GestureSwipeEventArgs)); + signal.AddDelegate (new EventHandler<GestureSwipeEventArgs> (handler)); + } + } + + public unsafe class GestureMagnifyEventArgs : GLib.SignalArgs + { + //have to force pack=4, or Mono aligns doubles to 8 bytes + [StructLayout (LayoutKind.Sequential, Pack=4)] + struct GdkEventGestureMagnify + { + public Gdk.EventType type; + public IntPtr window; + public sbyte send_event; + public uint time; + public double x, y; + public uint state; + public double magnification; + public IntPtr device; + public double x_root, y_root; + } + + // I tried to mimic the GTK# pattern of having a subclassed Event object on the EventArgs, but I gave up on + // figuring out how to get GTK# to marshal the event to a custom GestureMagnifyEvent class. Instead we just + // lift all the accessors up to the args class and dereference the handle directly. + GdkEventGestureMagnify *evt { + get { + var handle = ((Event)Args[0]).Handle; + return (GdkEventGestureMagnify *) handle; + } + } + + public Gdk.Window Window { + get { + return (Gdk.Window) GLib.Object.GetObject (evt->window); + } + } + + public Device Device { + get { + return (Device) GLib.Object.GetObject (evt->device); + } + } + + public uint Time { get { return evt->time; } } + public double X { get { return evt->x; } } + public double Y { get { return evt->y; } } + public ModifierType State { get { return (ModifierType) evt->state; } } + public double Magnification { get { return evt->magnification; } } + public double XRoot { get { return evt->x_root; } } + public double YRoot { get { return evt->y_root; } } + } + + public unsafe class GestureRotateEventArgs : GLib.SignalArgs + { + [StructLayout (LayoutKind.Sequential, Pack=4)] + struct GdkEventGestureRotate + { + public Gdk.EventType type; + public IntPtr window; + public sbyte send_event; + public uint time; + public double x, y; + public uint state; + public double rotation; + public IntPtr device; + public double x_root, y_root; + } + + GdkEventGestureRotate *evt { + get { + var handle = ((Event)Args[0]).Handle; + return (GdkEventGestureRotate *) handle; + } + } + + public Gdk.Window Window { + get { + return (Gdk.Window) GLib.Object.GetObject (evt->window); + } + } + + public Device Device { + get { + return (Device) GLib.Object.GetObject (evt->device); + } + } + + public uint Time { get { return evt->time; } } + public double X { get { return evt->x; } } + public double Y { get { return evt->y; } } + public ModifierType State { get { return (ModifierType) evt->state; } } + public double Rotation { get { return evt->rotation; } } + public double XRoot { get { return evt->x_root; } } + public double YRoot { get { return evt->y_root; } } + } + + public unsafe class GestureSwipeEventArgs : GLib.SignalArgs + { + [StructLayout (LayoutKind.Sequential, Pack=4)] + struct GdkEventGestureSwipe + { + public Gdk.EventType type; + public IntPtr window; + public sbyte send_event; + public uint time; + public double x, y; + public uint state; + public double delta_x, delta_y; + public IntPtr device; + public double x_root, y_root; + } + + GdkEventGestureSwipe *evt { + get { + var handle = ((Event)Args[0]).Handle; + return (GdkEventGestureSwipe *) handle; + } + } + + public Gdk.Window Window { + get { + return (Gdk.Window) GLib.Object.GetObject (evt->window); + } + } + + public Device Device { + get { + return (Device) GLib.Object.GetObject (evt->device); + } + } + + public uint Time { get { return evt->time; } } + public double X { get { return evt->x; } } + public double Y { get { return evt->y; } } + public ModifierType State { get { return (ModifierType) evt->state; } } + public double DeltaX { get { return evt->delta_x; } } + public double DeltaY { get { return evt->delta_y; } } + public double XRoot { get { return evt->x_root; } } + public double YRoot { get { return evt->y_root; } } + } + + /* + void PrintOffsets () + { + GdkEventGestureMagnify *v = (GdkEventGestureMagnify *)0; + Console.WriteLine ("type {0}", (int)&(v->type)); + Console.WriteLine ("window {0}", (int)&(v->window)); + Console.WriteLine ("send_event {0}", (int)&(v->send_event)); + Console.WriteLine ("time {0}", (int)&(v->time)); + Console.WriteLine ("x {0}", (int)&(v->x)); + Console.WriteLine ("y {0}", (int)&(v->y)); + Console.WriteLine ("state {0}", (int)&(v->state)); + Console.WriteLine ("magnification {0}", (int)&(v->magnification)); + Console.WriteLine ("x_root {0}", (int)&(v->x_root)); + Console.WriteLine ("y_root {0}", (int)&(v->y_root)); + } + + // gcc -m32 test.c `pkg-config --cflags gtk+-2.0` + #include <gtk/gtk.h> + + int main (int argc, char* argv) + { + GdkEventGestureMagnify *v = (GdkEventGestureMagnify *)0; + printf ("type %d\n", (int)&(v->type)); + printf ("window %d\n", (int)&(v->window)); + printf ("send_event %d\n", (int)&(v->send_event)); + printf ("time %d\n", (int)&(v->time)); + printf ("x %d\n", (int)&(v->x)); + printf ("y %d\n", (int)&(v->y)); + printf ("state %d\n", (int)&(v->state)); + printf ("magnification %d\n", (int)&(v->magnification)); + printf ("x_root %d\n", (int)&(v->x_root)); + printf ("y_root %d\n", (int)&(v->y_root)); + } + */ +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkUtil.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkUtil.cs index 5818c33e5b..a083d352f2 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkUtil.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkUtil.cs @@ -88,6 +88,19 @@ namespace MonoDevelop.Components return c.ToGdkColor (); } + /// <summary> + /// Makes a color lighter or darker + /// </summary> + /// <param name='lightAmount'> + /// Amount of lightness to add. If the value is positive, the color will be lighter, + /// if negative it will be darker. Value must be between 0 and 1. + /// </param> + public static HslColor AddLight (this HslColor color, double lightAmount) + { + color.L += lightAmount; + return color; + } + public static Cairo.Color AddLight (this Cairo.Color color, double lightAmount) { var c = color.ToXwtColor (); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkWorkarounds.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkWorkarounds.cs new file mode 100644 index 0000000000..398ae0e0e9 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkWorkarounds.cs @@ -0,0 +1,1275 @@ +// +// GtkWorkarounds.cs +// +// Authors: Jeffrey Stedfast <jeff@xamarin.com> +// +// Copyright (C) 2011 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 System.Drawing; +using System.Runtime.InteropServices; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; +using Gtk; +using MonoDevelop.Core; +using MonoDevelop.Ide.Editor.Highlighting; +using System.Text.RegularExpressions; + +namespace MonoDevelop.Components +{ + public static class GtkWorkarounds + { + const string LIBOBJC ="/usr/lib/libobjc.dylib"; + const string USER32DLL = "User32.dll"; + + [DllImport (LIBOBJC, EntryPoint = "sel_registerName")] + static extern IntPtr sel_registerName (string selector); + + [DllImport (LIBOBJC, EntryPoint = "objc_getClass")] + static extern IntPtr objc_getClass (string klass); + + [DllImport (LIBOBJC, EntryPoint = "objc_msgSend")] + static extern IntPtr objc_msgSend_IntPtr (IntPtr klass, IntPtr selector); + + [DllImport (LIBOBJC, EntryPoint = "objc_msgSend")] + static extern void objc_msgSend_void_bool (IntPtr klass, IntPtr selector, bool arg); + + [DllImport (LIBOBJC, EntryPoint = "objc_msgSend")] + static extern bool objc_msgSend_bool (IntPtr klass, IntPtr selector); + + [DllImport (LIBOBJC, EntryPoint = "objc_msgSend")] + static extern int objc_msgSend_NSInt32_NSInt32 (IntPtr klass, IntPtr selector, int arg); + + [DllImport (LIBOBJC, EntryPoint = "objc_msgSend")] + static extern long objc_msgSend_NSInt64_NSInt64 (IntPtr klass, IntPtr selector, long arg); + + [DllImport (LIBOBJC, EntryPoint = "objc_msgSend")] + static extern uint objc_msgSend_NSUInt32 (IntPtr klass, IntPtr selector); + + [DllImport (LIBOBJC, EntryPoint = "objc_msgSend")] + static extern ulong objc_msgSend_NSUInt64 (IntPtr klass, IntPtr selector); + + [DllImport (LIBOBJC, EntryPoint = "objc_msgSend_stret")] + static extern void objc_msgSend_CGRect32 (out CGRect32 rect, IntPtr klass, IntPtr selector); + + [DllImport (LIBOBJC, EntryPoint = "objc_msgSend_stret")] + static extern void objc_msgSend_CGRect64 (out CGRect64 rect, IntPtr klass, IntPtr selector); + + [DllImport (PangoUtil.LIBQUARTZ)] + static extern IntPtr gdk_quartz_window_get_nswindow (IntPtr window); + + [DllImport (PangoUtil.LIBQUARTZ)] + static extern bool gdk_window_has_embedded_nsview_focus (IntPtr window); + + struct CGRect32 + { + public float X, Y, Width, Height; + } + + struct CGRect64 + { + public double X, Y, Width, Height; + + public CGRect64 (CGRect32 rect32) + { + X = rect32.X; + Y = rect32.Y; + Width = rect32.Width; + Height = rect32.Height; + } + } + + static IntPtr cls_NSScreen; + static IntPtr sel_screens, sel_objectEnumerator, sel_nextObject, sel_frame, sel_visibleFrame, + sel_requestUserAttention, sel_setHasShadow, sel_invalidateShadow; + static IntPtr sharedApp; + static IntPtr cls_NSEvent; + static IntPtr sel_modifierFlags; + + const int NSCriticalRequest = 0; + const int NSInformationalRequest = 10; + + static System.Reflection.MethodInfo glibObjectGetProp, glibObjectSetProp; + + public static int GtkMinorVersion = 12, GtkMicroVersion = 0; + static bool oldMacKeyHacks = false; + + static GtkWorkarounds () + { + if (Platform.IsMac) { + InitMac (); + } + + var flags = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic; + glibObjectSetProp = typeof (GLib.Object).GetMethod ("SetProperty", flags); + glibObjectGetProp = typeof (GLib.Object).GetMethod ("GetProperty", flags); + + foreach (int i in new [] { 24, 22, 20, 18, 16, 14 }) { + if (Gtk.Global.CheckVersion (2, (uint)i, 0) == null) { + GtkMinorVersion = i; + break; + } + } + + for (int i = 1; i < 20; i++) { + if (Gtk.Global.CheckVersion (2, (uint)GtkMinorVersion, (uint)i) == null) { + GtkMicroVersion = i; + } else { + break; + } + } + + //opt into the fixes on GTK+ >= 2.24.8 + if (Platform.IsMac) { + try { + gdk_quartz_set_fix_modifiers (true); + } catch (EntryPointNotFoundException) { + oldMacKeyHacks = true; + } + } + + keymap.KeysChanged += delegate { + mappedKeys.Clear (); + }; + } + + static void InitMac () + { + cls_NSScreen = objc_getClass ("NSScreen"); + cls_NSEvent = objc_getClass ("NSEvent"); + sel_screens = sel_registerName ("screens"); + sel_objectEnumerator = sel_registerName ("objectEnumerator"); + sel_nextObject = sel_registerName ("nextObject"); + sel_visibleFrame = sel_registerName ("visibleFrame"); + sel_frame = sel_registerName ("frame"); + sel_requestUserAttention = sel_registerName ("requestUserAttention:"); + sel_modifierFlags = sel_registerName ("modifierFlags"); + sel_setHasShadow = sel_registerName ("setHasShadow:"); + sel_invalidateShadow = sel_registerName ("invalidateShadow"); + sharedApp = objc_msgSend_IntPtr (objc_getClass ("NSApplication"), sel_registerName ("sharedApplication")); + } + + static Gdk.Rectangle MacGetUsableMonitorGeometry (Gdk.Screen screen, int monitor) + { + IntPtr array = objc_msgSend_IntPtr (cls_NSScreen, sel_screens); + IntPtr iter = objc_msgSend_IntPtr (array, sel_objectEnumerator); + Gdk.Rectangle ygeometry = screen.GetMonitorGeometry (monitor); + Gdk.Rectangle xgeometry = screen.GetMonitorGeometry (0); + IntPtr scrn; + int i = 0; + + while ((scrn = objc_msgSend_IntPtr (iter, sel_nextObject)) != IntPtr.Zero && i < monitor) + i++; + + if (scrn == IntPtr.Zero) + return screen.GetMonitorGeometry (monitor); + + CGRect64 visible, frame; + + if (IntPtr.Size == 8) { + objc_msgSend_CGRect64 (out visible, scrn, sel_visibleFrame); + objc_msgSend_CGRect64 (out frame, scrn, sel_frame); + } else { + CGRect32 visible32, frame32; + objc_msgSend_CGRect32 (out visible32, scrn, sel_visibleFrame); + objc_msgSend_CGRect32 (out frame32, scrn, sel_frame); + visible = new CGRect64 (visible32); + frame = new CGRect64 (frame32); + } + + // Note: Frame and VisibleFrame rectangles are relative to monitor 0, but we need absolute + // coordinates. + visible.X += xgeometry.X; + frame.X += xgeometry.X; + + // VisibleFrame.Y is the height of the Dock if it is at the bottom of the screen, so in order + // to get the menu height, we just figure out the difference between the visibleFrame height + // and the actual frame height, then subtract the Dock height. + // + // We need to swap the Y offset with the menu height because our callers expect the Y offset + // to be from the top of the screen, not from the bottom of the screen. + double x, y, width, height; + + if (visible.Height < frame.Height) { + double dockHeight = visible.Y - frame.Y; + double menubarHeight = (frame.Height - visible.Height) - dockHeight; + + height = frame.Height - menubarHeight - dockHeight; + y = ygeometry.Y + menubarHeight; + } else { + height = frame.Height; + y = ygeometry.Y; + } + + // Takes care of the possibility of the Dock being positioned on the left or right edge of the screen. + width = System.Math.Min (visible.Width, frame.Width); + x = System.Math.Max (visible.X, frame.X); + + return new Gdk.Rectangle ((int) x, (int) y, (int) width, (int) height); + } + + static void MacRequestAttention (bool critical) + { + int kind = critical? NSCriticalRequest : NSInformationalRequest; + if (IntPtr.Size == 8) { + objc_msgSend_NSInt64_NSInt64 (sharedApp, sel_requestUserAttention, kind); + } else { + objc_msgSend_NSInt32_NSInt32 (sharedApp, sel_requestUserAttention, kind); + } + } + + // Note: we can't reuse RectangleF because the layout is different... + [StructLayout (LayoutKind.Sequential)] + struct Rect { + public int Left; + public int Top; + public int Right; + public int Bottom; + + public int X { get { return Left; } } + public int Y { get { return Top; } } + public int Width { get { return Right - Left; } } + public int Height { get { return Bottom - Top; } } + } + + const int MonitorInfoFlagsPrimary = 0x01; + + [StructLayout (LayoutKind.Sequential)] + unsafe struct MonitorInfo { + public int Size; + public Rect Frame; // Monitor + public Rect VisibleFrame; // Work + public int Flags; + public fixed byte Device[32]; + } + + [UnmanagedFunctionPointer (CallingConvention.Winapi)] + delegate int EnumMonitorsCallback (IntPtr hmonitor, IntPtr hdc, IntPtr prect, IntPtr user_data); + + [DllImport (USER32DLL)] + extern static int EnumDisplayMonitors (IntPtr hdc, IntPtr clip, EnumMonitorsCallback callback, IntPtr user_data); + + [DllImport (USER32DLL)] + extern static int GetMonitorInfoA (IntPtr hmonitor, ref MonitorInfo info); + + static Gdk.Rectangle WindowsGetUsableMonitorGeometry (Gdk.Screen screen, int monitor_id) + { + Gdk.Rectangle geometry = screen.GetMonitorGeometry (monitor_id); + List<MonitorInfo> screens = new List<MonitorInfo> (); + + EnumDisplayMonitors (IntPtr.Zero, IntPtr.Zero, delegate (IntPtr hmonitor, IntPtr hdc, IntPtr prect, IntPtr user_data) { + var info = new MonitorInfo (); + + unsafe { + info.Size = sizeof (MonitorInfo); + } + + GetMonitorInfoA (hmonitor, ref info); + + // In order to keep the order the same as Gtk, we need to put the primary monitor at the beginning. + if ((info.Flags & MonitorInfoFlagsPrimary) != 0) + screens.Insert (0, info); + else + screens.Add (info); + + return 1; + }, IntPtr.Zero); + + MonitorInfo monitor = screens[monitor_id]; + Rect visible = monitor.VisibleFrame; + Rect frame = monitor.Frame; + + // Rebase the VisibleFrame off of Gtk's idea of this monitor's geometry (since they use different coordinate systems) + int x = geometry.X + (visible.Left - frame.Left); + int width = visible.Width; + + int y = geometry.Y + (visible.Top - frame.Top); + int height = visible.Height; + + return new Gdk.Rectangle (x, y, width, height); + } + + public static Gdk.Rectangle GetUsableMonitorGeometry (this Gdk.Screen screen, int monitor) + { + if (Platform.IsWindows) + return WindowsGetUsableMonitorGeometry (screen, monitor); + + if (Platform.IsMac) + return MacGetUsableMonitorGeometry (screen, monitor); + + return screen.GetMonitorGeometry (monitor); + } + + public static int RunDialogWithNotification (Gtk.Dialog dialog) + { + if (Platform.IsMac) + MacRequestAttention (dialog.Modal); + + return dialog.Run (); + } + + public static void PresentWindowWithNotification (this Gtk.Window window) + { + window.Present (); + + if (Platform.IsMac) { + var dialog = window as Gtk.Dialog; + MacRequestAttention (dialog == null? false : dialog.Modal); + } + } + + public static GLib.Value GetProperty (this GLib.Object obj, string name) + { + return (GLib.Value) glibObjectGetProp.Invoke (obj, new object[] { name }); + } + + public static void SetProperty (this GLib.Object obj, string name, GLib.Value value) + { + glibObjectSetProp.Invoke (obj, new object[] { name, value }); + } + + public static bool TriggersContextMenu (this Gdk.EventButton evt) + { + return evt.Type == Gdk.EventType.ButtonPress && IsContextMenuButton (evt); + } + + public static bool IsContextMenuButton (this Gdk.EventButton evt) + { + if (evt.Button == 3 && + (evt.State & (Gdk.ModifierType.Button1Mask | Gdk.ModifierType.Button2Mask)) == 0) + return true; + + if (Platform.IsMac) { + if (!oldMacKeyHacks && + evt.Button == 1 && + (evt.State & Gdk.ModifierType.ControlMask) != 0 && + (evt.State & (Gdk.ModifierType.Button2Mask | Gdk.ModifierType.Button3Mask)) == 0) + { + return true; + } + } + + return false; + } + + public static Gdk.ModifierType GetCurrentKeyModifiers () + { + if (Platform.IsMac) { + Gdk.ModifierType mtype = Gdk.ModifierType.None; + ulong mod; + if (IntPtr.Size == 8) { + mod = objc_msgSend_NSUInt64 (cls_NSEvent, sel_modifierFlags); + } else { + mod = objc_msgSend_NSUInt32 (cls_NSEvent, sel_modifierFlags); + } + if ((mod & (1 << 17)) != 0) + mtype |= Gdk.ModifierType.ShiftMask; + if ((mod & (1 << 18)) != 0) + mtype |= Gdk.ModifierType.ControlMask; + if ((mod & (1 << 19)) != 0) + mtype |= Gdk.ModifierType.Mod1Mask; // Alt key + if ((mod & (1 << 20)) != 0) + mtype |= Gdk.ModifierType.Mod2Mask; // Command key + return mtype; + } + else { + Gdk.ModifierType mtype; + Gtk.Global.GetCurrentEventState (out mtype); + return mtype; + } + } + + public static void GetPageScrollPixelDeltas (this Gdk.EventScroll evt, double pageSizeX, double pageSizeY, + out double deltaX, out double deltaY) + { + if (!GetEventScrollDeltas (evt, out deltaX, out deltaY)) { + var direction = evt.Direction; + deltaX = deltaY = 0; + if (pageSizeY != 0 && (direction == Gdk.ScrollDirection.Down || direction == Gdk.ScrollDirection.Up)) { + deltaY = System.Math.Pow (pageSizeY, 2.0 / 3.0); + deltaX = 0.0; + if (direction == Gdk.ScrollDirection.Up) + deltaY = -deltaY; + } else if (pageSizeX != 0) { + deltaX = System.Math.Pow (pageSizeX, 2.0 / 3.0); + deltaY = 0.0; + if (direction == Gdk.ScrollDirection.Left) + deltaX = -deltaX; + } + } + } + + public static void AddValueClamped (this Gtk.Adjustment adj, double value) + { + adj.Value = System.Math.Max (adj.Lower, System.Math.Min (adj.Value + value, adj.Upper - adj.PageSize)); + } + + [DllImport (PangoUtil.LIBGTK, CallingConvention = CallingConvention.Cdecl)] + extern static bool gdk_event_get_scroll_deltas (IntPtr eventScroll, out double deltaX, out double deltaY); + static bool scrollDeltasNotSupported; + + public static bool GetEventScrollDeltas (Gdk.EventScroll evt, out double deltaX, out double deltaY) + { + if (!scrollDeltasNotSupported) { + try { + return gdk_event_get_scroll_deltas (evt.Handle, out deltaX, out deltaY); + } catch (EntryPointNotFoundException) { + scrollDeltasNotSupported = true; + } + } + deltaX = deltaY = 0; + return false; + } + + /// <summary>Shows a context menu.</summary> + /// <param name='menu'>The menu.</param> + /// <param name='parent'>The parent widget.</param> + /// <param name='evt'>The mouse event. May be null if triggered by keyboard.</param> + /// <param name='caret'>The caret/selection position within the parent, if the EventButton is null.</param> + public static void ShowContextMenu (Gtk.Menu menu, Gtk.Widget parent, Gdk.EventButton evt, Gdk.Rectangle caret) + { + Gtk.MenuPositionFunc posFunc = null; + + if (parent != null) { + menu.AttachToWidget (parent, null); + menu.Hidden += (sender, e) => { + if (menu.AttachWidget != null) + menu.Detach (); + }; + posFunc = delegate (Gtk.Menu m, out int x, out int y, out bool pushIn) { + Gdk.Window window = evt != null? evt.Window : parent.GdkWindow; + window.GetOrigin (out x, out y); + var alloc = parent.Allocation; + if (evt != null) { + x += (int) evt.X; + y += (int) evt.Y; + } else if (caret.X >= alloc.X && caret.Y >= alloc.Y) { + x += caret.X; + y += caret.Y + caret.Height; + } else { + x += alloc.X; + y += alloc.Y; + } + Gtk.Requisition request = m.SizeRequest (); + var screen = parent.Screen; + Gdk.Rectangle geometry = GetUsableMonitorGeometry (screen, screen.GetMonitorAtPoint (x, y)); + + //whether to push or flip menus that would extend offscreen + //FIXME: this is the correct behaviour for mac, check other platforms + bool flip_left = true; + bool flip_up = false; + + if (x + request.Width > geometry.X + geometry.Width) { + if (flip_left) { + x -= request.Width; + } else { + x = geometry.X + geometry.Width - request.Width; + } + + if (x < geometry.Left) + x = geometry.Left; + } + + if (y + request.Height > geometry.Y + geometry.Height) { + if (flip_up) { + y -= request.Height; + } else { + y = geometry.Y + geometry.Height - request.Height; + } + + if (y < geometry.Top) + y = geometry.Top; + } + + pushIn = false; + }; + } + + uint time; + uint button; + + if (evt == null) { + time = Gtk.Global.CurrentEventTime; + button = 0; + } else { + time = evt.Time; + button = evt.Button; + } + + //HACK: work around GTK menu issues on mac when passing button to menu.Popup + //some menus appear and immediately hide, and submenus don't activate + if (Platform.IsMac) { + button = 0; + } + + menu.Popup (null, null, posFunc, button, time); + } + + public static void ShowContextMenu (Gtk.Menu menu, Gtk.Widget parent, Gdk.EventButton evt) + { + ShowContextMenu (menu, parent, evt, Gdk.Rectangle.Zero); + } + + public static void ShowContextMenu (Gtk.Menu menu, Gtk.Widget parent, Gdk.Rectangle caret) + { + ShowContextMenu (menu, parent, null, caret); + } + + struct MappedKeys + { + public Gdk.Key Key; + public Gdk.ModifierType State; + public KeyboardShortcut[] Shortcuts; + } + + //introduced in GTK 2.20 + [DllImport (PangoUtil.LIBGDK, CallingConvention = CallingConvention.Cdecl)] + extern static bool gdk_keymap_add_virtual_modifiers (IntPtr keymap, ref Gdk.ModifierType state); + + //Custom patch in Mono Mac w/GTK+ 2.24.8+ + [DllImport (PangoUtil.LIBGDK, CallingConvention = CallingConvention.Cdecl)] + extern static bool gdk_quartz_set_fix_modifiers (bool fix); + + static Gdk.Keymap keymap = Gdk.Keymap.Default; + static Dictionary<ulong,MappedKeys> mappedKeys = new Dictionary<ulong,MappedKeys> (); + + /// <summary>Map raw GTK key input to work around platform bugs and decompose accelerator keys</summary> + /// <param name='evt'>The raw key event</param> + /// <param name='key'>The composed key</param> + /// <param name='state'>The composed modifiers</param> + /// <param name='shortcuts'>All the key/modifier decompositions that can be used as accelerators</param> + public static void MapKeys (Gdk.EventKey evt, out Gdk.Key key, out Gdk.ModifierType state, + out KeyboardShortcut[] shortcuts) + { + //this uniquely identifies the raw key + ulong id; + unchecked { + id = (((ulong)(uint)evt.State) | (((ulong)evt.HardwareKeycode) << 32) | (((ulong)evt.Group) << 48)); + } + + bool remapKey = Platform.IsWindows && evt.HardwareKeycode == 0; + MappedKeys mapped; + if (remapKey || !mappedKeys.TryGetValue (id, out mapped)) + mappedKeys[id] = mapped = MapKeys (evt); + + shortcuts = mapped.Shortcuts; + state = mapped.State; + key = mapped.Key; + + if (remapKey) { + key = (Gdk.Key)evt.KeyValue; + } + } + + static MappedKeys MapKeys (Gdk.EventKey evt) + { + MappedKeys mapped; + Gdk.ModifierType modifier = evt.State; + byte grp = evt.Group; + + if (GtkMinorVersion >= 20) { + gdk_keymap_add_virtual_modifiers (keymap.Handle, ref modifier); + } + + //full key mapping + uint keyval; + int effectiveGroup, level; + Gdk.ModifierType consumedModifiers; + TranslateKeyboardState (evt, modifier, grp, out keyval, out effectiveGroup, + out level, out consumedModifiers); + mapped.Key = (Gdk.Key)keyval; + mapped.State = FixMacModifiers (evt.State & ~consumedModifiers, grp); + + //decompose the key into accel combinations + var accelList = new List<KeyboardShortcut> (); + + const Gdk.ModifierType accelMods = Gdk.ModifierType.ShiftMask | Gdk.ModifierType.Mod1Mask + | Gdk.ModifierType.ControlMask | Gdk.ModifierType.SuperMask |Gdk.ModifierType.MetaMask; + + //all accels ignore the lock key + modifier &= ~Gdk.ModifierType.LockMask; + + //fully decomposed + TranslateKeyboardState (evt, Gdk.ModifierType.None, 0, + out keyval, out effectiveGroup, out level, out consumedModifiers); + accelList.Add (new KeyboardShortcut ((Gdk.Key)keyval, FixMacModifiers (modifier, grp) & accelMods)); + + //with shift composed + if ((modifier & Gdk.ModifierType.ShiftMask) != 0) { + keymap.TranslateKeyboardState (evt.HardwareKeycode, Gdk.ModifierType.ShiftMask, 0, + out keyval, out effectiveGroup, out level, out consumedModifiers); + + if (Platform.IsWindows && evt.HardwareKeycode == 0) { + keyval = (ushort)evt.KeyValue; + } + + // Prevent consumption of non-Shift modifiers (that we didn't even provide!) + consumedModifiers &= Gdk.ModifierType.ShiftMask; + + var m = FixMacModifiers ((modifier & ~consumedModifiers), grp) & accelMods; + AddIfNotDuplicate (accelList, new KeyboardShortcut ((Gdk.Key)keyval, m)); + } + + //with group 1 composed + if (grp == 1) { + TranslateKeyboardState (evt, modifier & ~Gdk.ModifierType.ShiftMask, 1, + out keyval, out effectiveGroup, out level, out consumedModifiers); + + // Prevent consumption of Shift modifier (that we didn't even provide!) + consumedModifiers &= ~Gdk.ModifierType.ShiftMask; + + var m = FixMacModifiers ((modifier & ~consumedModifiers), 0) & accelMods; + AddIfNotDuplicate (accelList, new KeyboardShortcut ((Gdk.Key)keyval, m)); + } + + //with group 1 and shift composed + if (grp == 1 && (modifier & Gdk.ModifierType.ShiftMask) != 0) { + TranslateKeyboardState (evt, modifier, 1, + out keyval, out effectiveGroup, out level, out consumedModifiers); + var m = FixMacModifiers ((modifier & ~consumedModifiers), 0) & accelMods; + AddIfNotDuplicate (accelList, new KeyboardShortcut ((Gdk.Key)keyval, m)); + } + + //and also allow the fully mapped key as an accel + AddIfNotDuplicate (accelList, new KeyboardShortcut (mapped.Key, mapped.State & accelMods)); + + mapped.Shortcuts = accelList.ToArray (); + return mapped; + } + + // Workaround for bug "Bug 688247 - Ctrl+Alt key not work on windows7 with bootcamp on a Mac Book Pro" + // Ctrl+Alt should behave like right alt key - unfortunately TranslateKeyboardState doesn't handle it. + static void TranslateKeyboardState (Gdk.EventKey evt, Gdk.ModifierType state, int group, out uint keyval, + out int effective_group, out int level, out Gdk.ModifierType consumed_modifiers) + { + uint hardware_keycode = evt.HardwareKeycode; + + if (Platform.IsWindows) { + const Gdk.ModifierType ctrlAlt = Gdk.ModifierType.ControlMask | Gdk.ModifierType.Mod1Mask; + if ((state & ctrlAlt) == ctrlAlt) { + state = (state & ~ctrlAlt) | Gdk.ModifierType.Mod2Mask; + group = 1; + } + // Case: Caps lock on + shift + key + // See: Bug 8069 - [UI Refresh] If caps lock is on, holding the shift key prevents typed characters from appearing + if (state.HasFlag (Gdk.ModifierType.ShiftMask)) { + state &= ~Gdk.ModifierType.ShiftMask; + } + } + + keymap.TranslateKeyboardState (hardware_keycode, state, group, out keyval, out effective_group, + out level, out consumed_modifiers); + + if (Platform.IsWindows && hardware_keycode == 0) { + keyval = evt.KeyValue; + } + } + + static Gdk.ModifierType FixMacModifiers (Gdk.ModifierType mod, byte grp) + { + if (!oldMacKeyHacks) + return mod; + + // Mac GTK+ maps the command key to the Mod1 modifier, which usually means alt/ + // We map this instead to meta, because the Mac GTK+ has mapped the cmd key + // to the meta key (yay inconsistency!). IMO super would have been saner. + if ((mod & Gdk.ModifierType.Mod1Mask) != 0) { + mod ^= Gdk.ModifierType.Mod1Mask; + mod |= Gdk.ModifierType.MetaMask; + } + + //some versions of GTK map opt as mod5, which converts to the virtual super modifier + if ((mod & (Gdk.ModifierType.Mod5Mask | Gdk.ModifierType.SuperMask)) != 0) { + mod ^= (Gdk.ModifierType.Mod5Mask | Gdk.ModifierType.SuperMask); + mod |= Gdk.ModifierType.Mod1Mask; + } + + // When opt modifier is active, we need to decompose this to make the command appear correct for Mac. + // In addition, we can only inspect whether the opt/alt key is pressed by examining + // the key's "group", because the Mac GTK+ treats opt as a group modifier and does + // not expose it as an actual GDK modifier. + if (grp == (byte) 1) { + mod |= Gdk.ModifierType.Mod1Mask; + } + + return mod; + } + + static void AddIfNotDuplicate<T> (List<T> list, T item) where T : IEquatable<T> + { + for (int i = 0; i < list.Count; i++) { + if (list[i].Equals (item)) + return; + } + list.Add (item); + } + + [System.Runtime.InteropServices.DllImport (PangoUtil.LIBGDK, CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr gdk_win32_drawable_get_handle (IntPtr drawable); + + enum DwmWindowAttribute + { + NcRenderingEnabled = 1, + NcRenderingPolicy, + TransitionsForceDisabled, + AllowNcPaint, + CaptionButtonBounds, + NonClientRtlLayout, + ForceIconicRepresentation, + Flip3DPolicy, + ExtendedFrameBounds, + HasIconicBitmap, + DisallowPeek, + ExcludedFromPeek, + Last, + } + + struct Win32Rect + { + public int Left, Top, Right, Bottom; + + public Win32Rect (int left, int top, int right, int bottom) + { + this.Left = left; + this.Top = top; + this.Right = right; + this.Bottom = bottom; + } + } + + [DllImport ("dwmapi.dll")] + static extern int DwmGetWindowAttribute (IntPtr hwnd, DwmWindowAttribute attribute, out Win32Rect value, int valueSize); + + [DllImport ("dwmapi.dll")] + static extern int DwmIsCompositionEnabled (out bool enabled); + + [DllImport (USER32DLL)] + static extern bool GetWindowRect (IntPtr hwnd, out Win32Rect rect); + + public static void SetImCursorLocation (Gtk.IMContext ctx, Gdk.Window clientWindow, Gdk.Rectangle cursor) + { + // work around GTK+ Bug 663096 - Windows IME position is wrong when Aero glass is enabled + // https://bugzilla.gnome.org/show_bug.cgi?id=663096 + if (Platform.IsWindows && System.Environment.OSVersion.Version.Major >= 6) { + bool enabled; + if (DwmIsCompositionEnabled (out enabled) == 0 && enabled) { + var hwnd = gdk_win32_drawable_get_handle (clientWindow.Toplevel.Handle); + Win32Rect rect; + // this module gets the WINVER=6 version of GetWindowRect, which returns the correct value + if (GetWindowRect (hwnd, out rect)) { + int x, y; + clientWindow.Toplevel.GetPosition (out x, out y); + cursor.X = cursor.X - x + rect.Left; + cursor.Y = cursor.Y - y + rect.Top - cursor.Height; + } + } + } + ctx.CursorLocation = cursor; + } + + /// <summary>X coordinate of the pixels inside the right edge of the rectangle</summary> + /// <remarks>Workaround for inconsistency of Right property between GTK# versions</remarks> + public static int RightInside (this Gdk.Rectangle rect) + { + return rect.X + rect.Width - 1; + } + + /// <summary>Y coordinate of the pixels inside the bottom edge of the rectangle</summary> + /// <remarks>Workaround for inconsistency of Bottom property between GTK# versions#</remarks> + public static int BottomInside (this Gdk.Rectangle rect) + { + return rect.Y + rect.Height - 1; + } + + /// <summary> + /// Shows or hides the shadow of the window rendered by the native toolkit + /// </summary> + public static void ShowNativeShadow (Gtk.Window window, bool show) + { + if (Platform.IsMac) { + var ptr = gdk_quartz_window_get_nswindow (window.GdkWindow.Handle); + objc_msgSend_void_bool (ptr, sel_setHasShadow, show); + } + } + + public static void UpdateNativeShadow (Gtk.Window window) + { + if (!Platform.IsMac) + return; + + var ptr = gdk_quartz_window_get_nswindow (window.GdkWindow.Handle); + objc_msgSend_IntPtr (ptr, sel_invalidateShadow); + } + + public static bool HasNSTextFieldFocus (Gdk.Window window) + { + if (Platform.IsMac) { + try { + return gdk_window_has_embedded_nsview_focus (window.Handle); + } catch (Exception e) { + return false; + } + } else { + return false; + } + } + + [DllImport (PangoUtil.LIBGTKGLUE, CallingConvention = CallingConvention.Cdecl)] + static extern void gtksharp_container_leak_fixed_marker (); + + static HashSet<Type> fixedContainerTypes; + static Dictionary<IntPtr,ForallDelegate> forallCallbacks; + static bool containerLeakFixed; + + // Works around BXC #3801 - Managed Container subclasses are incorrectly resurrected, then leak. + // It does this by registering an alternative callback for gtksharp_container_override_forall, which + // ignores callbacks if the wrapper no longer exists. This means that the objects no longer enter a + // finalized->release->dispose->re-wrap resurrection cycle. + // We use a dynamic method to access internal/private GTK# API in a performant way without having to track + // per-instance delegates. + public static void FixContainerLeak (Gtk.Container c) + { + if (containerLeakFixed) { + return; + } + + FixContainerLeak (c.GetType ()); + } + + static void FixContainerLeak (Type t) + { + if (containerLeakFixed) { + return; + } + + if (fixedContainerTypes == null) { + try { + gtksharp_container_leak_fixed_marker (); + containerLeakFixed = true; + return; + } catch (EntryPointNotFoundException) { + } + fixedContainerTypes = new HashSet<Type>(); + forallCallbacks = new Dictionary<IntPtr, ForallDelegate> (); + } + + if (!fixedContainerTypes.Add (t)) { + return; + } + + //need to fix the callback for the type and all the managed supertypes + var lookupGType = typeof (GLib.Object).GetMethod ("LookupGType", BindingFlags.Static | BindingFlags.NonPublic); + do { + var gt = (GLib.GType) lookupGType.Invoke (null, new[] { t }); + var cb = CreateForallCallback (gt.Val); + forallCallbacks[gt.Val] = cb; + gtksharp_container_override_forall (gt.Val, cb); + t = t.BaseType; + } while (fixedContainerTypes.Add (t) && t.Assembly != typeof (Gtk.Container).Assembly); + } + + static ForallDelegate CreateForallCallback (IntPtr gtype) + { + var dm = new DynamicMethod ( + "ContainerForallCallback", + typeof(void), + new Type[] { typeof(IntPtr), typeof(bool), typeof(IntPtr), typeof(IntPtr) }, + typeof(GtkWorkarounds).Module, + true); + + var invokerType = typeof(Gtk.Container.CallbackInvoker); + + //this was based on compiling a similar method and disassembling it + ILGenerator il = dm.GetILGenerator (); + var IL_002b = il.DefineLabel (); + var IL_003f = il.DefineLabel (); + var IL_0060 = il.DefineLabel (); + var label_return = il.DefineLabel (); + + var loc_container = il.DeclareLocal (typeof(Gtk.Container)); + var loc_obj = il.DeclareLocal (typeof(object)); + var loc_invoker = il.DeclareLocal (invokerType); + var loc_ex = il.DeclareLocal (typeof(Exception)); + + //check that the type is an exact match + // prevent stack overflow, because the callback on a more derived type will handle everything + il.Emit (OpCodes.Ldarg_0); + il.Emit (OpCodes.Call, typeof(GLib.ObjectManager).GetMethod ("gtksharp_get_type_id", BindingFlags.Static | BindingFlags.NonPublic)); + + il.Emit (OpCodes.Ldc_I8, gtype.ToInt64 ()); + il.Emit (OpCodes.Newobj, typeof (IntPtr).GetConstructor (new Type[] { typeof (Int64) })); + il.Emit (OpCodes.Call, typeof (IntPtr).GetMethod ("op_Equality", BindingFlags.Static | BindingFlags.Public)); + il.Emit (OpCodes.Brfalse, label_return); + + il.BeginExceptionBlock (); + il.Emit (OpCodes.Ldnull); + il.Emit (OpCodes.Stloc, loc_container); + il.Emit (OpCodes.Ldsfld, typeof (GLib.Object).GetField ("Objects", BindingFlags.Static | BindingFlags.NonPublic)); + il.Emit (OpCodes.Ldarg_0); + il.Emit (OpCodes.Box, typeof (IntPtr)); + il.Emit (OpCodes.Callvirt, typeof (System.Collections.Hashtable).GetProperty ("Item").GetGetMethod ()); + il.Emit (OpCodes.Stloc, loc_obj); + il.Emit (OpCodes.Ldloc, loc_obj); + il.Emit (OpCodes.Brfalse, IL_002b); + + var tref = typeof (GLib.Object).Assembly.GetType ("GLib.ToggleRef"); + il.Emit (OpCodes.Ldloc, loc_obj); + il.Emit (OpCodes.Castclass, tref); + il.Emit (OpCodes.Callvirt, tref.GetProperty ("Target").GetGetMethod ()); + il.Emit (OpCodes.Isinst, typeof (Gtk.Container)); + il.Emit (OpCodes.Stloc, loc_container); + + il.MarkLabel (IL_002b); + il.Emit (OpCodes.Ldloc, loc_container); + il.Emit (OpCodes.Brtrue, IL_003f); + + il.Emit (OpCodes.Ldarg_0); + il.Emit (OpCodes.Ldarg_1); + il.Emit (OpCodes.Ldarg_2); + il.Emit (OpCodes.Ldarg_3); + il.Emit (OpCodes.Call, typeof (Gtk.Container).GetMethod ("gtksharp_container_base_forall", BindingFlags.Static | BindingFlags.NonPublic)); + il.Emit (OpCodes.Br, IL_0060); + + il.MarkLabel (IL_003f); + il.Emit (OpCodes.Ldloca_S, 2); + il.Emit (OpCodes.Ldarg_2); + il.Emit (OpCodes.Ldarg_3); + il.Emit (OpCodes.Call, invokerType.GetConstructor ( + BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof (IntPtr), typeof (IntPtr) }, null)); + il.Emit (OpCodes.Ldloc, loc_container); + il.Emit (OpCodes.Ldarg_1); + il.Emit (OpCodes.Ldloc, loc_invoker); + il.Emit (OpCodes.Box, invokerType); + il.Emit (OpCodes.Ldftn, invokerType.GetMethod ("Invoke")); + il.Emit (OpCodes.Newobj, typeof (Gtk.Callback).GetConstructor ( + BindingFlags.Instance | BindingFlags.Public, null, new Type[] { typeof (object), typeof (IntPtr) }, null)); + var forallMeth = typeof (Gtk.Container).GetMethod ("ForAll", + BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof (bool), typeof (Gtk.Callback) }, null); + il.Emit (OpCodes.Callvirt, forallMeth); + + il.MarkLabel (IL_0060); + + il.BeginCatchBlock (typeof (Exception)); + il.Emit (OpCodes.Stloc, loc_ex); + il.Emit (OpCodes.Ldloc, loc_ex); + il.Emit (OpCodes.Ldc_I4_0); + il.Emit (OpCodes.Call, typeof (GLib.ExceptionManager).GetMethod ("RaiseUnhandledException")); + il.Emit (OpCodes.Leave, label_return); + il.EndExceptionBlock (); + + il.MarkLabel (label_return); + il.Emit (OpCodes.Ret); + + return (ForallDelegate) dm.CreateDelegate (typeof (ForallDelegate)); + } + + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + delegate void ForallDelegate (IntPtr container, bool include_internals, IntPtr cb, IntPtr data); + + [DllImport(PangoUtil.LIBGTKGLUE, CallingConvention = CallingConvention.Cdecl)] + static extern void gtksharp_container_override_forall (IntPtr gtype, ForallDelegate cb); + + const string urlRegexStr = @"(http|ftp)s?\:\/\/[\w\d\.,;_/\-~%@()+:?&^=#!]*[\w\d/]"; + static readonly Regex UrlRegex = new Regex (urlRegexStr, RegexOptions.Compiled | RegexOptions.ExplicitCapture); + + public static string MarkupLinks (string text) + { + if (GtkMinorVersion < 18) + return text; + return UrlRegex.Replace (text, MatchToUrl); + } + + static string MatchToUrl (System.Text.RegularExpressions.Match m) + { + var s = m.ToString (); + return String.Format ("<a href='{0}'>{1}</a>", s, s.Replace ("_", "__")); + } + + public static void SetLinkHandler (this Gtk.Label label, Action<string> urlHandler) + { + if (GtkMinorVersion >= 18) + new UrlHandlerClosure (urlHandler).ConnectTo (label); + } + + //create closure manually so we can apply ConnectBefore + class UrlHandlerClosure + { + Action<string> urlHandler; + + public UrlHandlerClosure (Action<string> urlHandler) + { + this.urlHandler = urlHandler; + } + + [GLib.ConnectBefore] + void HandleLink (object sender, ActivateLinkEventArgs args) + { + urlHandler (args.Url); + args.RetVal = true; + } + + public void ConnectTo (Gtk.Label label) + { + var signal = GLib.Signal.Lookup (label, "activate-link", typeof(ActivateLinkEventArgs)); + signal.AddDelegate (new EventHandler<ActivateLinkEventArgs> (HandleLink)); + } + + class ActivateLinkEventArgs : GLib.SignalArgs + { + public string Url { get { return (string)base.Args [0]; } } + } + } + + static bool canSetOverlayScrollbarPolicy = true; + + [DllImport (PangoUtil.LIBQUARTZ)] + static extern void gtk_scrolled_window_set_overlay_policy (IntPtr sw, Gtk.PolicyType hpolicy, Gtk.PolicyType vpolicy); + + [DllImport (PangoUtil.LIBQUARTZ)] + static extern void gtk_scrolled_window_get_overlay_policy (IntPtr sw, out Gtk.PolicyType hpolicy, out Gtk.PolicyType vpolicy); + + public static void SetOverlayScrollbarPolicy (Gtk.ScrolledWindow sw, Gtk.PolicyType hpolicy, Gtk.PolicyType vpolicy) + { + if (!canSetOverlayScrollbarPolicy) { + return; + } + try { + gtk_scrolled_window_set_overlay_policy (sw.Handle, hpolicy, vpolicy); + return; + } catch (DllNotFoundException) { + } catch (EntryPointNotFoundException) { + } + } + + public static void GetOverlayScrollbarPolicy (Gtk.ScrolledWindow sw, out Gtk.PolicyType hpolicy, out Gtk.PolicyType vpolicy) + { + if (!canSetOverlayScrollbarPolicy) { + hpolicy = vpolicy = 0; + return; + } + try { + gtk_scrolled_window_get_overlay_policy (sw.Handle, out hpolicy, out vpolicy); + return; + } catch (DllNotFoundException) { + } catch (EntryPointNotFoundException) { + } + hpolicy = vpolicy = 0; + canSetOverlayScrollbarPolicy = false; + } + + [DllImport (PangoUtil.LIBGTK, CallingConvention = CallingConvention.Cdecl)] + static extern bool gtk_tree_view_get_tooltip_context (IntPtr raw, ref int x, ref int y, bool keyboard_tip, out IntPtr model, out IntPtr path, IntPtr iter); + + //the GTK# version of this has 'out' instead of 'ref', preventing passing the x,y values in + public static bool GetTooltipContext (this TreeView tree, ref int x, ref int y, bool keyboardTip, + out TreeModel model, out TreePath path, out Gtk.TreeIter iter) + { + IntPtr intPtr = Marshal.AllocHGlobal (Marshal.SizeOf (typeof(TreeIter))); + IntPtr handle; + IntPtr intPtr2; + bool result = gtk_tree_view_get_tooltip_context (tree.Handle, ref x, ref y, keyboardTip, out handle, out intPtr2, intPtr); + model = TreeModelAdapter.GetObject (handle, false); + path = intPtr2 == IntPtr.Zero ? null : ((TreePath)GLib.Opaque.GetOpaque (intPtr2, typeof(TreePath), false)); + iter = TreeIter.New (intPtr); + Marshal.FreeHGlobal (intPtr); + return result; + } + + static bool supportsHiResIcons = true; + + [DllImport (PangoUtil.LIBGTK, CallingConvention = CallingConvention.Cdecl)] + static extern void gtk_icon_source_set_scale (IntPtr source, double scale); + + [DllImport (PangoUtil.LIBGTK, CallingConvention = CallingConvention.Cdecl)] + static extern void gtk_icon_source_set_scale_wildcarded (IntPtr source, bool setting); + + [DllImport (PangoUtil.LIBGTK, CallingConvention = CallingConvention.Cdecl)] + static extern double gtk_widget_get_scale_factor (IntPtr widget); + + [DllImport (PangoUtil.LIBGDK, CallingConvention = CallingConvention.Cdecl)] + static extern double gdk_screen_get_monitor_scale_factor (IntPtr widget, int monitor); + + [DllImport (PangoUtil.LIBGOBJECT, CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr g_object_get_data (IntPtr source, string name); + + [DllImport (PangoUtil.LIBGTK, CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr gtk_icon_set_render_icon_scaled (IntPtr handle, IntPtr style, int direction, int state, int size, IntPtr widget, IntPtr intPtr, ref double scale); + + public static bool SetSourceScale (Gtk.IconSource source, double scale) + { + if (!supportsHiResIcons) + return false; + + try { + gtk_icon_source_set_scale (source.Handle, scale); + return true; + } catch (DllNotFoundException) { + } catch (EntryPointNotFoundException) { + } + supportsHiResIcons = false; + return false; + } + + public static bool SetSourceScaleWildcarded (Gtk.IconSource source, bool setting) + { + if (!supportsHiResIcons) + return false; + + try { + gtk_icon_source_set_scale_wildcarded (source.Handle, setting); + return true; + } catch (DllNotFoundException) { + } catch (EntryPointNotFoundException) { + } + supportsHiResIcons = false; + return false; + } + + public static Gdk.Pixbuf Get2xVariant (Gdk.Pixbuf px) + { + if (!supportsHiResIcons) + return null; + + try { + IntPtr res = g_object_get_data (px.Handle, "gdk-pixbuf-2x-variant"); + if (res != IntPtr.Zero && res != px.Handle) + return (Gdk.Pixbuf) GLib.Object.GetObject (res); + else + return null; + } catch (DllNotFoundException) { + } catch (EntryPointNotFoundException) { + } + supportsHiResIcons = false; + return null; + } + + public static void Set2xVariant (Gdk.Pixbuf px, Gdk.Pixbuf variant2x) + { + } + + public static double GetScaleFactor (Gtk.Widget w) + { + if (!supportsHiResIcons) + return 1; + + try { + return gtk_widget_get_scale_factor (w.Handle); + } catch (DllNotFoundException) { + } catch (EntryPointNotFoundException) { + } + supportsHiResIcons = false; + return 1; + } + + public static double GetScaleFactor (this Gdk.Screen screen, int monitor) + { + if (!supportsHiResIcons) + return 1; + + try { + return gdk_screen_get_monitor_scale_factor (screen.Handle, monitor); + } catch (DllNotFoundException) { + } catch (EntryPointNotFoundException) { + } + supportsHiResIcons = false; + return 1; + } + + public static double GetScaleFactor () + { + return GetScaleFactor (Gdk.Screen.Default, 0); + } + + public static double GetPixelScale () + { + if (Platform.IsWindows) + return GetScaleFactor (); + else + return 1d; + } + + public static Gdk.Pixbuf RenderIcon (this Gtk.IconSet iconset, Gtk.Style style, Gtk.TextDirection direction, Gtk.StateType state, Gtk.IconSize size, Gtk.Widget widget, string detail, double scale) + { + if (scale == 1d) + return iconset.RenderIcon (style, direction, state, size, widget, detail); + + if (!supportsHiResIcons) + return null; + + try { + IntPtr intPtr = GLib.Marshaller.StringToPtrGStrdup (detail); + IntPtr o = gtk_icon_set_render_icon_scaled (iconset.Handle, (style != null) ? style.Handle : IntPtr.Zero, (int)direction, (int)state, (int)size, (widget != null) ? widget.Handle : IntPtr.Zero, intPtr, ref scale); + Gdk.Pixbuf result = (Gdk.Pixbuf) GLib.Object.GetObject (o); + GLib.Marshaller.Free (intPtr); + return result; + } catch (DllNotFoundException) { + } catch (EntryPointNotFoundException) { + } + supportsHiResIcons = false; + return null; + } + } + + public struct KeyboardShortcut : IEquatable<KeyboardShortcut> + { + public static readonly KeyboardShortcut Empty = new KeyboardShortcut ((Gdk.Key) 0, (Gdk.ModifierType) 0); + + Gdk.ModifierType modifier; + Gdk.Key key; + + public KeyboardShortcut (Gdk.Key key, Gdk.ModifierType modifier) + { + this.modifier = modifier; + this.key = key; + } + + public Gdk.Key Key { + get { return key; } + } + + public Gdk.ModifierType Modifier { + get { return modifier; } + } + + public bool IsEmpty { + get { return Key == (Gdk.Key) 0; } + } + + public override bool Equals (object obj) + { + return obj is KeyboardShortcut && this.Equals ((KeyboardShortcut) obj); + } + + public override int GetHashCode () + { + //FIXME: we're only using a few bits of mod and mostly the lower bits of key - distribute it better + return (int) Key ^ (int) Modifier; + } + + public bool Equals (KeyboardShortcut other) + { + return other.Key == Key && other.Modifier == Modifier; + } + } + +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HPanedThin.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HPanedThin.cs index d1de2f1c9f..a4f1fec7b4 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HPanedThin.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HPanedThin.cs @@ -37,7 +37,7 @@ namespace MonoDevelop.Components public HPanedThin () { - Mono.TextEditor.GtkWorkarounds.FixContainerLeak (this); + GtkWorkarounds.FixContainerLeak (this); handle = new CustomPanedHandle (this); handle.Parent = this; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HeaderBox.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HeaderBox.cs index 6edbdf9213..c34e0eda46 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HeaderBox.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HeaderBox.cs @@ -25,7 +25,6 @@ // THE SOFTWARE. using System; using Gtk; -using Mono.TextEditor; namespace MonoDevelop.Components { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HelperMethods.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HelperMethods.cs new file mode 100644 index 0000000000..5b758e5a50 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HelperMethods.cs @@ -0,0 +1,136 @@ +// HelperMethods.cs +// +// Cut & paste from PangoCairoHelper. +// +// Author: +// Aaron Bockover <abockover@novell.com> +// Mike Krüger <mkrueger@novell.com> +// +// Copyright (c) 2008 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +using System; +using System.Linq; +using System.Runtime.InteropServices; +using System.Collections.Generic; + +namespace MonoDevelop.Components +{ + public static class HelperMethods + { + [DllImport(PangoUtil.LIBPANGOCAIRO, CallingConvention=CallingConvention.Cdecl)] + static extern void pango_cairo_show_layout (IntPtr cr, IntPtr layout); + + public static void ShowLayout (this Cairo.Context cr, Pango.Layout layout) + { + pango_cairo_show_layout (cr == null ? IntPtr.Zero : cr.Handle, layout == null ? IntPtr.Zero : layout.Handle); + } + + [DllImport(PangoUtil.LIBPANGOCAIRO, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr pango_cairo_create_layout (IntPtr cr); + + public static Pango.Layout CreateLayout (this Cairo.Context cr) + { + IntPtr raw_ret = pango_cairo_create_layout (cr == null ? IntPtr.Zero : cr.Handle); + return GLib.Object.GetObject (raw_ret) as Pango.Layout; + } + + [DllImport(PangoUtil.LIBPANGOCAIRO, CallingConvention=CallingConvention.Cdecl)] + static extern void pango_cairo_layout_path (IntPtr cr, IntPtr layout); + + public static void LayoutPath (this Cairo.Context cr, Pango.Layout layout) + { + pango_cairo_layout_path (cr == null ? IntPtr.Zero : cr.Handle, layout == null ? IntPtr.Zero : layout.Handle); + } + + [DllImport(PangoUtil.LIBPANGOCAIRO, CallingConvention=CallingConvention.Cdecl)] + static extern void pango_cairo_context_set_resolution (IntPtr pango_context, double dpi); + + public static void ContextSetResolution (this Pango.Context context, double dpi) + { + pango_cairo_context_set_resolution (context == null ? IntPtr.Zero : context.Handle, dpi); + } + + [DllImport(PangoUtil.LIBPANGOCAIRO, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr pango_layout_get_context (IntPtr layout); + + public static string GetColorString (Gdk.Color color) + { + return string.Format ("#{0:X02}{1:X02}{2:X02}", color.Red / 256, color.Green / 256, color.Blue / 256); + } + + public static Pango.Context LayoutGetContext (this Pango.Layout layout) + { + IntPtr handle = pango_layout_get_context (layout.Handle); + return handle.Equals (IntPtr.Zero) ? null : GLib.Object.GetObject (handle) as Pango.Context; + } + + public static void DrawLine (this Cairo.Context cr, Cairo.Color color, double x1, double y1, double x2, double y2) + { + cr.SetSourceColor (color); + cr.MoveTo (x1, y1); + cr.LineTo (x2, y2); + cr.Stroke (); + } + + public static void Line (this Cairo.Context cr, double x1, double y1, double x2, double y2) + { + cr.MoveTo (x1, y1); + cr.LineTo (x2, y2); + } + + public static void SharpLineX (this Cairo.Context cr, double x1, double y1, double x2, double y2) + { + cr.MoveTo (x1 + 0.5, y1); + cr.LineTo (x2 + 0.5, y2); + } + + public static void SharpLineY (this Cairo.Context cr, double x1, double y1, double x2, double y2) + { + cr.MoveTo (x1, y1 + 0.5); + cr.LineTo (x2, y2 + 0.5); + } + + public static void SetSourceColor (this Cairo.Context cr, Cairo.Color color) + { + cr.SetSourceRGBA (color.R, color.G, color.B, color.A); + } + + //this is needed for building against old Mono.Cairo versions + [Obsolete] + public static void SetSource (this Cairo.Context cr, Cairo.Pattern pattern) + { + cr.Pattern = pattern; + } + + [Obsolete] + public static Cairo.Surface GetTarget (this Cairo.Context cr) + { + return cr.Target; + } + + [Obsolete] + public static void Dispose (this Cairo.Context cr) + { + ((IDisposable)cr).Dispose (); + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HslColor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HslColor.cs new file mode 100644 index 0000000000..7e56d747d1 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HslColor.cs @@ -0,0 +1,292 @@ +// +// HslColor.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 Gdk; +using System.Collections.Generic; + +namespace MonoDevelop.Components +{ + public struct HslColor + { + public double H { + get; + set; + } + + public double S { + get; + set; + } + + public double L { + get; + set; + } + + public double Alpha { + get; + set; + } + + void ToRgb(out double r, out double g, out double b) + { + if (L == 0) { + r = g = b = 0; + return; + } + + if (S == 0) { + r = g = b = L; + } else { + double temp2 = L <= 0.5 ? L * (1.0 + S) : L + S -(L * S); + double temp1 = 2.0 * L - temp2; + + double[] t3 = new double[] { H + 1.0 / 3.0, H, H - 1.0 / 3.0}; + double[] clr= new double[] { 0, 0, 0}; + for (int i = 0; i < 3; i++) { + if (t3[i] < 0) + t3[i] += 1.0; + if (t3[i] > 1) + t3[i]-=1.0; + if (6.0 * t3[i] < 1.0) + clr[i] = temp1 + (temp2 - temp1) * t3[i] * 6.0; + else if (2.0 * t3[i] < 1.0) + clr[i] = temp2; + else if (3.0 * t3[i] < 2.0) + clr[i] = (temp1 + (temp2 - temp1) * ((2.0 / 3.0) - t3[i]) * 6.0); + else + clr[i] = temp1; + } + + r = clr[0]; + g = clr[1]; + b = clr[2]; + } + } + + public static implicit operator Color (HslColor hsl) + { + double r = 0, g = 0, b = 0; + hsl.ToRgb (out r, out g, out b); + return new Color ((byte)(255 * r), + (byte)(255 * g), + (byte)(255 * b)); + } + + public static implicit operator Cairo.Color (HslColor hsl) + { + double r = 0, g = 0, b = 0; + hsl.ToRgb (out r, out g, out b); + return new Cairo.Color (r, g, b, hsl.Alpha); + } + + public static implicit operator HslColor (Color color) + { + return new HslColor (color); + } + + public static implicit operator HslColor (Cairo.Color color) + { + return new HslColor (color); + } + + #if MAC + + public static implicit operator HslColor (AppKit.NSColor color) + { + return new HslColor ((double)color.RedComponent, (double)color.GreenComponent, (double)color.BlueComponent); + } + + public static implicit operator AppKit.NSColor (HslColor hsl) + { + double r = 0, g = 0, b = 0; + hsl.ToRgb (out r, out g, out b); + return AppKit.NSColor.FromDeviceRgba ((nfloat)r, (nfloat)g, (nfloat)b, (nfloat)hsl.Alpha); + } + + + public static implicit operator CoreGraphics.CGColor (HslColor hsl) + { + double r = 0, g = 0, b = 0; + hsl.ToRgb (out r, out g, out b); + return new CoreGraphics.CGColor ((nfloat)r, (nfloat)g, (nfloat)b, (nfloat)hsl.Alpha); + } + #endif + + + public static HslColor FromHsl (double h, double s, double l) + { + return new HslColor { + H = h, + S = s, + L = l, + Alpha = 1.0d + }; + } + + public uint ToPixel () + { + double r, g, b; + ToRgb(out r, out g, out b); + uint rv = (uint)(r * 255); + uint gv = (uint)(g * 255); + uint bv = (uint)(b * 255); + return rv << 16 | gv << 8 | bv; + } + + public static HslColor FromPixel (uint pixel) + { + var r = ((pixel >> 16) & 0xFF) / 255.0; + var g = ((pixel >> 8) & 0xFF) / 255.0; + var b = (pixel & 0xFF) / 255.0; + return new HslColor (r, g, b); + } + + public HslColor (double r, double g, double b, double a = 1.0) : this () + { + double v = System.Math.Max (r, g); + v = System.Math.Max (v, b); + + double m = System.Math.Min (r, g); + m = System.Math.Min (m, b); + + this.L = (m + v) / 2.0; + if (this.L <= 0.0) + return; + double vm = v - m; + this.S = vm; + + if (this.S > 0.0) { + this.S /= (this.L <= 0.5) ? (v + m) : (2.0 - v - m); + } else { + return; + } + + double r2 = (v - r) / vm; + double g2 = (v - g) / vm; + double b2 = (v - b) / vm; + + if (r == v) { + this.H = (g == m ? 5.0 + b2 : 1.0 - g2); + } else if (g == v) { + this.H = (b == m ? 1.0 + r2 : 3.0 - b2); + } else { + this.H = (r == m ? 3.0 + g2 : 5.0 - r2); + } + this.H /= 6.0; + + this.Alpha = a; + } + + public HslColor (Color color) : this (color.Red / (double)ushort.MaxValue, color.Green / (double)ushort.MaxValue, color.Blue / (double)ushort.MaxValue) + { + Alpha = 1.0; + } + + public HslColor (Cairo.Color color) : this (color.R, color.G, color.B, color.A) + { + } + + public static HslColor Parse (string color) + { + Gdk.Color col = new Gdk.Color (0, 0, 0); + Gdk.Color.Parse (color, ref col); + return (HslColor)col; + } + + public static double Brightness (HslColor c) + { + return Brightness ((Cairo.Color)c); + } + + public static double Brightness (Cairo.Color c) + { + double r = c.R; + double g = c.G; + double b = c.B; + return System.Math.Sqrt (r * .241 + g * .691 + b * .068); + } + + public static double Brightness (Gdk.Color c) + { + double r = c.Red / (double)ushort.MaxValue; + double g = c.Green / (double)ushort.MaxValue; + double b = c.Blue / (double)ushort.MaxValue; + return System.Math.Sqrt (r * .241 + g * .691 + b * .068); + } + + public static List<HslColor> GenerateHighlightColors (HslColor backGround, HslColor foreGround, int n) + { + double bgH = (backGround.H == 0 && backGround.S == 0) ? 2 / 3.0 : backGround.H; + var result = new List<HslColor> (); + for (int i = 0; i < n; i++) { + double h = bgH + (i + 1.0) / (double)n; + + // for monochromatic backround the h value doesn't matter + if (i + 1 == n && !(backGround.H == 0 && backGround.S == 0)) + h = bgH + 0.5; + + if (h > 1.0) + h -= 1.0; + + double s = 0.85; + double l = 0.5; + if (backGround.H == 0 && backGround.S == 0 && backGround.L < 0.5) + l = 0.8; + result.Add (HslColor.FromHsl (h, s, l)); + } + return result; + } + + public override string ToString () + { + return string.Format ("[HslColor: H={0}, S={1}, L={2}, A={3}]", H, S, L, Alpha); + } + + public string ToPangoString () + { + var resultColor = (Cairo.Color)this; + return string.Format ("#{0:x2}{1:x2}{2:x2}", + (int)(resultColor.R * 255), + (int)(resultColor.G * 255), + (int)(resultColor.B * 255)); + } + + + public string ToMarkup () + { + if (Alpha == 1.0) + return ToPangoString (); + var resultColor = (Cairo.Color)this; + return string.Format ("#{0:x2}{1:x2}{2:x2}{3:x2}", + (int)(resultColor.R * 255), + (int)(resultColor.G * 255), + (int)(resultColor.B * 255), + (int)(resultColor.A * 255)); + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ImageView.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ImageView.cs index 5851edf92f..8b9bc5a851 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ImageView.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ImageView.cs @@ -70,7 +70,7 @@ namespace MonoDevelop.Components } double IconScale { - get { return Mono.TextEditor.GtkWorkarounds.GetPixelScale (); } + get { return GtkWorkarounds.GetPixelScale (); } } protected override void OnSizeRequested (ref Gtk.Requisition requisition) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/MiniButton.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/MiniButton.cs index 34a646b8e4..715045bf8c 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/MiniButton.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/MiniButton.cs @@ -26,7 +26,6 @@ using System; using Gtk; -using Mono.TextEditor; namespace MonoDevelop.Components { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PangoUtil.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PangoUtil.cs new file mode 100644 index 0000000000..86de702f7f --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PangoUtil.cs @@ -0,0 +1,260 @@ +// +// PangoUtils.cs +// +// Author: +// Michael Hutchinson <mhutchinson@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 Gtk; +using System.Runtime.InteropServices; + +namespace MonoDevelop.Components +{ + public static class PangoUtil + { + internal const string LIBGTK = "libgtk-win32-2.0-0.dll"; + internal const string LIBATK = "libatk-1.0-0.dll"; + internal const string LIBGLIB = "libglib-2.0-0.dll"; + internal const string LIBGDK = "libgdk-win32-2.0-0.dll"; + internal const string LIBGOBJECT = "libgobject-2.0-0.dll"; + internal const string LIBPANGO = "libpango-1.0-0.dll"; + internal const string LIBPANGOCAIRO = "libpangocairo-1.0-0.dll"; + internal const string LIBQUARTZ = "libgtk-quartz-2.0.dylib"; + internal const string LIBGTKGLUE = "gtksharpglue-2"; + + /// <summary> + /// This doesn't leak Pango layouts, unlike some other ways to create them in GTK# <= 2.12.11 + /// </summary> + public static Pango.Layout CreateLayout (Widget widget) + { + var ptr = gtk_widget_create_pango_layout (widget.Handle, IntPtr.Zero); + return ptr == IntPtr.Zero? null : new Pango.Layout (ptr); + } + + public static Pango.Layout CreateLayout (Widget widget, string text) + { + IntPtr textPtr = text == null? IntPtr.Zero : GLib.Marshaller.StringToPtrGStrdup (text); + + var ptr = gtk_widget_create_pango_layout (widget.Handle, textPtr); + + if (textPtr != IntPtr.Zero) + GLib.Marshaller.Free (textPtr); + + return ptr == IntPtr.Zero? null : new Pango.Layout (ptr); + } + + public static Pango.Layout CreateLayout (PrintContext context) + { + var ptr = gtk_print_context_create_pango_layout (context.Handle); + return ptr == IntPtr.Zero? null : new Pango.Layout (ptr); + } + + [DllImport (LIBGTK, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr gtk_widget_create_pango_layout (IntPtr widget, IntPtr text); + + [DllImport (LIBGTK, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr gtk_print_context_create_pango_layout (IntPtr context); + } + + /// <summary> + /// This creates a Pango list and applies attributes to it with *much* less overhead than the GTK# version. + /// </summary> + class FastPangoAttrList : IDisposable + { + IntPtr list; + + public FastPangoAttrList () + { + list = pango_attr_list_new (); + } + + public void AddStyleAttribute (Pango.Style style, uint start, uint end) + { + Add (pango_attr_style_new (style), start, end); + } + + public void AddWeightAttribute (Pango.Weight weight, uint start, uint end) + { + Add (pango_attr_weight_new (weight), start, end); + } + + public void AddForegroundAttribute (Gdk.Color color, uint start, uint end) + { + Add (pango_attr_foreground_new (color.Red, color.Green, color.Blue), start, end); + } + + public void AddBackgroundAttribute (Gdk.Color color, uint start, uint end) + { + Add (pango_attr_background_new (color.Red, color.Green, color.Blue), start, end); + } + + public void AddUnderlineAttribute (Pango.Underline underline, uint start, uint end) + { + Add (pango_attr_underline_new (underline), start, end); + } + + void Add (IntPtr attribute, uint start, uint end) + { + unsafe { + PangoAttribute *attPtr = (PangoAttribute *) attribute; + attPtr->start_index = start; + attPtr->end_index = end; + } + pango_attr_list_insert (list, attribute); + } + + /// <summary> + /// Like Splice, except it only offsets/clamps the inserted items, doesn't affect items already in the list. + /// </summary> + public void InsertOffsetList (Pango.AttrList atts, uint startOffset, uint endOffset) + { + //HACK: atts.Iterator.Attrs broken (throws NRE), so manually P/Invoke + var iter = pango_attr_list_get_iterator (atts.Handle); + try { + do { + IntPtr list = pango_attr_iterator_get_attrs (iter); + try { + int len = g_slist_length (list); + for (uint i = 0; i < len; i++) { + IntPtr val = g_slist_nth_data (list, i); + AddOffsetCopy (val, startOffset, endOffset); + } + } finally { + g_slist_free (list); + } + } while (pango_attr_iterator_next (iter)); + } finally { + pango_attr_iterator_destroy (iter); + } + } + + void AddOffsetCopy (IntPtr attr, uint startOffset, uint endOffset) + { + var copy = pango_attribute_copy (attr); + unsafe { + PangoAttribute *attPtr = (PangoAttribute *) copy; + attPtr->start_index = startOffset + attPtr->start_index; + attPtr->end_index = System.Math.Min (endOffset, startOffset + attPtr->end_index); + } + pango_attr_list_insert (list, copy); + } + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr pango_attr_style_new (Pango.Style style); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr pango_attr_stretch_new (Pango.Stretch stretch); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr pango_attr_weight_new (Pango.Weight weight); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr pango_attr_foreground_new (ushort red, ushort green, ushort blue); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr pango_attr_background_new (ushort red, ushort green, ushort blue); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr pango_attr_underline_new (Pango.Underline underline); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr pango_attr_list_new (); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern void pango_attr_list_unref (IntPtr list); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern void pango_attr_list_insert (IntPtr list, IntPtr attr); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern void pango_layout_set_attributes (IntPtr layout, IntPtr attrList); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern void pango_attr_list_splice (IntPtr attr_list, IntPtr other, Int32 pos, Int32 len); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr pango_attribute_copy (IntPtr attr); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr pango_attr_list_get_iterator (IntPtr list); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern bool pango_attr_iterator_next (IntPtr iterator); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern void pango_attr_iterator_destroy (IntPtr iterator); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr pango_attr_iterator_get_attrs (IntPtr iterator); + + [DllImport (PangoUtil.LIBGLIB, CallingConvention = CallingConvention.Cdecl)] + private static extern int g_slist_length (IntPtr l); + + [DllImport (PangoUtil.LIBGLIB, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr g_slist_nth_data (IntPtr l, uint n); + + [DllImport (PangoUtil.LIBGLIB, CallingConvention = CallingConvention.Cdecl)] + private static extern void g_slist_free (IntPtr l); + + public void Splice (Pango.AttrList attrs, int pos, int len) + { + pango_attr_list_splice (list, attrs.Handle, pos, len); + } + + public void AssignTo (Pango.Layout layout) + { + pango_layout_set_attributes (layout.Handle, list); + } + + [StructLayout (LayoutKind.Sequential)] + struct PangoAttribute + { + public IntPtr klass; + public uint start_index; + public uint end_index; + } + + public void Dispose () + { + if (list != IntPtr.Zero) { + GC.SuppressFinalize (this); + Destroy (); + } + } + + //NOTE: the list destroys all its attributes when the ref count reaches zero + void Destroy () + { + pango_attr_list_unref (list); + list = IntPtr.Zero; + } + + ~FastPangoAttrList () + { + GLib.Idle.Add (delegate { + Destroy (); + return false; + }); + } + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PathBar.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PathBar.cs index 8ff221923e..d90d8d8a81 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PathBar.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PathBar.cs @@ -25,15 +25,13 @@ // THE SOFTWARE. using System; -using System.Collections.Generic; using System.Linq; using Gtk; using Gdk; using MonoDevelop.Ide; using MonoDevelop.Ide.Gui; -using Mono.TextEditor; -using ICSharpCode.NRefactory; +using MonoDevelop.Core.Text; namespace MonoDevelop.Components { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PopoverWindow.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PopoverWindow.cs index 04d0d9f32b..97423f16c6 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PopoverWindow.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PopoverWindow.cs @@ -27,7 +27,6 @@ using System; using Gtk; using MonoDevelop.Ide; using MonoDevelop.Ide.Gui; -using Mono.TextEditor; using Gdk; using Xwt.Motion; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PopoverWindowTheme.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PopoverWindowTheme.cs index d477f4700b..29bf1dcc25 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PopoverWindowTheme.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PopoverWindowTheme.cs @@ -27,8 +27,9 @@ using System; using Gtk; using MonoDevelop.Ide; using MonoDevelop.Ide.Gui; -using Mono.TextEditor; using Gdk; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Ide.Editor.Highlighting; namespace MonoDevelop.Components { @@ -264,7 +265,7 @@ namespace MonoDevelop.Components Font = Pango.FontDescription.FromString ("Normal"); } - public void SetSchemeColors (Mono.TextEditor.Highlighting.ColorScheme scheme) + public void SetSchemeColors (ColorScheme scheme) { TopColor = scheme.TooltipText.Background.AddLight (0.03); BottomColor = scheme.TooltipText.Background; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/SearchEntry.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/SearchEntry.cs index 44bd4e78ed..188f400dd3 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/SearchEntry.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/SearchEntry.cs @@ -31,7 +31,6 @@ using System; using Gtk; using MonoDevelop.Core; using MonoDevelop.Ide.Gui; -using Mono.TextEditor; namespace MonoDevelop.Components { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/SectionList.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/SectionList.cs index 02688602d0..6ea647114b 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/SectionList.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/SectionList.cs @@ -29,7 +29,6 @@ using System.Collections.Generic; using Gtk; using Gdk; using Cairo; -using Mono.TextEditor; namespace MonoDevelop.Components { @@ -91,7 +90,7 @@ namespace MonoDevelop.Components public SectionList () { - Mono.TextEditor.GtkWorkarounds.FixContainerLeak (this); + GtkWorkarounds.FixContainerLeak (this); this.WidgetFlags |= WidgetFlags.NoWindow; WidthRequest = 100; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Tabstrip.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Tabstrip.cs index d6c04f907a..a60edb358e 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Tabstrip.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Tabstrip.cs @@ -30,7 +30,6 @@ using System.ComponentModel; using System.Drawing.Design; using Cairo; using Gtk; -using Mono.TextEditor; using System.Linq; namespace MonoDevelop.Components diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/TooltipWindow.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/TooltipWindow.cs index e5685f27af..95036e0cbe 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/TooltipWindow.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/TooltipWindow.cs @@ -26,12 +26,10 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -using System; using MonoDevelop.Ide; using Gtk; using Gdk; -using Mono.TextEditor.PopupWindow; namespace MonoDevelop.Components { @@ -41,7 +39,7 @@ namespace MonoDevelop.Components public string LinkColor { get { - var color = Mono.TextEditor.HslColor.GenerateHighlightColors (Style.Background (State), Style.Text (State), 3)[2]; + var color = HslColor.GenerateHighlightColors (Style.Background (State), Style.Text (State), 3)[2]; return color.ToPangoString (); } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/VPanedThin.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/VPanedThin.cs index 838c04f970..ac607fda01 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/VPanedThin.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/VPanedThin.cs @@ -34,7 +34,7 @@ namespace MonoDevelop.Components public VPanedThin () { - Mono.TextEditor.GtkWorkarounds.FixContainerLeak (this); + GtkWorkarounds.FixContainerLeak (this); handle = new CustomPanedHandle (this); handle.Parent = this; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/WindowTransparencyDecorator.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/WindowTransparencyDecorator.cs new file mode 100644 index 0000000000..9b8e8526d8 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/WindowTransparencyDecorator.cs @@ -0,0 +1,116 @@ +// +// WindowTransparencyDecorator.cs +// +// Author: +// Michael Hutchinson <mhutch@xamarin.com> +// +// Based on code derived from Banshee.Widgets.EllipsizeLabel +// by Aaron Bockover (aaron@aaronbock.net) +// +// Copyright (C) 2005-2008 Novell, Inc (http://www.novell.com) +// Copyright (C) 2012 Xamarin Inc (http://www.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.Reflection; +using System.Runtime.InteropServices; + +using Gtk; +using Gdk; + +namespace MonoDevelop.Components +{ + public class WindowTransparencyDecorator + { + Gtk.Window window; + bool semiTransparent; + bool snooperInstalled; + uint snooperID; + const double opacity = 0.2; + + WindowTransparencyDecorator (Gtk.Window window) + { + this.window = window; + + window.Shown += ShownHandler; + window.Hidden += HiddenHandler; + window.Destroyed += DestroyedHandler; + } + + public static WindowTransparencyDecorator Attach (Gtk.Window window) + { + return new WindowTransparencyDecorator (window); + } + + public void Detach () + { + if (window == null) + return; + + //remove the snooper + HiddenHandler (null, null); + + //annul allreferences between this and the window + window.Shown -= ShownHandler; + window.Hidden -= HiddenHandler; + window.Destroyed -= DestroyedHandler; + window = null; + } + + void ShownHandler (object sender, EventArgs args) + { + if (!snooperInstalled) + snooperID = Gtk.Key.SnooperInstall (TransparencyKeySnooper); + snooperInstalled = true; + + //NOTE: we unset transparency when showing, instead of when hiding + //because the latter case triggers a metacity+compositing bug that shows the window again + SemiTransparent = false; + } + + void HiddenHandler (object sender, EventArgs args) + { + if (snooperInstalled) + Gtk.Key.SnooperRemove (snooperID); + snooperInstalled = false; + } + + void DestroyedHandler (object sender, EventArgs args) + { + Detach (); + } + + int TransparencyKeySnooper (Gtk.Widget widget, EventKey evnt) + { + if (evnt != null && evnt.Key == Gdk.Key.Control_L || evnt.Key == Gdk.Key.Control_R) + SemiTransparent = (evnt.Type == Gdk.EventType.KeyPress); + return 0; //FALSE + } + + bool SemiTransparent { + set { + if (semiTransparent != value) { + semiTransparent = value; + window.Opacity = semiTransparent? opacity : 1.0; + } + } + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CodeCompletionContextEventArgs.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CodeCompletionContextEventArgs.cs index a658f30b05..5d455b0bb3 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CodeCompletionContextEventArgs.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CodeCompletionContextEventArgs.cs @@ -29,7 +29,7 @@ namespace MonoDevelop.Ide.CodeCompletion { public class CodeCompletionContextEventArgs : EventArgs { - public ICompletionWidget Widget { + internal ICompletionWidget Widget { get; set; } @@ -44,7 +44,7 @@ namespace MonoDevelop.Ide.CodeCompletion set; } - public CodeCompletionContextEventArgs (ICompletionWidget widget, CodeCompletionContext codeCompletionContext, string completedWord) + internal CodeCompletionContextEventArgs (ICompletionWidget widget, CodeCompletionContext codeCompletionContext, string completedWord) { this.Widget = widget; this.CodeCompletionContext = codeCompletionContext; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionCategory.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionCategory.cs new file mode 100644 index 0000000000..fc88fd62b2 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionCategory.cs @@ -0,0 +1,50 @@ +// +// CompletionCategory.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.CodeCompletion +{ + public abstract class CompletionCategory : IComparable<CompletionCategory> + { + public string DisplayText { get; set; } + + public string Icon { get; set; } + + protected CompletionCategory () + { + } + + protected CompletionCategory (string displayText, string icon) + { + this.DisplayText = displayText; + this.Icon = icon; + } + + public abstract int CompareTo (CompletionCategory other); + } + +} + diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionData.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionData.cs index 2303d06b5e..e93c4687d0 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionData.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionData.cs @@ -30,11 +30,11 @@ using System; using System.Collections.Generic; using System.Linq; using MonoDevelop.Core; -using ICSharpCode.NRefactory.Completion; +using MonoDevelop.Ide.Editor.Extension; namespace MonoDevelop.Ide.CodeCompletion { - public class CompletionData : ICompletionData, IComparable + public class CompletionData : IComparable { protected CompletionData () {} @@ -43,6 +43,13 @@ namespace MonoDevelop.Ide.CodeCompletion public virtual string Description { get; set; } public virtual string CompletionText { get; set; } + /// <summary> + /// int.MaxValue == highest prioriy, + /// -int.MaxValue == lowest priority + /// </summary> + /// <value>The priority group.</value> + public virtual int PriorityGroup { get { return 0; } } + public virtual string GetDisplayDescription (bool isSelected) { return null; @@ -70,13 +77,13 @@ namespace MonoDevelop.Ide.CodeCompletion } } - public virtual IEnumerable<ICompletionData> OverloadedData { + public virtual IReadOnlyList<CompletionData> OverloadedData { get { throw new InvalidOperationException (); } } - public virtual void AddOverload (ICompletionData data) + public virtual void AddOverload (CompletionData data) { throw new InvalidOperationException (); } @@ -102,7 +109,7 @@ namespace MonoDevelop.Ide.CodeCompletion return result; } - public virtual void InsertCompletionText (CompletionListWindow window, ref KeyActions ka, Gdk.Key closeChar, char keyChar, Gdk.ModifierType modifier) + public virtual void InsertCompletionText (CompletionListWindow window, ref KeyActions ka, KeyDescriptor descriptor) { var currentWord = GetCurrentWord (window); window.CompletionWidget.SetCompletionText (window.CodeCompletionContext, currentWord, CompletionText); @@ -117,12 +124,12 @@ namespace MonoDevelop.Ide.CodeCompletion public virtual int CompareTo (object obj) { - if (!(obj is ICompletionData)) + if (!(obj is CompletionData)) return 0; - return Compare (this, (ICompletionData)obj); + return Compare (this, (CompletionData)obj); } - public static int Compare (ICompletionData a, ICompletionData b) + public static int Compare (CompletionData a, CompletionData b) { var result = ((a.DisplayFlags & DisplayFlags.Obsolete) == (b.DisplayFlags & DisplayFlags.Obsolete)) ? StringComparer.OrdinalIgnoreCase.Compare (a.DisplayText, b.DisplayText) : (a.DisplayFlags & DisplayFlags.Obsolete) != 0 ? 1 : -1; if (result == 0) { @@ -133,7 +140,7 @@ namespace MonoDevelop.Ide.CodeCompletion if (aIsImport && !bIsImport) return 1; if (aIsImport && bIsImport) - return StringComparer.Ordinal.Compare (a.Description, b.Description); + return StringComparer.Ordinal.Compare (((CompletionData)a).Description, ((CompletionData)b).Description); var ca = a as CompletionData; var cb = b as CompletionData; if (ca != null && cb != null && !ca.Icon.IsNull && !cb.Icon.IsNull) { @@ -144,5 +151,19 @@ namespace MonoDevelop.Ide.CodeCompletion } #endregion + + protected string ApplyDiplayFlagsFormatting (string markup) + { + if (!HasOverloads && (DisplayFlags & DisplayFlags.Obsolete) != 0 || HasOverloads && OverloadedData.All (data => (data.DisplayFlags & DisplayFlags.Obsolete) != 0)) + return "<s>" + markup + "</s>"; + if ((DisplayFlags & DisplayFlags.MarkedBold) != 0) + return "<b>" + markup + "</b>"; + return markup; + } + + public virtual string GetDisplayTextMarkup () + { + return ApplyDiplayFlagsFormatting (GLib.Markup.EscapeText (DisplayText)); + } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionDataList.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionDataList.cs index 2311475497..e0c21b8177 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionDataList.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionDataList.cs @@ -30,12 +30,14 @@ using System; using System.Collections.Generic; using System.Linq; using MonoDevelop.Core; -using ICSharpCode.NRefactory.Completion; +using MonoDevelop.Ide.Editor.Extension; namespace MonoDevelop.Ide.CodeCompletion { - public interface ICompletionDataList : IList<ICompletionData> + public interface ICompletionDataList : IList<CompletionData> { + int TriggerWordLength { get; } + bool IsSorted { get; } bool AutoCompleteUniqueMatch { get; } bool AutoCompleteEmptyMatch { get; } @@ -44,8 +46,8 @@ namespace MonoDevelop.Ide.CodeCompletion bool AutoSelect { get; } string DefaultCompletionString { get; } CompletionSelectionMode CompletionSelectionMode { get; } - void Sort (Comparison<ICompletionData> comparison); - void Sort (IComparer<ICompletionData> comparison); + void Sort (Comparison<CompletionData> comparison); + void Sort (IComparer<CompletionData> comparison); IEnumerable<ICompletionKeyHandler> KeyHandler { get; } @@ -56,8 +58,8 @@ namespace MonoDevelop.Ide.CodeCompletion public interface ICompletionKeyHandler { - bool PreProcessKey (CompletionListWindow listWindow, Gdk.Key key, char keyChar, Gdk.ModifierType modifier, out KeyActions keyAction); - bool PostProcessKey (CompletionListWindow listWindow, Gdk.Key key, char keyChar, Gdk.ModifierType modifier, out KeyActions keyAction); + bool PreProcessKey (CompletionListWindow listWindow, KeyDescriptor descriptor, out KeyActions keyAction); + bool PostProcessKey (CompletionListWindow listWindow, KeyDescriptor descriptor, out KeyActions keyAction); } public enum CompletionSelectionMode { @@ -65,10 +67,12 @@ namespace MonoDevelop.Ide.CodeCompletion OwnTextField } - public class CompletionDataList : List<ICompletionData>, ICompletionDataList + public class CompletionDataList : List<CompletionData>, ICompletionDataList { + public int TriggerWordLength { get; set; } + public bool IsSorted { get; set; } - public IComparer<ICompletionData> Comparer { get; set; } + public IComparer<CompletionData> Comparer { get; set; } public bool AutoCompleteUniqueMatch { get; set; } public string DefaultCompletionString { get; set; } @@ -87,7 +91,7 @@ namespace MonoDevelop.Ide.CodeCompletion this.AutoSelect = true; } - public CompletionDataList (IEnumerable<ICompletionData> data) : base(data) + public CompletionDataList (IEnumerable<CompletionData> data) : base(data) { this.AutoSelect = true; } @@ -144,7 +148,7 @@ namespace MonoDevelop.Ide.CodeCompletion return false; } - public void RemoveWhere (Func<ICompletionData,bool> shouldRemove) + public void RemoveWhere (Func<CompletionData,bool> shouldRemove) { for (int i = 0; i < this.Count;) { if (shouldRemove (this[i])) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionListWindow.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionListWindow.cs index 224fa14b54..d31732a305 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionListWindow.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionListWindow.cs @@ -32,8 +32,8 @@ using Gtk; using MonoDevelop.Core; using MonoDevelop.Components; using System.Linq; -using ICSharpCode.NRefactory.Completion; -using Mono.TextEditor.PopupWindow; +using MonoDevelop.Ide.Editor.Extension; +using System.ComponentModel; namespace MonoDevelop.Ide.CodeCompletion { @@ -42,11 +42,11 @@ namespace MonoDevelop.Ide.CodeCompletion const int declarationWindowMargin = 3; TooltipInformationWindow declarationviewwindow; - ICompletionData currentData; + CompletionData currentData; Widget parsingMessage; int initialWordLength; int previousWidth = -1, previousHeight = -1; - + public CodeCompletionContext CodeCompletionContext { get; set; @@ -60,7 +60,6 @@ namespace MonoDevelop.Ide.CodeCompletion } IMutableCompletionDataList mutableList; - ICompletionDataList completionDataList; public ICompletionDataList CompletionDataList { get { return completionDataList; } set { @@ -93,7 +92,7 @@ namespace MonoDevelop.Ide.CodeCompletion List.QueueDraw (); }; previewEntry.KeyPressEvent += delegate(object o, KeyPressEventArgs args) { - var keyAction = PreProcessKey (args.Event.Key, (char)args.Event.KeyValue, args.Event.State); + var keyAction = PreProcessKey (KeyDescriptor.FromGtk (args.Event.Key, (char)args.Event.KeyValue, args.Event.State)); if (keyAction.HasFlag (KeyActions.Complete)) CompleteWord (); @@ -189,61 +188,81 @@ namespace MonoDevelop.Ide.CodeCompletion base.OnDestroyed (); } - public void PostProcessKeyEvent (Gdk.Key key, char keyChar, Gdk.ModifierType modifier) + public void PostProcessKeyEvent (KeyDescriptor descriptor) { KeyActions ka = KeyActions.None; bool keyHandled = false; - foreach (var handler in CompletionDataList.KeyHandler) { - if (handler.PostProcessKey (this, key, keyChar, modifier, out ka)) { - keyHandled = true; - break; + if (CompletionDataList != null) { + foreach (var handler in CompletionDataList.KeyHandler) { + if (handler.PostProcessKey (this, descriptor, out ka)) { + keyHandled = true; + break; + } } } if (!keyHandled) - ka = PostProcessKey (key, keyChar, modifier); + ka = PostProcessKey (descriptor); if ((ka & KeyActions.Complete) != 0) - CompleteWord (ref ka, key, keyChar, modifier); - if ((ka & KeyActions.CloseWindow) != 0) + CompleteWord (ref ka, descriptor); + if ((ka & KeyActions.CloseWindow) != 0) { CompletionWindowManager.HideWindow (); + OnWindowClosed (EventArgs.Empty); + } + } + + /// <summary> + /// For unit test purposes. + /// </summary> + [EditorBrowsableAttribute(EditorBrowsableState.Never)] + internal event EventHandler WindowClosed; + + protected virtual void OnWindowClosed (EventArgs e) + { + var handler = WindowClosed; + if (handler != null) + handler (this, e); } public void ToggleCategoryMode () { - List.InCategoryMode = !List.InCategoryMode; + ListWidget.EnableCompletionCategoryMode.Set (!ListWidget.EnableCompletionCategoryMode.Value); + List.UpdateCategoryMode (); ResetSizes (); List.QueueDraw (); } - public bool PreProcessKeyEvent (Gdk.Key key, char keyChar, Gdk.ModifierType modifier) + public bool PreProcessKeyEvent (KeyDescriptor descriptor) { - if (key == Gdk.Key.Escape) { + if (descriptor.SpecialKey == SpecialKey.Escape) { CompletionWindowManager.HideWindow (); return true; } KeyActions ka = KeyActions.None; bool keyHandled = false; - foreach (ICompletionKeyHandler handler in CompletionDataList.KeyHandler) { - if (handler.PreProcessKey (this, key, keyChar, modifier, out ka)) { - keyHandled = true; - break; + if (CompletionDataList != null) { + foreach (ICompletionKeyHandler handler in CompletionDataList.KeyHandler) { + if (handler.PreProcessKey (this, descriptor, out ka)) { + keyHandled = true; + break; + } } } - if (!keyHandled) - ka = PreProcessKey (key, keyChar, modifier); + ka = PreProcessKey (descriptor); if ((ka & KeyActions.Complete) != 0) - CompleteWord (ref ka, key, keyChar, modifier); + CompleteWord (ref ka, descriptor); - if ((ka & KeyActions.CloseWindow) != 0) + if ((ka & KeyActions.CloseWindow) != 0) { CompletionWindowManager.HideWindow (); + OnWindowClosed (EventArgs.Empty); + } if ((ka & KeyActions.Ignore) != 0) return true; - if ((ka & KeyActions.Process) != 0) { - if (key == Gdk.Key.Left || key == Gdk.Key.Right) { + if (descriptor.SpecialKey == SpecialKey.Left || descriptor.SpecialKey == SpecialKey.Right) { // Close if there's a modifier active EXCEPT lock keys and Modifiers // Makes an exception for Mod1Mask (usually alt), shift, control, meta and super // This prevents the window from closing if the num/scroll/caps lock are active @@ -252,19 +271,21 @@ namespace MonoDevelop.Ide.CodeCompletion // if ((modifier & ~(Gdk.ModifierType.LockMask | (Gdk.ModifierType.ModifierMask & ~(Gdk.ModifierType.ShiftMask | Gdk.ModifierType.Mod1Mask | Gdk.ModifierType.ControlMask | Gdk.ModifierType.MetaMask | Gdk.ModifierType.SuperMask)))) != 0) { // this version doesn't work for my system - seems that I've a modifier active // that gdk doesn't know about. How about the 2nd version - should close on left/rigt + shift/mod1/control/meta/super - if ((modifier & (Gdk.ModifierType.ShiftMask | Gdk.ModifierType.Mod1Mask | Gdk.ModifierType.ControlMask | Gdk.ModifierType.MetaMask | Gdk.ModifierType.SuperMask)) != 0) { + if ((descriptor.ModifierKeys & (ModifierKeys.Shift | ModifierKeys.Alt | ModifierKeys.Control | ModifierKeys.Command)) != 0) { CompletionWindowManager.HideWindow (); + OnWindowClosed (EventArgs.Empty); return false; } if (declarationviewwindow != null && declarationviewwindow.Multiple) { - if (key == Gdk.Key.Left) + if (descriptor.SpecialKey == SpecialKey.Left) declarationviewwindow.OverloadLeft (); else declarationviewwindow.OverloadRight (); UpdateDeclarationView (); } else { CompletionWindowManager.HideWindow (); + OnWindowClosed (EventArgs.Empty); return false; } return true; @@ -286,8 +307,8 @@ namespace MonoDevelop.Ide.CodeCompletion { if (list == null) throw new ArgumentNullException ("list"); - if (completionContext == null) - throw new ArgumentNullException ("completionContext"); + if (completionWidget == null) + throw new ArgumentNullException ("completionWidget"); if (completionContext == null) throw new ArgumentNullException ("completionContext"); if (mutableList != null) { @@ -355,9 +376,9 @@ namespace MonoDevelop.Ide.CodeCompletion return false; } - class DataItemComparer : IComparer<ICompletionData> + class DataItemComparer : IComparer<CompletionData> { - public int Compare (ICompletionData a, ICompletionData b) + public int Compare (CompletionData a, CompletionData b) { if (a is IComparable && b is IComparable) return ((IComparable)a).CompareTo (b); @@ -365,7 +386,7 @@ namespace MonoDevelop.Ide.CodeCompletion } } - IComparer<ICompletionData> GetComparerForCompletionList (ICompletionDataList dataList) + IComparer<CompletionData> GetComparerForCompletionList (ICompletionDataList dataList) { var concrete = dataList as CompletionDataList; return concrete != null && concrete.Comparer != null ? concrete.Comparer : new DataItemComparer (); @@ -443,23 +464,40 @@ namespace MonoDevelop.Ide.CodeCompletion public bool CompleteWord () { KeyActions ka = KeyActions.None; - return CompleteWord (ref ka, (Gdk.Key)0, '\0', Gdk.ModifierType.None); + return CompleteWord (ref ka, KeyDescriptor.Empty); } - - public bool CompleteWord (ref KeyActions ka, Gdk.Key closeChar, char keyChar, Gdk.ModifierType modifier) + + internal bool IsInCompletion { get; set; } + + public bool CompleteWord (ref KeyActions ka, KeyDescriptor descriptor) { if (SelectedItem == -1 || completionDataList == null) return false; var item = completionDataList [SelectedItem]; if (item == null) return false; - // first close the completion list, then insert the text. - // this is required because that's the logical event chain, otherwise things could be messed up - CloseCompletionList (); - ((CompletionData)item).InsertCompletionText (this, ref ka, closeChar, keyChar, modifier); - AddWordToHistory (PartialWord, item.CompletionText); - OnWordCompleted (new CodeCompletionContextEventArgs (CompletionWidget, CodeCompletionContext, item.CompletionText)); - return true; + IsInCompletion = true; + try {
+ // first close the completion list, then insert the text.
+ // this is required because that's the logical event chain, otherwise things could be messed up
+ CloseCompletionList ();
+ /* var cdItem = (CompletionData)item; + cdItem.InsertCompletionText (this, ref ka, closeChar, keyChar, modifier); + AddWordToHistory (PartialWord, cdItem.CompletionText); + OnWordCompleted (new CodeCompletionContextEventArgs (CompletionWidget, CodeCompletionContext, cdItem.CompletionText)); + */
+ if (item.HasOverloads && declarationviewwindow.CurrentOverload >= 0 && declarationviewwindow.CurrentOverload < item.OverloadedData.Count) { + item.OverloadedData[declarationviewwindow.CurrentOverload].InsertCompletionText (this, ref ka, descriptor); + } else { + item.InsertCompletionText (this, ref ka, descriptor); + } + cache.CommitCompletionData (item); + OnWordCompleted (new CodeCompletionContextEventArgs (CompletionWidget, CodeCompletionContext, item.DisplayText)); + } finally { + IsInCompletion = false; + CompletionWindowManager.HideWindow (); + } + return true; } protected virtual void OnWordCompleted (CodeCompletionContextEventArgs e) @@ -587,17 +625,17 @@ namespace MonoDevelop.Ide.CodeCompletion return false; var data = completionDataList [selectedItem]; - IEnumerable<ICompletionData> filteredOverloads; + IEnumerable<CompletionData> filteredOverloads; if (data.HasOverloads) { filteredOverloads = data.OverloadedData; } else { - filteredOverloads = new ICompletionData[] { data }; + filteredOverloads = new CompletionData[] { data }; } EnsureDeclarationViewWindow (); if (data != currentData) { declarationviewwindow.Clear (); - var overloads = new List<ICompletionData> (filteredOverloads); + var overloads = new List<CompletionData> (filteredOverloads); foreach (var overload in overloads) { declarationviewwindow.AddOverload ((CompletionData)overload); } @@ -626,7 +664,7 @@ namespace MonoDevelop.Ide.CodeCompletion return false; } - protected override void ResetState () + protected internal override void ResetState () { StartOffset = 0; previousWidth = previousHeight = -1; @@ -663,28 +701,27 @@ namespace MonoDevelop.Ide.CodeCompletion bool IListDataProvider.HasMarkup (int n) { - return (completionDataList [n].DisplayFlags & (DisplayFlags.Obsolete | DisplayFlags.MarkedBold)) != 0; + return true; } //NOTE: we only ever return markup for items marked as obsolete string IListDataProvider.GetMarkup (int n) { var completionData = completionDataList[n]; - if (!completionData.HasOverloads && (completionData.DisplayFlags & DisplayFlags.Obsolete) != 0 || - completionData.HasOverloads && completionData.OverloadedData.All (data => (data.DisplayFlags & DisplayFlags.Obsolete) != 0)) - return "<s>" + GLib.Markup.EscapeText (completionDataList[n].DisplayText) + "</s>"; - - if ((completionData.DisplayFlags & DisplayFlags.MarkedBold) != 0) - return "<b>" + GLib.Markup.EscapeText (completionDataList[n].DisplayText) + "</b>"; - return GLib.Markup.EscapeText (completionDataList[n].DisplayText); + return completionData.GetDisplayTextMarkup (); } string IListDataProvider.GetCompletionText (int n) { - return completionDataList[n].CompletionText; + return ((CompletionData)completionDataList[n]).CompletionText; + } + + CompletionData IListDataProvider.GetCompletionData (int n) + { + return completionDataList[n]; } - IComparer<ICompletionData> defaultComparer; + IComparer<CompletionData> defaultComparer; int IListDataProvider.CompareTo (int n, int m) { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionWindowManager.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionWindowManager.cs index ab723739b3..b383b7b65d 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionWindowManager.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionWindowManager.cs @@ -27,6 +27,7 @@ using System; using MonoDevelop.Core; using MonoDevelop.Ide.Gui.Content; +using MonoDevelop.Ide.Editor.Extension; namespace MonoDevelop.Ide.CodeCompletion { @@ -80,20 +81,22 @@ namespace MonoDevelop.Ide.CodeCompletion } // ext may be null, but then parameter completion don't work - public static bool ShowWindow (CompletionTextEditorExtension ext, char firstChar, ICompletionDataList list, ICompletionWidget completionWidget, CodeCompletionContext completionContext) + internal static bool ShowWindow (CompletionTextEditorExtension ext, char firstChar, ICompletionDataList list, ICompletionWidget completionWidget, CodeCompletionContext completionContext) { try { if (ext != null) { - int inserted = ext.document.Editor.EnsureCaretIsNotVirtual (); + int inserted = ext.Editor.EnsureCaretIsNotVirtual (); if (inserted > 0) - completionContext.TriggerOffset = ext.document.Editor.Caret.Offset; + completionContext.TriggerOffset = ext.Editor.CaretOffset; } if (wnd == null) { wnd = new CompletionListWindow (); wnd.WordCompleted += HandleWndWordCompleted; } - if (ext != null) - wnd.TransientFor = ext.document.Editor.Parent.Toplevel as Gtk.Window; + if (ext != null) { + var widget = ext.Editor.GetNativeWidget<Gtk.Widget> (); + wnd.TransientFor = widget?.Parent?.Toplevel as Gtk.Window; + } wnd.Extension = ext; try { if (!wnd.ShowListWindow (firstChar, list, completionWidget, completionContext)) { @@ -136,21 +139,22 @@ namespace MonoDevelop.Ide.CodeCompletion OnWindowClosed (EventArgs.Empty); } - public static bool PreProcessKeyEvent (Gdk.Key key, char keyChar, Gdk.ModifierType modifier) + public static bool PreProcessKeyEvent (KeyDescriptor descriptor) { if (!IsVisible) return false; - if (keyChar != '\0') { + if (descriptor.KeyChar != '\0') { wnd.EndOffset = wnd.StartOffset + wnd.CurrentPartialWord.Length + 1; } - return wnd.PreProcessKeyEvent (key, keyChar, modifier); + return wnd.PreProcessKeyEvent (descriptor); } public static void UpdateCursorPosition () { if (!IsVisible) return; - + if (wnd.IsInCompletion) + return; var caretOffset = wnd.CompletionWidget.CaretOffset; if (caretOffset < wnd.StartOffset || caretOffset > wnd.EndOffset) HideWindow (); @@ -164,11 +168,11 @@ namespace MonoDevelop.Ide.CodeCompletion } } - public static void PostProcessKeyEvent (Gdk.Key key, char keyChar, Gdk.ModifierType modifier) + public static void PostProcessKeyEvent (KeyDescriptor descriptor) { if (!IsVisible) return; - wnd.PostProcessKeyEvent (key, keyChar, modifier); + wnd.PostProcessKeyEvent (descriptor); } public static void RepositionWindow () @@ -184,7 +188,7 @@ namespace MonoDevelop.Ide.CodeCompletion return; ParameterInformationWindowManager.UpdateWindow (wnd.Extension, wnd.CompletionWidget); if (wnd.Extension != null) - wnd.Extension.document.Editor.FixVirtualIndentation (); + wnd.Extension.Editor.FixVirtualIndentation (); wnd.HideWindow (); OnWindowClosed (EventArgs.Empty); //DestroyWindow (); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/IRefactoringContext.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/DisplayFlags.cs index c14b46e8fc..0450a0f7d2 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/IRefactoringContext.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/DisplayFlags.cs @@ -1,10 +1,10 @@ // -// IRefactoringContext.cs +// DisplayFlags.cs // // Author: // Mike Krüger <mkrueger@xamarin.com> // -// Copyright (c) 2013 Xamarin Inc. (http://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 @@ -26,16 +26,17 @@ using System; -namespace MonoDevelop.Ide.TypeSystem +namespace MonoDevelop.Ide.CodeCompletion { - /// <summary> - /// Base interface for refactoring contexts - /// </summary> - public interface IRefactoringContext + [Flags] + public enum DisplayFlags { - /// <summary> - /// Creates a refactoring script. - /// </summary> - IDisposable CreateScript (); + None = 0, + Hidden = 1, + Obsolete = 2, + DescriptionHasMarkup = 4, + NamedArgument = 8, + IsImportCompletion = 16, + MarkedBold = 32 } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ICompletionWidget.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ICompletionWidget.cs index 668359506a..1824432bb9 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ICompletionWidget.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ICompletionWidget.cs @@ -31,29 +31,33 @@ using Gtk; namespace MonoDevelop.Ide.CodeCompletion { - public interface ICompletionWidget + interface ICompletionWidget { - CodeCompletionContext CurrentCodeCompletionContext { + CodeCompletionContext CurrentCodeCompletionContext + { get; } - int CaretOffset { get;} + int CaretOffset { get; set; } int TextLength { get; } int SelectedLength { get; } string GetText (int startOffset, int endOffset); - + char GetChar (int offset); - + void Replace (int offset, int count, string text); - + Gtk.Style GtkStyle { get; } + double ZoomLevel { get; } CodeCompletionContext CreateCodeCompletionContext (int triggerOffset); string GetCompletionText (CodeCompletionContext ctx); void SetCompletionText (CodeCompletionContext ctx, string partial_word, string complete_word); - + void SetCompletionText (CodeCompletionContext ctx, string partial_word, string complete_word, int completeWordOffset); - + + void AddSkipChar (int cursorPosition, char c); + event EventHandler CompletionContextChanged; } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ListWidget.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ListWidget.cs index 0b607df034..72367422a9 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ListWidget.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ListWidget.cs @@ -30,12 +30,14 @@ using System.Linq; using Gdk; using Gtk; using Pango; -using ICSharpCode.NRefactory.Completion; -using Mono.TextEditor; -using Mono.TextEditor.Highlighting; +using ICSharpCode.NRefactory6.CSharp.Completion; using MonoDevelop.Components; using MonoDevelop.Ide.Fonts; -using MonoDevelop.Ide.Gui.Content;
+using MonoDevelop.Ide.Gui.Content; +using MonoDevelop.Ide.Editor; +using System.ComponentModel.Design; +using MonoDevelop.Ide.Editor.Highlighting; +using MonoDevelop.Ide.Editor.Extension; using MonoDevelop.Core; namespace MonoDevelop.Ide.CodeCompletion @@ -95,26 +97,27 @@ namespace MonoDevelop.Ide.CodeCompletion public bool CloseOnSquareBrackets { get; set; - }
-
- public readonly static PropertyWrapper<bool> EnableCompletionCategoryMode = PropertyService.Wrap("EnableCompletionCategoryMode", false);
-
+ } + + public readonly static PropertyWrapper<bool> EnableCompletionCategoryMode = PropertyService.Wrap("EnableCompletionCategoryMode", false); + public bool InCategoryMode { - get { return EnableCompletionCategoryMode; } - set { - EnableCompletionCategoryMode.Set(value); + get { return EnableCompletionCategoryMode && categories.Count > 1; } + } - CalcVisibleRows (); - if (value) - SelectFirstItemInCategory (); - } + internal void UpdateCategoryMode () + { + CalcVisibleRows (); + if (InCategoryMode) + SelectFirstItemInCategory (); } + public int CategoryCount { get { return this.categories.Count; } } ICompletionWidget completionWidget; - public ICompletionWidget CompletionWidget { + internal ICompletionWidget CompletionWidget { get { return completionWidget; } @@ -141,14 +144,11 @@ namespace MonoDevelop.Ide.CodeCompletion // TODO: Add font property to ICompletionWidget; if (itemFont != null) itemFont.Dispose (); - itemFont = FontService.GetFontDescription ("Editor").Copy (); - var provider = CompletionWidget as ITextEditorDataProvider; - if (provider != null) { - var newSize = (itemFont.Size * provider.GetTextEditorData ().Options.Zoom); - if (newSize > 0) { - itemFont.Size = (int)newSize; - layout.FontDescription = itemFont; - } + itemFont = FontService.MonospaceFont.Copy (); + var newSize = (itemFont.Size * (completionWidget != null ? this.completionWidget.ZoomLevel : 1)); + if (newSize > 0) { + itemFont.Size = (int)newSize; + layout.FontDescription = itemFont; } } @@ -380,9 +380,17 @@ namespace MonoDevelop.Ide.CodeCompletion public bool SelectionEnabled { get { - return AutoSelect && (AutoCompleteEmptyMatch || !string.IsNullOrEmpty (CompletionString)); + return AutoSelect && (AutoCompleteEmptyMatch || !IsEmptyMatch (CompletionString)); } } + + static bool IsEmptyMatch (string completionString) + { + if (string.IsNullOrEmpty (completionString)) + return true; + var ch = completionString [0]; + return char.IsDigit (ch); + } protected override bool OnButtonPressEvent (EventButton e) { @@ -656,7 +664,7 @@ namespace MonoDevelop.Ide.CodeCompletion }); categories = newCategories; - SelectFirstItemInCategory (); + //SelectFirstItemInCategory (); CalcVisibleRows (); SetAdjustments (); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ListWindow.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ListWindow.cs index 97f12ae487..0a2edce515 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ListWindow.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ListWindow.cs @@ -25,21 +25,20 @@ // // -using Gtk; -using Gdk; -using Pango; using System; -using System.Xml; -using System.Linq; -using System.Text; using System.Collections.Generic; using MonoDevelop.Core.Text; -using ICSharpCode.NRefactory.Completion; -using Mono.TextEditor; +using ICSharpCode.NRefactory6.CSharp.Completion; using MonoDevelop.Ide.Gui.Content; +using System.Linq; +using Gdk; +using Gtk; +using MonoDevelop.Core.Text; using MonoDevelop.Components; -using Mono.TextEditor.Highlighting; -using MonoDevelop.Core; +using MonoDevelop.Ide.Gui.Content; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Ide.Editor.Highlighting; +using MonoDevelop.Ide.Editor.Extension; namespace MonoDevelop.Ide.CodeCompletion { @@ -60,7 +59,9 @@ namespace MonoDevelop.Ide.CodeCompletion ListWidget list; Widget footer; protected VBox vbox; - + internal MruCache cache = new MruCache(); + protected ICompletionDataList completionDataList; + public CompletionTextEditorExtension Extension { get; set; @@ -129,7 +130,7 @@ namespace MonoDevelop.Ide.CodeCompletion /// This method is used to set the completion window to it's inital state. /// This is required for re-using the window object. /// </summary> - protected virtual void ResetState () + protected internal virtual void ResetState () { HideWhenWordDeleted = false; lastCommitCharEndoffset = -1; @@ -226,7 +227,7 @@ namespace MonoDevelop.Ide.CodeCompletion set; } - public ICompletionWidget CompletionWidget { + internal ICompletionWidget CompletionWidget { get { return list.CompletionWidget; } @@ -273,20 +274,21 @@ namespace MonoDevelop.Ide.CodeCompletion private set; } - public KeyActions PostProcessKey (Gdk.Key key, char keyChar, Gdk.ModifierType modifier) + public KeyActions PostProcessKey (KeyDescriptor descriptor) { - if (StartOffset > CompletionWidget.CaretOffset) + if (CompletionWidget == null || StartOffset > CompletionWidget.CaretOffset) // CompletionWidget == null may happen in unit tests. return KeyActions.CloseWindow | KeyActions.Process; if (HideWhenWordDeleted && StartOffset >= CompletionWidget.CaretOffset) return KeyActions.CloseWindow | KeyActions.Process; - switch (key) { - case Gdk.Key.BackSpace: + switch (descriptor.SpecialKey) { + case SpecialKey.BackSpace: ResetSizes (); UpdateWordSelection (); return KeyActions.Process; } - + var keyChar = descriptor.KeyChar; + const string commitChars = " <>()[]{}=+-*/%~&^|!.,;:"; if (keyChar == '[' && CloseOnSquareBrackets) return KeyActions.Process | KeyActions.CloseWindow; @@ -306,13 +308,13 @@ namespace MonoDevelop.Ide.CodeCompletion if (!text.ToUpper ().StartsWith (curword.ToUpper (), StringComparison.Ordinal)) match = -1; } - if (match >= 0 && !hasMismatches && keyChar != '<') { + if (match >= 0 && !hasMismatches && keyChar != '<' && keyChar != ' ') { ResetSizes (); UpdateWordSelection (); return KeyActions.Process; } - if (keyChar == '.') - list.AutoSelect = list.AutoCompleteEmptyMatch = true; +// if (keyChar == '.') +// list.AutoSelect = list.AutoCompleteEmptyMatch = true; lastCommitCharEndoffset = CompletionWidget.CaretOffset - 1; if (list.SelectionEnabled && CompletionCharacters.CompleteOn (keyChar)) { @@ -322,30 +324,47 @@ namespace MonoDevelop.Ide.CodeCompletion } return KeyActions.CloseWindow | KeyActions.Process; } - + + if (char.IsPunctuation (descriptor.KeyChar) && descriptor.KeyChar != '_') { + if (descriptor.KeyChar == ':') { + foreach (var item in FilteredItems) { + if (DataProvider.GetText (item).EndsWith (descriptor.KeyChar.ToString (), StringComparison.Ordinal)) { + list.SelectedItem = item; + return KeyActions.Complete | KeyActions.CloseWindow | KeyActions.Ignore; + } + } + } else { + var selectedItem = list.SelectedItem; + if (selectedItem < 0 || selectedItem >= DataProvider.ItemCount) + return KeyActions.CloseWindow; + if (!DataProvider.GetText (selectedItem).Substring (0, CurrentPartialWord.Length) .EndsWith (descriptor.KeyChar.ToString (), StringComparison.Ordinal)) + return KeyActions.Process | KeyActions.CloseWindow; + } + } return KeyActions.Process; } - public KeyActions PreProcessKey (Gdk.Key key, char keyChar, Gdk.ModifierType modifier) + public KeyActions PreProcessKey (KeyDescriptor descriptor) { - switch (key) { - case Gdk.Key.Home: - if ((modifier & ModifierType.ShiftMask) == ModifierType.ShiftMask) + switch (descriptor.SpecialKey) { + case SpecialKey.Home: + if ((descriptor.ModifierKeys & ModifierKeys.Shift) == ModifierKeys.Shift) return KeyActions.Process; List.SelectionFilterIndex = 0; return KeyActions.Ignore; - case Gdk.Key.End: - if ((modifier & ModifierType.ShiftMask) == ModifierType.ShiftMask) + case SpecialKey.End: + if ((descriptor.ModifierKeys & ModifierKeys.Shift) == ModifierKeys.Shift) return KeyActions.Process; List.SelectionFilterIndex = List.filteredItems.Count - 1; return KeyActions.Ignore; - case Gdk.Key.Up: - if ((modifier & Gdk.ModifierType.ShiftMask) == Gdk.ModifierType.ShiftMask) { + case SpecialKey.Up: + if ((descriptor.ModifierKeys & ModifierKeys.Shift) == ModifierKeys.Shift) { if (!SelectionEnabled /*&& !CompletionWindowManager.ForceSuggestionMode*/) AutoCompleteEmptyMatch = AutoSelect = true; if (!List.InCategoryMode) { - List.InCategoryMode = true; + ListWidget.EnableCompletionCategoryMode.Set (true); + List.UpdateCategoryMode (); return KeyActions.Ignore; } List.MoveToCategory (-1); @@ -360,25 +379,23 @@ namespace MonoDevelop.Ide.CodeCompletion } return KeyActions.Ignore; - case Gdk.Key.Tab: + case SpecialKey.Tab: //tab always completes current item even if selection is disabled if (!AutoSelect) AutoSelect = true; - goto case Gdk.Key.Return; + goto case SpecialKey.Return; - case Gdk.Key.Return: - case Gdk.Key.ISO_Enter: - case Gdk.Key.Key_3270_Enter: - case Gdk.Key.KP_Enter: + case SpecialKey.Return: lastCommitCharEndoffset = CompletionWidget.CaretOffset; - WasShiftPressed = (modifier & ModifierType.ShiftMask) == ModifierType.ShiftMask; + WasShiftPressed = (descriptor.ModifierKeys & ModifierKeys.Shift) == ModifierKeys.Shift; return KeyActions.Complete | KeyActions.Ignore | KeyActions.CloseWindow; - case Gdk.Key.Down: - if ((modifier & Gdk.ModifierType.ShiftMask) == Gdk.ModifierType.ShiftMask) { + case SpecialKey.Down: + if ((descriptor.ModifierKeys & ModifierKeys.Shift) == ModifierKeys.Shift) { if (!SelectionEnabled /*&& !CompletionWindowManager.ForceSuggestionMode*/) AutoCompleteEmptyMatch = AutoSelect = true; if (!List.InCategoryMode) { - List.InCategoryMode = true; + ListWidget.EnableCompletionCategoryMode.Set (true); + List.UpdateCategoryMode (); return KeyActions.Ignore; } List.MoveToCategory (1); @@ -394,8 +411,8 @@ namespace MonoDevelop.Ide.CodeCompletion } return KeyActions.Ignore; - case Gdk.Key.Page_Up: - if ((modifier & ModifierType.ShiftMask) == ModifierType.ShiftMask) + case SpecialKey.PageUp: + if ((descriptor.ModifierKeys & ModifierKeys.Shift) == ModifierKeys.Shift) return KeyActions.Process; if (list.filteredItems.Count < 2) return KeyActions.CloseWindow | KeyActions.Process; @@ -403,8 +420,8 @@ namespace MonoDevelop.Ide.CodeCompletion list.MoveCursor (-8); return KeyActions.Ignore; - case Gdk.Key.Page_Down: - if ((modifier & ModifierType.ShiftMask) == ModifierType.ShiftMask) + case SpecialKey.PageDown: + if ((descriptor.ModifierKeys & ModifierKeys.Shift) == ModifierKeys.Shift) return KeyActions.Process; if (list.filteredItems.Count < 2) return KeyActions.CloseWindow | KeyActions.Process; @@ -412,42 +429,42 @@ namespace MonoDevelop.Ide.CodeCompletion list.MoveCursor (8); return KeyActions.Ignore; - case Gdk.Key.Left: + case SpecialKey.Left: //if (curPos == 0) return KeyActions.CloseWindow | KeyActions.Process; //curPos--; return KeyActions.Process; - case Gdk.Key.Right: + case SpecialKey.Right: //if (curPos == word.Length) return KeyActions.CloseWindow | KeyActions.Process; //curPos++; return KeyActions.Process; - case Gdk.Key.Caps_Lock: - case Gdk.Key.Num_Lock: - case Gdk.Key.Scroll_Lock: - return KeyActions.Ignore; - - case Gdk.Key.Control_L: - case Gdk.Key.Control_R: - case Gdk.Key.Alt_L: - case Gdk.Key.Alt_R: - case Gdk.Key.Shift_L: - case Gdk.Key.Shift_R: - case Gdk.Key.ISO_Level3_Shift: - // AltGr - return KeyActions.Process; +// case Gdk.Key.Caps_Lock: +// case Gdk.Key.Num_Lock: +// case Gdk.Key.Scroll_Lock: +// return KeyActions.Ignore; +// +// case Gdk.Key.Control_L: +// case Gdk.Key.Control_R: +// case Gdk.Key.Alt_L: +// case Gdk.Key.Alt_R: +// case Gdk.Key.Shift_L: +// case Gdk.Key.Shift_R: +// case Gdk.Key.ISO_Level3_Shift: +// // AltGr +// return KeyActions.Process; } - if (keyChar == '\0') + if (descriptor.KeyChar == '\0') return KeyActions.Process; - if (keyChar == ' ' && (modifier & ModifierType.ShiftMask) == ModifierType.ShiftMask) + if (descriptor.KeyChar == ' ' && (descriptor.ModifierKeys & ModifierKeys.Shift) == ModifierKeys.Shift) return KeyActions.CloseWindow | KeyActions.Process; // special case end with punctuation like 'param:' -> don't input double punctuation, otherwise we would end up with 'param::' - if (char.IsPunctuation (keyChar) && keyChar != '_') { - if (keyChar == ':') { + if (char.IsPunctuation (descriptor.KeyChar) && descriptor.KeyChar != '_') { + if (descriptor.KeyChar == ':') { foreach (var item in FilteredItems) { - if (DataProvider.GetText (item).EndsWith (keyChar.ToString (), StringComparison.Ordinal)) { + if (DataProvider.GetText (item).EndsWith (descriptor.KeyChar.ToString (), StringComparison.Ordinal)) { list.SelectedItem = item; return KeyActions.Complete | KeyActions.CloseWindow | KeyActions.Ignore; } @@ -456,14 +473,12 @@ namespace MonoDevelop.Ide.CodeCompletion var selectedItem = list.SelectedItem; if (selectedItem < 0 || selectedItem >= DataProvider.ItemCount) return KeyActions.CloseWindow; - if (DataProvider.GetText (selectedItem).EndsWith (keyChar.ToString (), StringComparison.Ordinal)) { + if (DataProvider.GetText (selectedItem).EndsWith (descriptor.KeyChar.ToString (), StringComparison.Ordinal)) { return KeyActions.Complete | KeyActions.CloseWindow | KeyActions.Ignore; } } } - - /* //don't input letters/punctuation etc when non-shift modifiers are active bool nonShiftModifierActive = ((Gdk.ModifierType.ControlMask | Gdk.ModifierType.MetaMask | Gdk.ModifierType.Mod1Mask | Gdk.ModifierType.SuperMask) @@ -544,75 +559,69 @@ namespace MonoDevelop.Ide.CodeCompletion { // default - word with highest match rating in the list. hasMismatches = true; - if (partialWord == null) - return -1; - int idx = -1; - var matcher = CompletionMatcher.CreateCompletionMatcher (partialWord); - string bestWord = null; - int bestRank = int.MinValue; - int bestIndex = 0; + + StringMatcher matcher = null; if (!string.IsNullOrEmpty (partialWord)) { + matcher = CompletionMatcher.CreateCompletionMatcher (partialWord); + string bestWord = null; + int bestRank = int.MinValue; + int bestIndex = 0; + int bestIndexPriority = int.MinValue; for (int i = 0; i < list.filteredItems.Count; i++) { - int index = list.filteredItems[i]; - string text = DataProvider.GetText (index); + int index = list.filteredItems [i]; + var data = DataProvider.GetCompletionData (index); + if (bestIndexPriority > data.PriorityGroup) + continue; + string text = data.DisplayText; int rank; if (!matcher.CalcMatchRank (text, out rank)) continue; - if (rank > bestRank) { + if (rank > bestRank || data.PriorityGroup > bestIndexPriority) { bestWord = text; bestRank = rank; bestIndex = i; + bestIndexPriority = data.PriorityGroup; } } + + if (bestWord != null) { + idx = bestIndex; + hasMismatches = false; + // exact match found. + if (string.Compare (bestWord, partialWord ?? "", true) == 0) + return idx; + } } - if (bestWord != null) { - idx = bestIndex; - hasMismatches = false; - // exact match found. - if (string.Compare (bestWord, partialWord ?? "", true) == 0) - return idx; + + CompletionData currentData; + int bestMruIndex; + if (idx >= 0) { + currentData = completionDataList [list.filteredItems [idx]]; + bestMruIndex = cache.GetIndex (currentData); + } else { + bestMruIndex = int.MaxValue; + currentData = null; } - - if (string.IsNullOrEmpty (partialWord) || partialWord.Length <= 2) { - // Search for history matches. - string historyWord; - if (wordHistory.TryGetValue (partialWord, out historyWord)) { - for (int xIndex = 0; xIndex < list.filteredItems.Count; xIndex++) { - string currentWord = DataProvider.GetCompletionText (list.filteredItems[xIndex]); - if (currentWord == historyWord) { - idx = xIndex; - break; + + for (int i = 0; i < list.filteredItems.Count; i++) { + var mruData = completionDataList [list.filteredItems [i]]; + int curMruIndex = cache.GetIndex (mruData); + if (curMruIndex == 1) + continue; + if (curMruIndex < bestMruIndex) { + int r1 = 0, r2 = 0; + if (currentData == null || matcher != null && matcher.CalcMatchRank (mruData.DisplayText, out r1) && matcher.CalcMatchRank (currentData.DisplayText, out r2)) { + if (r1 >= r2) { + bestMruIndex = curMruIndex; + idx = i; + currentData = mruData; } } } } - return idx; - } - static Dictionary<string,string> wordHistory = new Dictionary<string,string> (); - static List<string> partalWordHistory = new List<string> (); - const int maxHistoryLength = 500; - protected void AddWordToHistory (string partialWord, string word) - { - if (!wordHistory.ContainsKey (partialWord)) { - wordHistory.Add (partialWord, word); - partalWordHistory.Add (partialWord); - while (partalWordHistory.Count > maxHistoryLength) { - string first = partalWordHistory [0]; - partalWordHistory.RemoveAt (0); - wordHistory.Remove (first); - } - } else { - partalWordHistory.Remove (partialWord); - partalWordHistory.Add (partialWord); - wordHistory [partialWord] = word; - } - } - public static void ClearHistory () - { - wordHistory.Clear (); - partalWordHistory.Clear (); + return idx; } void SelectEntry (int n) @@ -698,6 +707,7 @@ namespace MonoDevelop.Ide.CodeCompletion CompletionCategory GetCompletionCategory (int n); bool HasMarkup (int n); string GetCompletionText (int n); + CompletionData GetCompletionData (int n); string GetDescription (int n, bool isSelected); string GetRightSideDescription (int n, bool isSelected); Xwt.Drawing.Image GetIcon (int n); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/MemberCompletionData.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/MemberCompletionData.cs index f5c3cdc31e..1d33b4ee7b 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/MemberCompletionData.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/MemberCompletionData.cs @@ -25,7 +25,6 @@ // THE SOFTWARE. using System; -using ICSharpCode.NRefactory.TypeSystem; namespace MonoDevelop.Ide.CodeCompletion { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/MruCache.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/MruCache.cs new file mode 100644 index 0000000000..90e679af3f --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/MruCache.cs @@ -0,0 +1,67 @@ +// +// MruCache.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.CodeCompletion +{ + class MruCache + { + const int MaxItems = 42; + + readonly List<string> lastItems = new List<string> (MaxItems); + readonly object mruLock = new object (); + + public void CommitCompletionData (CompletionData item) + { + lock (mruLock) { + var removed = lastItems.Remove (item.DisplayText); + if (!removed && lastItems.Count == MaxItems) + lastItems.RemoveAt (0); + + lastItems.Add (item.DisplayText); + } + } + + /// <summary> + /// Lower is better. 1 == not in list. + /// </summary> + public int GetIndex (CompletionData item) + { + lock (mruLock) { + var index = lastItems.IndexOf (item.DisplayText); + return -index; + } + } + + public void Clear () + { + lock (mruLock) { + lastItems.Clear (); + } + } + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/MutableCompletionDataList.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/MutableCompletionDataList.cs index 871134a1c8..69488e2d04 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/MutableCompletionDataList.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/MutableCompletionDataList.cs @@ -61,32 +61,24 @@ namespace MonoDevelop.Ide.CodeCompletion public event EventHandler Changing { add { - if (changing == null) - TypeSystemService.ParseOperationStarted += HandleParseOperationStarted; changing += value; } remove { changing -= value; - if (changing == null) - TypeSystemService.ParseOperationStarted -= HandleParseOperationStarted; } } public event EventHandler Changed { add { - if (changed == null) - TypeSystemService.ParseOperationFinished += HandleParseOperationFinished; changed += value; } remove { changed -= value; - if (changed == null) - TypeSystemService.ParseOperationFinished -= HandleParseOperationFinished; } } public bool IsChanging { - get { return TypeSystemService.IsParsing; } + get { return false; } } protected virtual void OnChanging () @@ -117,10 +109,6 @@ namespace MonoDevelop.Ide.CodeCompletion { if (!disposed) { disposed = true; - if (changing != null) - TypeSystemService.ParseOperationStarted -= HandleParseOperationStarted; - if (changed != null) - TypeSystemService.ParseOperationFinished -= HandleParseOperationFinished; } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ParameterDataProvider.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ParameterHintingData.cs index 3d1b0811b9..aa2077c177 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ParameterDataProvider.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ParameterHintingData.cs @@ -25,54 +25,43 @@ // THE SOFTWARE. using System; using ICSharpCode.NRefactory.Completion; +using ICSharpCode.NRefactory6.CSharp.Completion; +using Microsoft.CodeAnalysis; +using System.Collections.Immutable; +using System.Linq; +using System.Collections.Generic; +using System.Threading; +using MonoDevelop.Ide.TypeSystem; +using MonoDevelop.Projects; +using MonoDevelop.Ide.Editor; namespace MonoDevelop.Ide.CodeCompletion { - public abstract class ParameterDataProvider : IParameterDataProvider + public abstract class ParameterHintingData { - public ParameterDataProvider (int startOffset) - { - this.startOffset = startOffset; - } - - public virtual TooltipInformation CreateTooltipInformation (int overload, int currentParameter, bool smartWrap) - { - return new TooltipInformation (); - } - - #region IParameterDataProvider implementation - - string IParameterDataProvider.GetHeading (int overload, string[] parameterDescription, int currentParameter) - { - throw new NotImplementedException (); + public ISymbol Symbol { + get; + private set; } - string IParameterDataProvider.GetDescription (int overload, int currentParameter) + protected ParameterHintingData (ISymbol symbol) { - throw new NotImplementedException (); + Symbol = symbol; } - string IParameterDataProvider.GetParameterDescription (int overload, int paramIndex) - { - throw new NotImplementedException (); + public abstract int ParameterCount { + get; } - public abstract int GetParameterCount (int overload); - public abstract bool AllowParameterList (int overload); - public abstract string GetParameterName (int overload, int paramIndex); - - public abstract int Count { + public abstract bool IsParameterListAllowed { get; } - readonly int startOffset; - public int StartOffset { - get { - return startOffset; - } - } + public abstract string GetParameterName (int parameter); - #endregion + public virtual TooltipInformation CreateTooltipInformation (TextEditor editor, DocumentContext ctx, int currentParameter, bool smartWrap) + { + return new TooltipInformation (); + } } } - diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ParameterHintingResult.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ParameterHintingResult.cs new file mode 100644 index 0000000000..dba4aedf0c --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ParameterHintingResult.cs @@ -0,0 +1,88 @@ +// +// ParameterHintingResult.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.CodeCompletion +{ + public class ParameterHintingResult : IReadOnlyList<ParameterHintingData> + { + public static readonly ParameterHintingResult Empty = new ParameterHintingResult (new List<ParameterHintingData> (), -1); + + protected readonly List<ParameterHintingData> data; + /// <summary> + /// Gets the start offset of the parameter expression node. + /// </summary> + public int StartOffset { + get; + private set; + } + + protected ParameterHintingResult (int startOffset) + { + this.data = new List<ParameterHintingData> (); + this.StartOffset = startOffset; + } + + public ParameterHintingResult (List<ParameterHintingData> data, int startOffset) + { + this.data = data; + this.StartOffset = startOffset; + } + + #region IEnumerable implementation + public IEnumerator<ParameterHintingData> GetEnumerator () + { + return data.GetEnumerator (); + } + #endregion + + #region IEnumerable implementation + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator () + { + return data.GetEnumerator (); + } + #endregion + + #region IReadOnlyList implementation + public ParameterHintingData this [int index] { + get { + return data [index]; + } + } + #endregion + + #region IReadOnlyCollection implementation + public int Count { + get { + return data.Count; + } + } + #endregion + } + +} + diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ParameterInformationWindow.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ParameterInformationWindow.cs index 14c2042e07..40278f4734 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ParameterInformationWindow.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ParameterInformationWindow.cs @@ -25,16 +25,15 @@ // // - using System; -using System.Text; using MonoDevelop.Core; using Gtk; using MonoDevelop.Components; -using ICSharpCode.NRefactory.Completion; using MonoDevelop.Ide.Gui.Content; -using System.Collections.Generic; using MonoDevelop.Ide.Fonts; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Ide.Editor.Highlighting; +using MonoDevelop.Ide.Editor.Extension; namespace MonoDevelop.Ide.CodeCompletion { @@ -74,7 +73,7 @@ namespace MonoDevelop.Ide.CodeCompletion this.AllowGrow = false; this.CanFocus = false; this.CanDefault = false; - Mono.TextEditor.PopupWindow.WindowTransparencyDecorator.Attach (this); + WindowTransparencyDecorator.Attach (this); headlabel = new MonoDevelop.Components.FixedWidthWrapLabel (); headlabel.Indent = -20; @@ -96,7 +95,7 @@ namespace MonoDevelop.Ide.CodeCompletion vb2.Spacing = 4; vb2.PackStart (hb, true, true, 0); ContentBox.Add (vb2); - var scheme = Mono.TextEditor.Highlighting.SyntaxModeService.GetColorStyle (IdeApp.Preferences.ColorScheme); + var scheme = SyntaxModeService.GetColorStyle (IdeApp.Preferences.ColorScheme); Theme.SetSchemeColors (scheme); foreColor = scheme.PlainText.Foreground; @@ -107,22 +106,24 @@ namespace MonoDevelop.Ide.CodeCompletion } int lastParam = -2; - public void ShowParameterInfo (ParameterDataProvider provider, int overload, int _currentParam, int maxSize) + TooltipInformation currentTooltipInformation; + + public void ShowParameterInfo (ParameterHintingResult provider, int overload, int _currentParam, int maxSize) { if (provider == null) throw new ArgumentNullException ("provider"); - int numParams = System.Math.Max (0, provider.GetParameterCount (overload)); + int numParams = System.Math.Max (0, provider[overload].ParameterCount); var currentParam = System.Math.Min (_currentParam, numParams - 1); if (numParams > 0 && currentParam < 0) currentParam = 0; - if (lastParam == currentParam) { + if (lastParam == currentParam && (currentTooltipInformation != null)) { return; } lastParam = currentParam; ClearDescriptions (); - var o = provider.CreateTooltipInformation (overload, currentParam, false); - + var parameterHintingData = (ParameterHintingData)provider [overload]; + currentTooltipInformation = parameterHintingData.CreateTooltipInformation (ext.Editor, ext.DocumentContext, currentParam, false); Theme.NumPages = provider.Count; Theme.CurrentPage = overload; if (provider.Count > 1) { @@ -130,17 +131,17 @@ namespace MonoDevelop.Ide.CodeCompletion Theme.PagerVertical = true; } - headlabel.Markup = o.SignatureMarkup; + headlabel.Markup = currentTooltipInformation.SignatureMarkup; headlabel.Visible = true; if (Theme.DrawPager) headlabel.WidthRequest = headlabel.RealWidth + 70; - foreach (var cat in o.Categories) { + foreach (var cat in currentTooltipInformation.Categories) { descriptionBox.PackStart (CreateCategory (cat.Item1, cat.Item2), true, true, 4); } - if (!string.IsNullOrEmpty (o.SummaryMarkup)) { - descriptionBox.PackStart (CreateCategory (GettextCatalog.GetString ("Summary"), o.SummaryMarkup), true, true, 4); + if (!string.IsNullOrEmpty (currentTooltipInformation.SummaryMarkup)) { + descriptionBox.PackStart (CreateCategory (GettextCatalog.GetString ("Summary"), currentTooltipInformation.SummaryMarkup), true, true, 4); } descriptionBox.ShowAll (); QueueResize (); @@ -185,6 +186,7 @@ namespace MonoDevelop.Ide.CodeCompletion public void ChangeOverload () { lastParam = -2; + currentTooltipInformation = null; } public void HideParameterInfo () diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ParameterInformationWindowManager.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ParameterInformationWindowManager.cs index 64c53b4f5f..f3de5195a4 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ParameterInformationWindowManager.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ParameterInformationWindowManager.cs @@ -31,8 +31,8 @@ using System.Collections; using System.Collections.Generic; using Gtk; using Gdk; -using ICSharpCode.NRefactory.Completion; using MonoDevelop.Ide.Gui.Content; +using MonoDevelop.Ide.Editor.Extension; namespace MonoDevelop.Ide.CodeCompletion { @@ -47,7 +47,8 @@ namespace MonoDevelop.Ide.CodeCompletion static ParameterInformationWindowManager () { - IdeApp.Workbench.RootWindow.Destroyed += (sender, e) => DestroyWindow (); + if (IdeApp.Workbench != null) + IdeApp.Workbench.RootWindow.Destroyed += (sender, e) => DestroyWindow (); } static void DestroyWindow () @@ -60,14 +61,14 @@ namespace MonoDevelop.Ide.CodeCompletion // Called when a key is pressed in the editor. // Returns false if the key press has to continue normal processing. - public static bool ProcessKeyEvent (CompletionTextEditorExtension ext, ICompletionWidget widget, Gdk.Key key, Gdk.ModifierType modifier) + internal static bool ProcessKeyEvent (CompletionTextEditorExtension ext, ICompletionWidget widget, KeyDescriptor descriptor) { if (methods.Count == 0) return false; MethodData cmd = methods [methods.Count - 1]; - - if (key == Gdk.Key.Down) { + + if (descriptor.SpecialKey == SpecialKey.Down) { if (cmd.MethodProvider.Count <= 1) return false; if (cmd.CurrentOverload < cmd.MethodProvider.Count - 1) @@ -77,7 +78,7 @@ namespace MonoDevelop.Ide.CodeCompletion window.ChangeOverload (); UpdateWindow (ext, widget); return true; - } else if (key == Gdk.Key.Up) { + } else if (descriptor.SpecialKey == SpecialKey.Up) { if (cmd.MethodProvider.Count <= 1) return false; if (cmd.CurrentOverload > 0) @@ -88,18 +89,18 @@ namespace MonoDevelop.Ide.CodeCompletion UpdateWindow (ext, widget); return true; } - else if (key == Gdk.Key.Escape) { + else if (descriptor.SpecialKey == SpecialKey.Escape) { HideWindow (ext, widget); return true; } return false; } - public static void PostProcessKeyEvent (CompletionTextEditorExtension ext, ICompletionWidget widget, Gdk.Key key, Gdk.ModifierType modifier) + internal static void PostProcessKeyEvent (CompletionTextEditorExtension ext, ICompletionWidget widget, KeyDescriptor descriptor) { } - public static void UpdateCursorPosition (CompletionTextEditorExtension ext, ICompletionWidget widget) + internal static void UpdateCursorPosition (CompletionTextEditorExtension ext, ICompletionWidget widget) { // Called after the key has been processed by the editor if (methods.Count == 0) @@ -124,12 +125,12 @@ namespace MonoDevelop.Ide.CodeCompletion UpdateWindow (ext, widget); } - public static void RepositionWindow (CompletionTextEditorExtension ext, ICompletionWidget widget) + internal static void RepositionWindow (CompletionTextEditorExtension ext, ICompletionWidget widget) { UpdateWindow (ext, widget); } - public static void ShowWindow (CompletionTextEditorExtension ext, ICompletionWidget widget, CodeCompletionContext ctx, ParameterDataProvider provider) + internal static void ShowWindow (CompletionTextEditorExtension ext, ICompletionWidget widget, CodeCompletionContext ctx, ParameterHintingResult provider) { if (provider.Count == 0) return; @@ -147,7 +148,7 @@ namespace MonoDevelop.Ide.CodeCompletion UpdateWindow (ext, widget); } - public static void HideWindow (CompletionTextEditorExtension ext, ICompletionWidget widget) + internal static void HideWindow (CompletionTextEditorExtension ext, ICompletionWidget widget) { methods.Clear (); if (window != null) @@ -162,7 +163,7 @@ namespace MonoDevelop.Ide.CodeCompletion return methods [methods.Count - 1].CurrentOverload; } - public static IParameterDataProvider GetCurrentProvider () + public static ParameterHintingResult GetCurrentProvider () { if (methods.Count == 0) return null; @@ -284,7 +285,7 @@ namespace MonoDevelop.Ide.CodeCompletion class MethodData { - public ParameterDataProvider MethodProvider; + public ParameterHintingResult MethodProvider; public CodeCompletionContext CompletionContext; int currentOverload; public int CurrentOverload { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/TooltipInformationWindow.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/TooltipInformationWindow.cs index d9847fb2b9..528bb4a685 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/TooltipInformationWindow.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/TooltipInformationWindow.cs @@ -30,7 +30,8 @@ using System.Collections.Generic; using MonoDevelop.Core; using MonoDevelop.Ide.Fonts; using System.Linq; -using Mono.TextEditor.PopupWindow; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Ide.Editor.Highlighting; namespace MonoDevelop.Ide.CodeCompletion { @@ -225,7 +226,7 @@ namespace MonoDevelop.Ide.CodeCompletion internal void SetDefaultScheme () { - var scheme = Mono.TextEditor.Highlighting.SyntaxModeService.GetColorStyle (IdeApp.Preferences.ColorScheme); + var scheme = SyntaxModeService.GetColorStyle (IdeApp.Preferences.ColorScheme); Theme.SetSchemeColors (scheme); foreColor = scheme.PlainText.Foreground; headLabel.ModifyFg (StateType.Normal, foreColor.ToGdkColor ()); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeFormatting/CodeFormatter.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeFormatting/CodeFormatter.cs index 6160c8b056..450441cb1a 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeFormatting/CodeFormatter.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeFormatting/CodeFormatter.cs @@ -26,12 +26,9 @@ using System; using MonoDevelop.Projects.Policies; -using Mono.TextEditor; using System.Collections.Generic; -using ICSharpCode.NRefactory; -using ICSharpCode.NRefactory.TypeSystem; -using ICSharpCode.NRefactory.Semantics; using MonoDevelop.Core; +using MonoDevelop.Ide.Editor; namespace MonoDevelop.Ide.CodeFormatting { @@ -96,16 +93,24 @@ namespace MonoDevelop.Ide.CodeFormatting /// <summary> /// Formats a text document directly with insert/remove operations. /// </summary> - public void OnTheFlyFormat (MonoDevelop.Ide.Gui.Document doc, int startOffset, int endOffset) + public void OnTheFlyFormat (TextEditor editor, DocumentContext documentContext, int startOffset, int endOffset) { var adv = formatter as IAdvancedCodeFormatter; if (adv == null || !adv.SupportsOnTheFlyFormatting) throw new InvalidOperationException ("On the fly formatting not supported"); - adv.OnTheFlyFormat (doc, startOffset, endOffset); + adv.OnTheFlyFormat (editor, documentContext, startOffset, endOffset); } - - public void CorrectIndenting (PolicyContainer policyParent, TextEditorData data, int line) + + /// <summary> + /// Formats a text document directly with insert/remove operations. + /// </summary> + public void OnTheFlyFormat (MonoDevelop.Ide.Gui.Document doc, int startOffset, int endOffset) + { + OnTheFlyFormat (doc.Editor, doc, startOffset, endOffset); + } + + public void CorrectIndenting (PolicyContainer policyParent, TextEditor data, int line) { var adv = formatter as IAdvancedCodeFormatter; if (adv == null || !adv.SupportsCorrectingIndent) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeFormatting/CodeFormatterService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeFormatting/CodeFormatterService.cs index 834881f688..aa483788ef 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeFormatting/CodeFormatterService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeFormatting/CodeFormatterService.cs @@ -29,6 +29,8 @@ using System.Linq; using System.Collections.Generic; using Mono.Addins; using MonoDevelop.Projects.Policies; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Core.Text; namespace MonoDevelop.Ide.CodeFormatting { @@ -68,5 +70,23 @@ namespace MonoDevelop.Ide.CodeFormatting return null; } + + public static void Format (TextEditor editor, DocumentContext ctx, ISegment segment) + { + if (editor == null) + throw new ArgumentNullException ("editor"); + if (ctx == null) + throw new ArgumentNullException ("ctx"); + if (segment == null) + throw new ArgumentNullException ("segment"); + var fmt = GetFormatter (editor.MimeType); + if (fmt == null) + return; + if (fmt.SupportsOnTheFlyFormatting) { + fmt.OnTheFlyFormat (editor, ctx, segment.Offset, segment.EndOffset); + return; + } + editor.Text = fmt.FormatText (ctx.HasProject ? ctx.Project.Policies : null, editor.Text, segment.Offset, segment.EndOffset); + } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeFormatting/CodeFormattingCommands.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeFormatting/CodeFormattingCommands.cs index 4ff1fd707a..507f74250a 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeFormatting/CodeFormattingCommands.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeFormatting/CodeFormattingCommands.cs @@ -28,7 +28,7 @@ using System; using MonoDevelop.Components.Commands; using MonoDevelop.Ide.Gui; using MonoDevelop.Core; -using Mono.TextEditor; +using MonoDevelop.Core.Text; namespace MonoDevelop.Ide.CodeFormatting { @@ -66,7 +66,7 @@ namespace MonoDevelop.Ide.CodeFormatting if (formatter.SupportsOnTheFlyFormatting) { using (var undo = doc.Editor.OpenUndoGroup ()) { - formatter.OnTheFlyFormat (doc, 0, doc.Editor.Length); + formatter.OnTheFlyFormat (doc.Editor, doc, 0, doc.Editor.Length); } } else { var text = doc.Editor.Text; @@ -74,9 +74,8 @@ namespace MonoDevelop.Ide.CodeFormatting if (formattedText == null || formattedText == text) return; - doc.Editor.Replace (0, text.Length, formattedText); + doc.Editor.ReplaceText (0, text.Length, formattedText); } - doc.Editor.Document.CommitUpdateAll (); } } @@ -96,26 +95,26 @@ namespace MonoDevelop.Ide.CodeFormatting if (formatter == null) return; - TextSegment selection; + ISegment selection; var editor = doc.Editor; if (editor.IsSomethingSelected) { selection = editor.SelectionRange; } else { - selection = editor.GetLine (editor.Caret.Line).Segment; + selection = editor.GetLine (editor.CaretLocation.Line); } using (var undo = editor.OpenUndoGroup ()) { var version = editor.Version; if (formatter.SupportsOnTheFlyFormatting) { - formatter.OnTheFlyFormat (doc, selection.Offset, selection.EndOffset); + formatter.OnTheFlyFormat (doc.Editor, doc, selection.Offset, selection.EndOffset); } else { var pol = doc.Project != null ? doc.Project.Policies : null; try { var editorText = editor.Text; string text = formatter.FormatText (pol, editorText, selection.Offset, selection.EndOffset); if (text != null && editorText.Substring (selection.Offset, selection.Length) != text) { - editor.Replace (selection.Offset, selection.Length, text); + editor.ReplaceText (selection.Offset, selection.Length, text); } } catch (Exception e) { LoggingService.LogError ("Error during format.", e); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeFormatting/IAdvancedCodeFormatter.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeFormatting/IAdvancedCodeFormatter.cs index 3b5e4321af..42e9cebec8 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeFormatting/IAdvancedCodeFormatter.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeFormatting/IAdvancedCodeFormatter.cs @@ -26,11 +26,9 @@ using System; using System.Collections.Generic; -using Mono.TextEditor; using MonoDevelop.Projects.Policies; -using ICSharpCode.NRefactory; -using ICSharpCode.NRefactory.TypeSystem; -using ICSharpCode.NRefactory.Semantics; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Ide.Gui; namespace MonoDevelop.Ide.CodeFormatting { @@ -39,10 +37,21 @@ namespace MonoDevelop.Ide.CodeFormatting bool SupportsOnTheFlyFormatting { get; } bool SupportsCorrectingIndent { get; } - void CorrectIndenting (PolicyContainer policyParent, IEnumerable<string> mimeTypeChain, TextEditorData textEditorData, int line); - - void OnTheFlyFormat (MonoDevelop.Ide.Gui.Document doc, int startOffset, int endOffset); + void CorrectIndenting (PolicyContainer policyParent, IEnumerable<string> mimeTypeChain, TextEditor textEditorData, int line); + void OnTheFlyFormat (TextEditor editor, DocumentContext context, int startOffset, int endOffset); + } + + public static class AdvancedCodeFormatterExtensions + { + public static void OnTheFlyFormat (this IAdvancedCodeFormatter formatter, Document document, int startOffset, int endOffset) + { + if (formatter == null) + throw new ArgumentNullException ("formatter"); + formatter.OnTheFlyFormat (document.Editor, document, startOffset, endOffset); + } + + } public abstract class AbstractAdvancedFormatter : AbstractCodeFormatter, IAdvancedCodeFormatter @@ -50,13 +59,18 @@ namespace MonoDevelop.Ide.CodeFormatting public virtual bool SupportsOnTheFlyFormatting { get { return false; } } public virtual bool SupportsCorrectingIndent { get { return false; } } - public virtual void OnTheFlyFormat (MonoDevelop.Ide.Gui.Document doc, int startOffset, int endOffset) + public virtual void OnTheFlyFormat (TextEditor editor, DocumentContext context, int startOffset, int endOffset) { throw new NotSupportedException (); } - + + public void OnTheFlyFormat (Document doc, int startOffset, int endOffset) + { + OnTheFlyFormat (doc.Editor, doc, startOffset, endOffset); + } + public virtual void CorrectIndenting (PolicyContainer policyParent, IEnumerable<string> mimeTypeChain, - TextEditorData data, int line) + TextEditor data, int line) { throw new NotSupportedException (); } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplate.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplate.cs index cd4b4c4c64..726c3ae0d3 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplate.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplate.cs @@ -33,9 +33,11 @@ using System.Text; using System.Text.RegularExpressions; using System.Xml; using MonoDevelop.Core; -using Mono.TextEditor; -using Mono.TextEditor.PopupWindow; using MonoDevelop.Ide.CodeFormatting; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Core.Text; +using System.Linq; +using MonoDevelop.Ide.Gui; namespace MonoDevelop.Ide.CodeTemplates { @@ -134,34 +136,34 @@ namespace MonoDevelop.Ide.CodeTemplates return string.Format("[CodeTemplate: Group={0}, Shortcut={1}, CodeTemplateType={2}, MimeType={3}, Description={4}, Code={5}]", Group, Shortcut, CodeTemplateType, MimeType, Description, Code); } - static int FindPrevWordStart (TextEditorData editor, int offset) + static int FindPrevWordStart (TextEditor editor, int offset) { while (--offset >= 0 && !Char.IsWhiteSpace (editor.GetCharAt (offset))) ; return ++offset; } - public static string GetWordBeforeCaret (TextEditorData editor) + public static string GetWordBeforeCaret (TextEditor editor) { - int offset = editor.Caret.Offset; + int offset = editor.CaretOffset; int start = FindPrevWordStart (editor, offset); return editor.GetTextBetween (start, offset); } - static int DeleteWordBeforeCaret (TextEditorData editor) + static int DeleteWordBeforeCaret (TextEditor editor) { - int offset = editor.Caret.Offset; + int offset = editor.CaretOffset; int start = FindPrevWordStart (editor, offset); - editor.Remove (start, offset - start); + editor.RemoveText (start, offset - start); return start; } - static Regex variableRegEx = new Regex ("\\$([^$]*)\\$", RegexOptions.Compiled); + static System.Text.RegularExpressions.Regex variableRegEx = new System.Text.RegularExpressions.Regex ("\\$([^$]*)\\$", RegexOptions.Compiled); public List<string> ParseVariables (string code) { var result = new List<string> (); - foreach (Match match in variableRegEx.Matches (code)) { + foreach (System.Text.RegularExpressions.Match match in variableRegEx.Matches (code)) { string name = match.Groups[1].Value; if (name == "end" || name == "selected" || string.IsNullOrEmpty (name) || name.Trim ().Length == 0) continue; @@ -211,9 +213,9 @@ namespace MonoDevelop.Ide.CodeTemplates var result = new TemplateResult (); var sb = new StringBuilder (); int lastOffset = 0; - string code = context.Document.Editor.FormatString (context.InsertPosition, context.TemplateCode); + string code = context.Editor.FormatString (context.InsertPosition, context.TemplateCode); result.TextLinks = new List<TextLink> (); - foreach (Match match in variableRegEx.Matches (code)) { + foreach (System.Text.RegularExpressions.Match match in variableRegEx.Matches (code)) { string name = match.Groups [1].Value; sb.Append (code.Substring (lastOffset, match.Index - lastOffset)); lastOffset = match.Index + match.Length; @@ -230,7 +232,7 @@ namespace MonoDevelop.Ide.CodeTemplates } if (!variableDecarations.ContainsKey (name)) continue; - TextLink link = result.TextLinks.Find (l => l.Name == name); + var link = result.TextLinks.Find (l => l.Name == name); bool isNew = link == null; if (isNew) { link = new TextLink (name); @@ -271,13 +273,13 @@ namespace MonoDevelop.Ide.CodeTemplates sb.Append (code.Substring (lastOffset, code.Length - lastOffset)); // format & indent template code - var data = new TextEditorData (); + var data = TextEditorFactory.CreateNewDocument (); data.Text = sb.ToString (); - data.Document.TextReplaced += delegate(object sender, DocumentChangeEventArgs e) { - int delta = e.ChangeDelta; + data.TextChanged += delegate(object sender, MonoDevelop.Core.Text.TextChangeEventArgs e) { + int delta = e.InsertionLength - e.RemovalLength; foreach (var link in result.TextLinks) { - link.Links = new List<TextSegment> (link.Links.AdjustSegments (e)); + link.Links = link.Links.AdjustSegments (e).ToList (); } if (result.CaretEndOffset > e.Offset) result.CaretEndOffset += delta; @@ -285,7 +287,6 @@ namespace MonoDevelop.Ide.CodeTemplates IndentCode (data, context.LineIndent); result.Code = data.Text; - data.Dispose (); return result; } @@ -319,12 +320,12 @@ namespace MonoDevelop.Ide.CodeTemplates return result.ToString (); } - static void IndentCode (TextEditorData data, string lineIndent) + static void IndentCode (ITextDocument data, string lineIndent) { for (int i = 1; i < data.LineCount; i++) { var line = data.GetLine (i + 1); if (line.Length > 0) - data.Insert (line.Offset, lineIndent); + data.InsertText (line.Offset, lineIndent); } } @@ -345,10 +346,10 @@ namespace MonoDevelop.Ide.CodeTemplates string RemoveIndent (string text, string indent) { - var doc = new TextDocument (); + var doc = TextEditorFactory.CreateNewDocument (); doc.Text = text; var result = new StringBuilder (); - foreach (DocumentLine line in doc.Lines) { + foreach (var line in doc.GetLines ()) { string curLineIndent = line.GetIndentation (doc); int offset = Math.Min (curLineIndent.Length, indent.Length); result.Append (doc.GetTextBetween (line.Offset + offset, line.EndOffsetIncludingDelimiter)); @@ -358,58 +359,64 @@ namespace MonoDevelop.Ide.CodeTemplates string Reindent (string text, string indent) { - var doc = new TextDocument (); + var doc = TextEditorFactory.CreateNewDocument (); doc.Text = text; var result = new StringBuilder (); - foreach (DocumentLine line in doc.Lines) { + foreach (var line in doc.GetLines ()) { if (result.Length > 0) result.Append (indent); result.Append (doc.GetTextAt (line.SegmentIncludingDelimiter)); } return result.ToString (); } - + public void Insert (MonoDevelop.Ide.Gui.Document document) { - var handler = document.GetContent<ICodeTemplateHandler> (); + Insert (document.Editor, document); + } + + public void Insert (TextEditor editor, DocumentContext context) + { + var handler = context.GetContent<ICodeTemplateHandler> (); if (handler != null) { - handler.InsertTemplate (this, document); + handler.InsertTemplate (this, editor, context); } else { - InsertTemplateContents (document); + InsertTemplateContents (editor, context); } } /// <summary> /// Don't use this unless you're implementing ICodeTemplateWidget. Use Insert instead. /// </summary> - public TemplateResult InsertTemplateContents (MonoDevelop.Ide.Gui.Document document) + public TemplateResult InsertTemplateContents (TextEditor editor, DocumentContext context) { - Mono.TextEditor.TextEditorData data = document.Editor; + var data = editor; - int offset = data.Caret.Offset; + int offset = data.CaretOffset; // string leadingWhiteSpace = GetLeadingWhiteSpace (editor, editor.CursorLine); - var context = new TemplateContext { + var templateCtx = new TemplateContext { Template = this, - Document = document, - ParsedDocument = document.ParsedDocument != null ? document.ParsedDocument.ParsedFile : null, - InsertPosition = data.Caret.Location, - LineIndent = data.Document.GetLineIndent (data.Caret.Line), + DocumentContext = context, + Editor = editor, + //ParsedDocument = context.ParsedDocument != null ? context.ParsedDocument.ParsedFile : null, + InsertPosition = data.CaretLocation, + LineIndent = data.GetLineIndent (data.CaretLocation.Line), TemplateCode = Code }; if (data.IsSomethingSelected) { int start = data.SelectionRange.Offset; - while (Char.IsWhiteSpace (data.Document.GetCharAt (start))) { + while (Char.IsWhiteSpace (data.GetCharAt (start))) { start++; } int end = data.SelectionRange.EndOffset; - while (Char.IsWhiteSpace (data.Document.GetCharAt (end - 1))) { + while (Char.IsWhiteSpace (data.GetCharAt (end - 1))) { end--; } - context.LineIndent = data.Document.GetLineIndent (data.Document.OffsetToLineNumber (start)); - context.SelectedText = RemoveIndent (data.Document.GetTextBetween (start, end), context.LineIndent); - data.Remove (start, end - start); + templateCtx.LineIndent = data.GetLineIndent (data.OffsetToLineNumber (start)); + templateCtx.SelectedText = RemoveIndent (data.GetTextBetween (start, end), templateCtx.LineIndent); + data.RemoveText (start, end - start); offset = start; } else { string word = GetWordBeforeCaret (data).Trim (); @@ -417,9 +424,9 @@ namespace MonoDevelop.Ide.CodeTemplates offset = DeleteWordBeforeCaret (data); } - TemplateResult template = FillVariables (context); + TemplateResult template = FillVariables (templateCtx); template.InsertPosition = offset; - document.Editor.Insert (offset, template.Code); + editor.InsertText (offset, template.Code); int newoffset; if (template.CaretEndOffset >= 0) { @@ -428,13 +435,13 @@ namespace MonoDevelop.Ide.CodeTemplates newoffset = offset + template.Code.Length; } - document.Editor.Caret.Location = document.Editor.OffsetToLocation (newoffset) ; + editor.CaretLocation = editor.OffsetToLocation (newoffset) ; var prettyPrinter = CodeFormatterService.GetFormatter (data.MimeType); if (prettyPrinter != null) { int endOffset = template.InsertPosition + template.Code.Length; var oldVersion = data.Version; - prettyPrinter.OnTheFlyFormat (document, template.InsertPosition, endOffset); + prettyPrinter.OnTheFlyFormat (editor, context, template.InsertPosition, endOffset); foreach (var textLink in template.TextLinks) { for (int i = 0; i < textLink.Links.Count; i++) { var segment = textLink.Links [i]; @@ -446,6 +453,12 @@ namespace MonoDevelop.Ide.CodeTemplates return template; } + public TemplateResult InsertTemplateContents (Document document) + { + if (document == null) + throw new ArgumentNullException ("document"); + return InsertTemplateContents (document.Editor, document); + } #region I/O public const string Node = "CodeTemplate"; const string HeaderNode = "Header"; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplateCompletionData.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplateCompletionData.cs index 3f19b81ce7..6edaa60d6e 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplateCompletionData.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplateCompletionData.cs @@ -28,20 +28,22 @@ using System; using MonoDevelop.Ide.Gui; using MonoDevelop.Ide.CodeCompletion; using MonoDevelop.Core; +using MonoDevelop.Ide.Editor.Extension; +using MonoDevelop.Ide.Editor; namespace MonoDevelop.Ide.CodeTemplates { public interface ICodeTemplateHandler { - void InsertTemplate (CodeTemplate template, Document document); + void InsertTemplate (CodeTemplate template, TextEditor editor, DocumentContext context); } public class CodeTemplateCompletionData : CompletionData { - Document doc; - CodeTemplate template; + readonly TextEditorExtension doc; + readonly CodeTemplate template; - public CodeTemplateCompletionData (Document doc, CodeTemplate template) + public CodeTemplateCompletionData (TextEditorExtension doc, CodeTemplate template) { this.doc = doc; this.template = template; @@ -51,9 +53,9 @@ namespace MonoDevelop.Ide.CodeTemplates this.Description = template.Shortcut + Environment.NewLine + GettextCatalog.GetString (template.Description); } - public override void InsertCompletionText (CompletionListWindow window, ref KeyActions ka, Gdk.Key closeChar, char keyChar, Gdk.ModifierType modifier) + public override void InsertCompletionText (CompletionListWindow window, ref KeyActions ka, KeyDescriptor descriptor) { - template.Insert (doc); + template.Insert (doc.Editor, doc.DocumentContext); } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplateListDataProvider.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplateListDataProvider.cs index 1487ce6806..30a94e91ef 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplateListDataProvider.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplateListDataProvider.cs @@ -24,9 +24,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -using System; using System.Collections.Generic; -using Mono.TextEditor.PopupWindow; namespace MonoDevelop.Ide.CodeTemplates { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplatePanel.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplatePanel.cs index f259dfdf67..a454de6047 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplatePanel.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplatePanel.cs @@ -31,6 +31,7 @@ using Gtk; using MonoDevelop.Core; using MonoDevelop.Ide.Gui.Dialogs; using MonoDevelop.Components; +using MonoDevelop.Ide.Editor; namespace MonoDevelop.Ide.CodeTemplates { @@ -41,14 +42,15 @@ namespace MonoDevelop.Ide.CodeTemplates Gtk.TreeStore templateStore; CellRendererText templateCellRenderer; CellRendererImage pixbufCellRenderer; - Mono.TextEditor.TextEditor textEditor = new Mono.TextEditor.TextEditor (); - Mono.TextEditor.TextEditorOptions options; + TextEditor textEditor = TextEditorFactory.CreateNewEditor (); + ITextEditorOptions options; public CodeTemplatePanelWidget (OptionsDialog parent) { this.Build(); - scrolledwindow1.Add (textEditor); - textEditor.ShowAll (); + Gtk.Widget control = textEditor; + scrolledwindow1.Add (control); + control.ShowAll (); templateStore = new TreeStore (typeof (CodeTemplate), typeof (string), typeof (string)); @@ -73,13 +75,9 @@ namespace MonoDevelop.Ide.CodeTemplates treeviewCodeTemplates.ExpandAll (); treeviewCodeTemplates.Selection.Changed += HandleChanged; - - options = new MonoDevelop.Ide.Gui.CommonTextEditorOptions (); - options.ShowLineNumberMargin = false; - options.ShowFoldMargin = false; - options.ShowIconMargin = false; - textEditor.Options = options; - textEditor.Document.ReadOnly = true; + + textEditor.Options = DefaultSourceEditorOptions.PlainEditor; + textEditor.IsReadOnly = true; this.buttonAdd.Clicked += ButtonAddClicked; this.buttonEdit.Clicked += ButtonEditClicked; this.buttonRemove.Clicked += ButtonRemoveClicked; @@ -188,10 +186,10 @@ namespace MonoDevelop.Ide.CodeTemplates CodeTemplate template = templateStore.GetValue (iter, 0) as CodeTemplate; if (template != null) { textEditor.ClearSelection (); - textEditor.Document.MimeType = template.MimeType; - textEditor.Document.Text = template.Code; + textEditor.MimeType = template.MimeType; + textEditor.Text = template.Code; } else { - textEditor.Document.Text = ""; + textEditor.Text = ""; } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/EditTemplateDialog.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/EditTemplateDialog.cs index f81346e749..eedfa7e058 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/EditTemplateDialog.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/EditTemplateDialog.cs @@ -32,6 +32,8 @@ using Gtk; using MonoDevelop.Core; using Gdk; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Core.Text; namespace MonoDevelop.Ide.CodeTemplates @@ -41,8 +43,8 @@ namespace MonoDevelop.Ide.CodeTemplates public partial class EditTemplateDialog : Gtk.Dialog { CodeTemplate template; - Mono.TextEditor.TextEditor textEditor = new Mono.TextEditor.TextEditor (); - Mono.TextEditor.TextEditorOptions options; + TextEditor textEditor = TextEditorFactory.CreateNewEditor (); + ITextEditorOptions options; ListStore variablesListStore; List<CodeTemplateVariable> variables = new List<CodeTemplateVariable> (); @@ -59,24 +61,20 @@ namespace MonoDevelop.Ide.CodeTemplates this.comboboxentryGroups.Entry.Text = template.Group ?? ""; this.comboboxentryMime.Entry.Text = template.MimeType ?? ""; this.entryDescription.Text = template.Description ?? ""; - this.textEditor.Document.MimeType = template.MimeType; - this.textEditor.Document.Text = template.Code; + this.textEditor.MimeType = template.MimeType; + this.textEditor.Text = template.Code; checkbuttonExpansion.Active = (template.CodeTemplateType & CodeTemplateType.Expansion) == CodeTemplateType.Expansion; checkbuttonSurroundWith.Active = (template.CodeTemplateType & CodeTemplateType.SurroundsWith) == CodeTemplateType.SurroundsWith; - scrolledwindow1.Child = textEditor; - textEditor.ShowAll (); - textEditor.Caret.PositionChanged += CaretPositionChanged; - options = new Mono.TextEditor.TextEditorOptions (); - options.ShowLineNumberMargin = false; - options.ShowFoldMargin = false; - options.ShowIconMargin = false; - options.ColorScheme = IdeApp.Preferences.ColorScheme; - textEditor.Options = options; - - HashSet<string> mimeTypes = new HashSet<string> (); - HashSet<string> groups = new HashSet<string> (); + Gtk.Widget control = textEditor; + scrolledwindow1.Child = control; + control.ShowAll (); + textEditor.CaretPositionChanged += CaretPositionChanged; + textEditor.Options = DefaultSourceEditorOptions.PlainEditor; + + var mimeTypes = new HashSet<string> (); + var groups = new HashSet<string> (); foreach (CodeTemplate ct in CodeTemplateService.Templates) { mimeTypes.Add (ct.MimeType); groups.Add (ct.Group); @@ -89,7 +87,7 @@ namespace MonoDevelop.Ide.CodeTemplates foreach (string group in groups) { comboboxentryGroups.AppendText (group); } - textEditor.Document.TextReplaced += DocumentTextReplaced; + textEditor.TextChanged += DocumentTextReplaced; this.buttonOk.Clicked += ButtonOkClicked; checkbuttonWhiteSpaces.Hide (); @@ -140,7 +138,7 @@ namespace MonoDevelop.Ide.CodeTemplates template.Group = this.comboboxentryGroups.Entry.Text; template.MimeType = this.comboboxentryMime.Entry.Text; template.Description = this.entryDescription.Text; - template.Code = this.textEditor.Document.Text; + template.Code = this.textEditor.Text; variables.ForEach (v => template.AddVariable (v)); template.CodeTemplateType = CodeTemplateType.Unknown; if (checkbuttonExpansion.Active) @@ -149,9 +147,9 @@ namespace MonoDevelop.Ide.CodeTemplates template.CodeTemplateType |= CodeTemplateType.SurroundsWith; } - void DocumentTextReplaced (object sender, Mono.TextEditor.DocumentChangeEventArgs e) + void DocumentTextReplaced (object sender, TextChangeEventArgs e) { - List<string> vars = template.ParseVariables (textEditor.Document.Text); + List<string> vars = template.ParseVariables (textEditor.Text); foreach (string var in vars) { if (!variables.Any (v => v.Name == var) && !template.Variables.Any (v => v.Name == var)) { variables.Add (new CodeTemplateVariable (var) { @@ -170,13 +168,13 @@ namespace MonoDevelop.Ide.CodeTemplates } - void CaretPositionChanged (object sender, Mono.TextEditor.DocumentLocationEventArgs e) + void CaretPositionChanged (object sender, EventArgs e) { comboboxVariables.Active = -1; - int offset = textEditor.Caret.Offset; + int offset = textEditor.CaretOffset; int start = offset; - while (start >= 0 && start < textEditor.Document.TextLength) { // caret offset may be behind the text - char ch = textEditor.Document.GetCharAt (start); + while (start >= 0 && start < textEditor.Length) { // caret offset may be behind the text + char ch = textEditor.GetCharAt (start); if (ch == '$') break; if (!char.IsLetterOrDigit (ch) && ch != '_') @@ -185,16 +183,16 @@ namespace MonoDevelop.Ide.CodeTemplates } int end = offset; - while (end < textEditor.Document.TextLength) { - char ch = textEditor.Document.GetCharAt (end); + while (end < textEditor.Length) { + char ch = textEditor.GetCharAt (end); if (ch == '$') break; if (!char.IsLetterOrDigit (ch) && ch != '_') return; end++; } - if (start >= 0 && end < textEditor.Document.TextLength) { - string varName = textEditor.Document.GetTextBetween (start, end).Trim ('$'); + if (start >= 0 && end < textEditor.Length) { + string varName = textEditor.GetTextBetween (start, end).Trim ('$'); TreeIter iter; if (variablesListStore.GetIterFirst (out iter)) { int i = -1; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/ExpansionObject.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/ExpansionObject.cs index 721f645107..9e3fba71fe 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/ExpansionObject.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/ExpansionObject.cs @@ -26,17 +26,19 @@ using System; using System.Collections.Generic; -using System.Text; using System.Text.RegularExpressions; using MonoDevelop.Ide.Gui.Content; -using Mono.TextEditor.PopupWindow; -using Mono.TextEditor; -using ICSharpCode.NRefactory.TypeSystem; using MonoDevelop.Ide.TypeSystem; -using ICSharpCode.NRefactory.Completion; using MonoDevelop.Ide.CodeCompletion; -using ICSharpCode.NRefactory.TypeSystem.Implementation; +using MonoDevelop.Ide.Tasks; +using Microsoft.CodeAnalysis; +using System.Threading.Tasks; +using System.Linq; +using ICSharpCode.NRefactory6.CSharp; +using ICSharpCode.NRefactory6.CSharp.Completion; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Ide.Editor.Extension; namespace MonoDevelop.Ide.CodeTemplates { @@ -47,17 +49,15 @@ namespace MonoDevelop.Ide.CodeTemplates set; } - public ICompilation Compilation { + public SemanticModel Compilation { get { - return Document.Compilation; + var analysisDocument = DocumentContext.ParsedDocument; + if (analysisDocument == null) + return null; + return analysisDocument.GetAst<SemanticModel> (); } } - - public IUnresolvedFile ParsedDocument { - get; - set; - } - + public DocumentLocation InsertPosition { get; set; @@ -78,7 +78,12 @@ namespace MonoDevelop.Ide.CodeTemplates set; } - public MonoDevelop.Ide.Gui.Document Document { + public DocumentContext DocumentContext { + get; + set; + } + + public TextEditor Editor { get; set; } @@ -93,36 +98,28 @@ namespace MonoDevelop.Ide.CodeTemplates public string GetCurrentClassName () { - if (CurrentContext.ParsedDocument == null) - return null; - IUnresolvedTypeDefinition type = null; - var provider = CurrentContext.Document.GetContent<ITextEditorMemberPositionProvider>(); - if (provider == null) { - type = CurrentContext.ParsedDocument.GetInnermostTypeDefinition (CurrentContext.InsertPosition.Line, CurrentContext.InsertPosition.Column); - } else { - type = provider.GetTypeAt (CurrentContext.Document.Editor.LocationToOffset (CurrentContext.InsertPosition)); - } - - if (type == null) + var compilation = CurrentContext.Compilation; + if (compilation == null) return null; - return type.Name; + var enclosingSymbol = compilation.GetEnclosingSymbol (CurrentContext.Editor.CaretOffset); + + if (!(enclosingSymbol is ITypeSymbol)) + enclosingSymbol = enclosingSymbol.ContainingType; + + return enclosingSymbol != null ? enclosingSymbol.Name : null; } public string GetConstructorModifier () { - if (CurrentContext.ParsedDocument == null) + var compilation = CurrentContext.Compilation; + if (compilation == null) return null; - IUnresolvedTypeDefinition type = null; - var provider = CurrentContext.Document.GetContent<ITextEditorMemberPositionProvider>(); - if (provider == null) { - type = CurrentContext.ParsedDocument.GetInnermostTypeDefinition (CurrentContext.InsertPosition.Line, CurrentContext.InsertPosition.Column); - } else { - type = provider.GetTypeAt (CurrentContext.Document.Editor.LocationToOffset (CurrentContext.InsertPosition)); - } - - if (type == null) - return ""; - return type.IsStatic ? "static " : "public "; + var enclosingSymbol = compilation.GetEnclosingSymbol (CurrentContext.Editor.CaretOffset); + + if (!(enclosingSymbol is ITypeSymbol)) + enclosingSymbol = enclosingSymbol.ContainingType; + + return enclosingSymbol != null && enclosingSymbol.IsStatic ? "static " : "public "; } public string GetLengthProperty (Func<string, string> callback, string varName) @@ -132,29 +129,27 @@ namespace MonoDevelop.Ide.CodeTemplates string var = callback (varName); - ITextEditorResolver textEditorResolver = CurrentContext.Document.GetContent <ITextEditorResolver> (); + ITextEditorResolver textEditorResolver = CurrentContext.DocumentContext.GetContent <ITextEditorResolver> (); if (textEditorResolver != null) { - var result = textEditorResolver.GetLanguageItem (CurrentContext.Document.Editor.Document.LocationToOffset (CurrentContext.InsertPosition), var); - if (result.Type.IsReferenceType.HasValue && !result.Type.IsReferenceType.Value) - return "Length"; + var result = textEditorResolver.GetLanguageItem (CurrentContext.Editor.LocationToOffset (CurrentContext.InsertPosition), var); + if (result != null) { + var returnType = result.GetReturnType (); + if (returnType != null && !returnType.IsReferenceType) + return "Length"; + } } return "Count"; } - IType GetElementType (IType result) + ITypeSymbol GetElementType (ITypeSymbol type) { - foreach (var baseType in result.GetAllBaseTypes ()) { - var baseTypeDef = baseType.GetDefinition(); - if (baseTypeDef != null && baseTypeDef.Name == "IEnumerable") { - if (baseTypeDef.Namespace == "System.Collections.Generic" && baseTypeDef.TypeParameterCount == 1) { - if (baseType.TypeArguments.Count > 0) - return baseType.TypeArguments[0]; - } else if (baseTypeDef.Namespace == "System.Collections" && baseTypeDef.TypeParameterCount == 0) { - return CurrentContext.Compilation.FindType (KnownTypeCode.Object); - } + foreach (var baseType in type.AllInterfaces) { + if (baseType != null && baseType.Name == "IEnumerable") { + if (baseType.TypeArguments.Length > 0) + return baseType.TypeArguments[0]; } } - return new UnknownType ("", "", 0); + return type; } @@ -162,70 +157,59 @@ namespace MonoDevelop.Ide.CodeTemplates { if (callback == null) return "var"; - + var compilation = CurrentContext.Compilation; + if (compilation == null) + return null; + string var = callback (varName); - ITextEditorResolver textEditorResolver = CurrentContext.Document.GetContent <ITextEditorResolver> (); - if (textEditorResolver != null) { - var result = textEditorResolver.GetLanguageItem (CurrentContext.Document.Editor.Caret.Offset, var); - if (result != null) { - var componentType = GetElementType (result.Type); - if (componentType.Kind != TypeKind.Unknown) { - var generator = CodeGenerator.CreateGenerator (CurrentContext.Document); - if (generator != null) - return generator.GetShortTypeString (CurrentContext.Document, componentType); - } - } - } - + + var offset = CurrentContext.Editor.CaretOffset; + var sym = compilation.LookupSymbols (offset).First (s => s.Name == var); + if (sym == null) + return "var"; + var rt = sym.GetReturnType (); + if (rt != null) + return rt.ToMinimalDisplayString (compilation, offset); return "var"; } - MonoDevelop.Ide.CodeCompletion.ICompletionDataList list; + + ICompletionDataList list; public IListDataProvider<string> GetCollections () { var result = new List<CodeTemplateVariableValue> (); - var ext = CurrentContext.Document.GetContent <CompletionTextEditorExtension> (); + var ext = CurrentContext.DocumentContext.GetContent <CompletionTextEditorExtension> (); if (ext != null) { if (list == null) list = ext.CodeCompletionCommand ( - CurrentContext.Document.GetContent <MonoDevelop.Ide.CodeCompletion.ICompletionWidget> ().CurrentCodeCompletionContext); + CurrentContext.DocumentContext.GetContent <MonoDevelop.Ide.CodeCompletion.ICompletionWidget> ().CurrentCodeCompletionContext); - foreach (object o in list) { - var data = o as IEntityCompletionData; - if (data == null) - continue; - - if (data.Entity is IMember) { - var m = data.Entity as IMember; - if (GetElementType (m.ReturnType).Kind != TypeKind.Unknown) { - if (m is IMethod) { - if (((IMethod)m).Parameters.Count == 0) - result.Add (new CodeTemplateVariableValue (m.Name + " ()", ((CompletionData)data).Icon)); - continue; - } - - result.Add (new CodeTemplateVariableValue (m.Name, ((CompletionData)data).Icon)); + foreach (var data in list.OfType<ISymbolCompletionData> ()) { + if (GetElementType (data.Symbol.GetReturnType ()).TypeKind != TypeKind.Error) { + var method = data as IMethodSymbol; + if (method != null) { + if (method.Parameters.Length == 0) + result.Add (new CodeTemplateVariableValue (data.Symbol.Name + " ()", ((CompletionData)data).Icon)); + continue; } + + result.Add (new CodeTemplateVariableValue (data.Symbol.Name, ((CompletionData)data).Icon)); } } - foreach (object o in list) { - var data = o as IEntityCompletionData; - if (data == null) - continue; - if (data.Entity is IParameter) { - var m = data.Entity as IParameter; - if (GetElementType (m.Type).Kind != TypeKind.Unknown) + foreach (var data in list.OfType<ISymbolCompletionData> ()) { + var m = data.Symbol as IParameterSymbol; + if (m != null) { + if (GetElementType (m.Type).TypeKind != TypeKind.Error) result.Add (new CodeTemplateVariableValue (m.Name, ((CompletionData)data).Icon)); } } - foreach (object o in list) { - var data = o as IVariableCompletionData; - if (data == null) + foreach (var sym in list.OfType<ISymbolCompletionData> ()) { + var m = sym.Symbol as ILocalSymbol; + if (m == null) continue; - var m = data.Variable; - if (GetElementType (m.Type).Kind != TypeKind.Unknown) - result.Add (new CodeTemplateVariableValue (m.Name, ((CompletionData)data).Icon)); + if (GetElementType (m.Type).TypeKind != TypeKind.Error) + result.Add (new CodeTemplateVariableValue (m.Name, ((CompletionData)m).Icon)); } } return new CodeTemplateListDataProvider (result); @@ -233,7 +217,8 @@ namespace MonoDevelop.Ide.CodeTemplates public string GetSimpleTypeName (string fullTypeName) { - if (CurrentContext.ParsedDocument == null) + var compilation = CurrentContext.Compilation; + if (compilation == null) return fullTypeName.Replace ("#", "."); string ns = ""; string name = ""; @@ -249,31 +234,21 @@ namespace MonoDevelop.Ide.CodeTemplates idx = name.IndexOf ('.'); if (idx >= 0) { - member = name.Substring (idx); + member = name.Substring (idx + 1); name = name.Substring (0, idx); } - var type = new GetClassTypeReference (ns, name, 0).Resolve (new SimpleTypeResolveContext (CurrentContext.Document.Compilation.MainAssembly)); - bool stripAttribute = false; - if (type == null || type.Kind == TypeKind.Unknown) { - type = new GetClassTypeReference (ns, name + "Attribute", 0).Resolve ( - new SimpleTypeResolveContext (CurrentContext.Document.Compilation.MainAssembly) - ); - stripAttribute = true; - } - if (type == null || type.Kind == TypeKind.Unknown) - return fullTypeName.Replace ("#", "."); - var generator = CodeGenerator.CreateGenerator (CurrentContext.Document); - if (generator != null) { - var result = generator.GetShortTypeString (CurrentContext.Document, type) + member; - if (stripAttribute && result.EndsWith ("Attribute", StringComparison.Ordinal)) - result = result.Substring (0, result.Length - "Attribute".Length); - return result; + var metadataName = string.IsNullOrEmpty (ns) ? name : ns + "." + name; + var type = compilation.Compilation.GetTypeByMetadataName (metadataName); + if (type != null) { + var minimalName = type.ToMinimalDisplayString (compilation, CurrentContext.Editor.CaretOffset); + return string.IsNullOrEmpty (member) ? minimalName : minimalName + "." + member; } return fullTypeName.Replace ("#", "."); } - static Regex functionRegEx = new Regex ("([^(]*)\\(([^(]*)\\)", RegexOptions.Compiled); + + static System.Text.RegularExpressions.Regex functionRegEx = new System.Text.RegularExpressions.Regex ("([^(]*)\\(([^(]*)\\)", RegexOptions.Compiled); // We should use reflection here (but for 5 functions it doesn't hurt) !!! - Mike @@ -294,7 +269,7 @@ namespace MonoDevelop.Ide.CodeTemplates public virtual IListDataProvider<string> RunFunction (TemplateContext context, Func<string, string> callback, string function) { this.CurrentContext = context; - Match match = functionRegEx.Match (function); + var match = functionRegEx.Match (function); if (!match.Success) return null; string name = match.Groups[1].Value; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ProjectContentEventArgs.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/IListDataProvider.cs index 54d31aa138..b1d02b8310 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ProjectContentEventArgs.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/IListDataProvider.cs @@ -1,10 +1,10 @@ // -// ProjectContentEventArgs.cs +// IListDataProvider.cs // // Author: // Mike Krüger <mkrueger@novell.com> // -// Copyright (c) 2011 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 @@ -23,30 +23,29 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. + using System; -using ICSharpCode.NRefactory.TypeSystem; -using MonoDevelop.Projects; +using System.Collections.Generic; -namespace MonoDevelop.Ide.TypeSystem +namespace MonoDevelop.Ide.CodeTemplates { - [Serializable] - public sealed class ProjectContentEventArgs : EventArgs + public interface IListDataProvider<T> { - public Project Project { - get; - private set; - } - - public IProjectContent Content { + int Count { get; - private set; } - public ProjectContentEventArgs (Project project, IProjectContent content) - { - this.Project = project; - this.Content = content; + T this[int index] { + get; } + + Xwt.Drawing.Image GetIcon (int index); + string GetText (int index); } -} - + + public interface IMarkupListDataProvider<T> : IListDataProvider<T> + { + bool HasMarkup (int index); + string GetMarkup (int index); + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/CustomStringTagProvider.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/CustomStringTagProvider.cs index b39ae14707..15f096ced4 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/CustomStringTagProvider.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/CustomStringTagProvider.cs @@ -87,17 +87,17 @@ namespace MonoDevelop.Ide.Commands case "CURLINE": if (wb.ActiveDocument != null && wb.ActiveDocument.Editor != null) - return wb.ActiveDocument.Editor.Caret.Line; + return wb.ActiveDocument.Editor.CaretLocation.Line; return null; case "CURCOLUMN": if (wb.ActiveDocument != null && wb.ActiveDocument.Editor != null) - return wb.ActiveDocument.Editor.Caret.Column; + return wb.ActiveDocument.Editor.CaretLocation.Column; return null; case "CUROFFSET": if (wb.ActiveDocument != null && wb.ActiveDocument.Editor != null) - return wb.ActiveDocument.Editor.Caret.Offset; + return wb.ActiveDocument.Editor.CaretOffset; return null; case "CURTEXT": diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/EditCommands.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/EditCommands.cs index 1ebdb497bd..49e2fab704 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/EditCommands.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/EditCommands.cs @@ -287,7 +287,7 @@ namespace MonoDevelop.Ide.Commands { Document doc = IdeApp.Workbench.ActiveDocument; string header = MonoDevelop.Ide.StandardHeader.StandardHeaderService.GetHeader (doc.Project, doc.Name, false); - doc.Editor.Insert (0, header + "\n"); + doc.Editor.InsertText (0, header + "\n"); } protected override void Update (CommandInfo info) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/FileCommands.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/FileCommands.cs index 82dab65f80..0c654ba2e9 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/FileCommands.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/FileCommands.cs @@ -38,6 +38,7 @@ using Gtk; using MonoDevelop.Ide.Projects; using MonoDevelop.Ide.Desktop; using System.Linq; +using MonoDevelop.Components; namespace MonoDevelop.Ide.Commands { @@ -365,7 +366,7 @@ namespace MonoDevelop.Ide.Commands protected override void Run (object dataItem) { string filename = (string)dataItem; - Gdk.ModifierType mtype = Mono.TextEditor.GtkWorkarounds.GetCurrentKeyModifiers (); + Gdk.ModifierType mtype = GtkWorkarounds.GetCurrentKeyModifiers (); bool inWorkspace = (mtype & Gdk.ModifierType.ControlMask) != 0; IdeApp.Workspace.OpenWorkspaceItem (filename, !inWorkspace); } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/ProjectCommands.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/ProjectCommands.cs index 1464202903..8e2fa052bb 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/ProjectCommands.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/ProjectCommands.cs @@ -338,7 +338,7 @@ namespace MonoDevelop.Ide.Commands if (h == null || !IdeApp.ProjectOperations.CurrentRunOperation.IsCompleted) return; - IdeApp.ProjectOperations.Execute (target); + IdeApp.ProjectOperations.Execute (target, h); } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/TextEditorCommands.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/TextEditorCommands.cs index fa09268349..eaa9fcd823 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/TextEditorCommands.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/TextEditorCommands.cs @@ -91,7 +91,11 @@ namespace MonoDevelop.Ide.Commands DuplicateLine, ToggleCompletionSuggestionMode, - ToggleBlockSelectionMode + ToggleBlockSelectionMode, + + DynamicAbbrev, + + PulseCaret } public class ToggleCompletionSuggestionModeHandler : CommandHandler diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/ViewCommands.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/ViewCommands.cs index 9d3d19c798..36d6161f18 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/ViewCommands.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/ViewCommands.cs @@ -378,7 +378,7 @@ namespace MonoDevelop.Ide.Commands protected override void Run () { - IdeApp.Workbench.ActiveDocument.Editor.SetCaretTo (IdeApp.Workbench.ActiveDocument.Editor.Caret.Line, IdeApp.Workbench.ActiveDocument.Editor.Caret.Column); + IdeApp.Workbench.ActiveDocument.Editor.StartCaretPulseAnimation (); } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/AbstractUsagesExtension.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/AbstractUsagesExtension.cs new file mode 100644 index 0000000000..ec5fd72ee1 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/AbstractUsagesExtension.cs @@ -0,0 +1,246 @@ +// +// AbstractUsagesExtension.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.Content; +using System.Collections.Generic; +using System.Linq; +using MonoDevelop.Ide.FindInFiles; +using System.Threading; +using System.Threading.Tasks; +using MonoDevelop.Core; +using System.Diagnostics.CodeAnalysis; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Core.Text; + +namespace MonoDevelop.Ide.Editor.Extension +{ + public abstract class UsageProviderEditorExtension : TextEditorExtension + { + public abstract IEnumerable<Usage> Usages { + get; + } + + public event EventHandler UsagesUpdated; + + protected void OnUsagesUpdated (EventArgs e) + { + var handler = UsagesUpdated; + if (handler != null) + handler (this, e); + } + } + + /// <summary> + /// Provides a base class for implementing highlighting of usages inside the text editor. + /// </summary> + public abstract class AbstractUsagesExtension<T> : UsageProviderEditorExtension + { + [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")] + protected static readonly List<MemberReference> EmptyList = new List<MemberReference> (); + + CancellationTokenSource tooltipCancelSrc = new CancellationTokenSource (); + List<ITextSegmentMarker> markers = new List<ITextSegmentMarker> (); + + public IList<ITextSegmentMarker> Markers { + get { + return markers; + } + } + + uint popupTimer; + + protected override void Initialize () + { + Editor.CaretPositionChanged += HandleTextEditorDataCaretPositionChanged; + Editor.TextChanged += HandleTextEditorDataDocumentTextReplaced; + Editor.SelectionChanged += HandleTextEditorDataSelectionChanged; + } + + void HandleTextEditorDataSelectionChanged (object sender, EventArgs e) + { + if (Editor.IsSomethingSelected) + RemoveMarkers (); + } + + void HandleTextEditorDataDocumentTextReplaced (object sender, TextChangeEventArgs e) + { + RemoveMarkers (); + } + + public override void Dispose () + { + CancelTooltip (); + + Editor.SelectionChanged -= HandleTextEditorDataSelectionChanged; + Editor.CaretPositionChanged -= HandleTextEditorDataCaretPositionChanged; + Editor.TextChanged -= HandleTextEditorDataDocumentTextReplaced; + base.Dispose (); + RemoveTimer (); + } + + public bool IsTimerOnQueue { + get { + return popupTimer != 0; + } + } + + public void ForceUpdate () + { + RemoveTimer (); + DelayedTooltipShow (); + } + + /// <summary> + /// Tries to resolve inside the current location inside tho document. + /// </summary> + /// <returns><c>true</c>, if resolve was successful, <c>false</c> otherwise.</returns> + /// <param name="token">A cancellation token to cancel the operation.</param> + protected abstract Task<T> ResolveAsync (CancellationToken token); + + + /// <summary> + /// Gets all references from a given resolve result. Note that this method is called on a background thread. + /// </summary> + /// <returns>The references.</returns> + /// <param name="resolveResult">The resolve result given in 'TryResolve'.</param> + /// <param name="token">A cancellation token to cancel the operation.</param> + protected abstract IEnumerable<MemberReference> GetReferences (T resolveResult, CancellationToken token); + + async void DelayedTooltipShow () + { + try { + CancelTooltip (); + + var token = tooltipCancelSrc.Token; + + T result = await ResolveAsync (token); + if (token.IsCancellationRequested) { + ClearQuickTasks (); + return; + } + + Task.Run (delegate { + var list = GetReferences (result, token).ToList (); + if (!token.IsCancellationRequested) { + Gtk.Application.Invoke (delegate { + if (!token.IsCancellationRequested) + ShowReferences (list); + }); + } + }); + + } catch (Exception e) { + LoggingService.LogError ("Unhandled Exception in HighlightingUsagesExtension", e); + } finally { + popupTimer = 0; + } + } + + void RemoveTimer () + { + if (popupTimer != 0) { + GLib.Source.Remove (popupTimer); + popupTimer = 0; + } + } + + void HandleTextEditorDataCaretPositionChanged (object sender, EventArgs e) + { + if (!DefaultSourceEditorOptions.Instance.EnableHighlightUsages) + return; + if (!Editor.IsSomethingSelected && markers.Any (m => m.Contains (Editor.CaretOffset))) + return; + RemoveMarkers (); + RemoveTimer (); + if (!Editor.IsSomethingSelected) + popupTimer = GLib.Timeout.Add (1000, () => { DelayedTooltipShow (); return false; } ); + } + + void ClearQuickTasks () + { + //UsagesSegments.Clear (); + if (usages.Count > 0) { + usages.Clear (); + OnUsagesUpdated (EventArgs.Empty); + } + } + + void CancelTooltip () + { + tooltipCancelSrc.Cancel (); + tooltipCancelSrc = new CancellationTokenSource (); + } + + void RemoveMarkers () + { + if (markers.Count == 0) + return; + // TextEditorData.Parent.TextViewMargin.AlphaBlendSearchResults = false; + foreach (var marker in markers) { + Editor.RemoveMarker (marker); + } + markers.Clear (); + } + + void ShowReferences (IEnumerable<MemberReference> references) + { + RemoveMarkers (); + var lineNumbers = new HashSet<int> (); + usages.Clear (); + var editor = Editor; + if (editor != null /*&& editor.TextViewMargin != null*/) { + if (references != null) { + foreach (var r in references) { + if (r == null) + continue; + var start = r.Offset; + var end = r.Offset + r.Length; + if (end > editor.Length) + continue; + var usage = new Usage (TextSegment.FromBounds (start, end), r.ReferenceUsageType); + usages.Add (usage); + var marker = TextMarkerFactory.CreateUsageMarker (editor, usage); + markers.Add (marker); + lineNumbers.Add (editor.OffsetToLineNumber (start)); + editor.AddMarker (marker); + } + } + } + OnUsagesUpdated (EventArgs.Empty); + } + + #region IUsageProvider implementation + + readonly List<Usage> usages = new List<Usage> (); + public override IEnumerable<Usage> Usages { + get { + return usages; + } + } + #endregion + } +} + diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/CompletionTextEditorExtension.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/CompletionTextEditorExtension.cs index 87562cd44b..2425d511de 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/CompletionTextEditorExtension.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/CompletionTextEditorExtension.cs @@ -33,143 +33,177 @@ using MonoDevelop.Components.Commands; using MonoDevelop.Ide.Commands; using MonoDevelop.Core; using MonoDevelop.Ide.CodeTemplates; -using ICSharpCode.NRefactory.Completion; +using MonoDevelop.Ide.Editor; +using System.Threading.Tasks; +using System.Threading; +using Gtk; -namespace MonoDevelop.Ide.Gui.Content +namespace MonoDevelop.Ide.Editor.Extension { - public class CompletionTextEditorExtension: TextEditorExtension + public class CompletionTextEditorExtension : TextEditorExtension { - CodeCompletionContext currentCompletionContext; - - bool autoHideCompletionWindow = true, autoHideParameterWindow = true; + protected CodeCompletionContext currentCompletionContext; + bool autoHideCompletionWindow = true, autoHideParameterWindow = true;
+
#region Completion related IDE -// public readonly static PropertyWrapper<bool> EnableCodeCompletion = PropertyService.Wrap ("EnableCodeCompletion", true); -// public readonly static PropertyWrapper<bool> EnableParameterInsight = PropertyService.Wrap ("EnableParameterInsight", true); + // public readonly static PropertyWrapper<bool> EnableCodeCompletion = PropertyService.Wrap ("EnableCodeCompletion", true);
+ // public readonly static PropertyWrapper<bool> EnableParameterInsight = PropertyService.Wrap ("EnableParameterInsight", true);
public readonly static PropertyWrapper<bool> EnableAutoCodeCompletion = PropertyService.Wrap ("EnableAutoCodeCompletion", true); public readonly static PropertyWrapper<bool> AddImportedItemsToCompletionList = PropertyService.Wrap ("AddImportedItemsToCompletionList", false); public readonly static PropertyWrapper<bool> IncludeKeywordsInCompletionList = PropertyService.Wrap ("IncludeKeywordsInCompletionList", true); public readonly static PropertyWrapper<bool> IncludeCodeSnippetsInCompletionList = PropertyService.Wrap ("IncludeCodeSnippetsInCompletionList", true); public readonly static PropertyWrapper<bool> AddParenthesesAfterCompletion = PropertyService.Wrap ("AddParenthesesAfterCompletion", false); public readonly static PropertyWrapper<bool> AddOpeningOnly = PropertyService.Wrap ("AddOpeningOnly", false); - public readonly static PropertyWrapper<int> CompletionListRows = PropertyService.Wrap ("CompletionListRows", 7); + public readonly static int CompletionListRows = 7; public readonly static PropertyWrapper<bool> FilterCompletionListByEditorBrowsable = PropertyService.Wrap ("FilterCompletionListByEditorBrowsable", true); - public readonly static PropertyWrapper<bool> IncludeEditorBrowsableAdvancedMembers = PropertyService.Wrap ("IncludeEditorBrowsableAdvancedMembers", true); - + public readonly static PropertyWrapper<bool> IncludeEditorBrowsableAdvancedMembers = PropertyService.Wrap ("IncludeEditorBrowsableAdvancedMembers", true);
+
#endregion - - public ICompletionWidget CompletionWidget { +
+ internal ICompletionWidget CompletionWidget + { get; set; } - public virtual string CompletionLanguage { - get { + public virtual string CompletionLanguage + { + get + { return "Other"; } } public void ShowCompletion (ICompletionDataList completionList) { - currentCompletionContext = CompletionWidget.CreateCodeCompletionContext (Document.Editor.Caret.Offset); + currentCompletionContext = CompletionWidget.CreateCodeCompletionContext (Editor.CaretOffset); int cpos, wlen; if (!GetCompletionCommandOffset (out cpos, out wlen)) { - cpos = Document.Editor.Caret.Offset; + cpos = Editor.CaretOffset; wlen = 0; } currentCompletionContext.TriggerOffset = cpos; currentCompletionContext.TriggerWordLength = wlen; - + CompletionWindowManager.ShowWindow (this, '\0', completionList, CompletionWidget, currentCompletionContext); } - - // When a key is pressed, and before the key is processed by the editor, this method will be invoked. - // Return true if the key press should be processed by the editor. - public override bool KeyPress (Gdk.Key key, char keyChar, Gdk.ModifierType modifier) + CancellationTokenSource completionTokenSrc = new CancellationTokenSource (); + CancellationTokenSource parameterHintingSrc = new CancellationTokenSource ();
+ // When a key is pressed, and before the key is processed by the editor, this method will be invoked.
+ // Return true if the key press should be processed by the editor.
+ public override bool KeyPress (KeyDescriptor descriptor) { bool res; if (currentCompletionContext != null) { - if (CompletionWindowManager.PreProcessKeyEvent (key, keyChar, modifier)) { - CompletionWindowManager.PostProcessKeyEvent (key, keyChar, modifier); - autoHideCompletionWindow = true; - // in named parameter case leave the parameter window open. - autoHideParameterWindow = keyChar != ':'; + if (CompletionWindowManager.PreProcessKeyEvent (descriptor)) { + CompletionWindowManager.PostProcessKeyEvent (descriptor); + autoHideCompletionWindow = true;
+ // in named parameter case leave the parameter window open.
+ autoHideParameterWindow = descriptor.KeyChar != ':'; if (!autoHideParameterWindow && ParameterInformationWindowManager.IsWindowVisible) - ParameterInformationWindowManager.PostProcessKeyEvent (this, CompletionWidget, key, modifier); - + ParameterInformationWindowManager.PostProcessKeyEvent (this, CompletionWidget, descriptor); + return false; } autoHideCompletionWindow = autoHideParameterWindow = false; } - + if (ParameterInformationWindowManager.IsWindowVisible) { - if (ParameterInformationWindowManager.ProcessKeyEvent (this, CompletionWidget, key, modifier)) + if (ParameterInformationWindowManager.ProcessKeyEvent (this, CompletionWidget, descriptor)) return false; autoHideCompletionWindow = autoHideParameterWindow = false; - } - - // int oldPos = Editor.CursorPosition; - // int oldLen = Editor.TextLength; - res = base.KeyPress (key, keyChar, modifier); - - CompletionWindowManager.PostProcessKeyEvent (key, keyChar, modifier); - - var ignoreMods = Gdk.ModifierType.ControlMask | Gdk.ModifierType.MetaMask - | Gdk.ModifierType.Mod1Mask | Gdk.ModifierType.SuperMask; - // Handle parameter completion + }
+
+ // int oldPos = Editor.CursorPosition;
+ // int oldLen = Editor.TextLength;
+ res = base.KeyPress (descriptor); + + CompletionWindowManager.PostProcessKeyEvent (descriptor); + + var ignoreMods = ModifierKeys.Control | ModifierKeys.Alt + | ModifierKeys.Command;
+ // Handle parameter completion
if (ParameterInformationWindowManager.IsWindowVisible) { - ParameterInformationWindowManager.PostProcessKeyEvent (this, CompletionWidget, key, modifier); + ParameterInformationWindowManager.PostProcessKeyEvent (this, CompletionWidget, descriptor); } - if ((modifier & ignoreMods) != 0) - return res; - - // don't complete on block selection - if (/*!EnableCodeCompletion ||*/ Document.Editor.SelectionMode == Mono.TextEditor.SelectionMode.Block) - return res; - + if ((descriptor.ModifierKeys & ignoreMods) != 0) + return res;
+
+ // don't complete on block selection
+ if (/*!EnableCodeCompletion ||*/ Editor.SelectionMode == MonoDevelop.Ide.Editor.SelectionMode.Block) + return res;
+
// Handle code completion - if (keyChar != '\0' && CompletionWidget != null && !CompletionWindowManager.IsVisible) { + if (descriptor.KeyChar != '\0' && CompletionWidget != null && !CompletionWindowManager.IsVisible) { currentCompletionContext = CompletionWidget.CurrentCodeCompletionContext; - int triggerWordLength = currentCompletionContext.TriggerWordLength; - ICompletionDataList completionList = HandleCodeCompletion (currentCompletionContext, keyChar, - ref triggerWordLength); - if (triggerWordLength > 0 && (triggerWordLength < Editor.Caret.Offset - || (triggerWordLength == 1 && Editor.Caret.Offset == 1))) { - currentCompletionContext - = CompletionWidget.CreateCodeCompletionContext (Editor.Caret.Offset - triggerWordLength); - currentCompletionContext.TriggerWordLength = triggerWordLength; - } - if (completionList != null) { - if (!CompletionWindowManager.ShowWindow (this, keyChar, completionList, CompletionWidget, currentCompletionContext)) + completionTokenSrc.Cancel (); + completionTokenSrc = new CancellationTokenSource (); + var token = completionTokenSrc.Token; + var caretOffset = Editor.CaretOffset; + try { + var task = HandleCodeCompletionAsync (currentCompletionContext, descriptor.KeyChar, token); + if (task != null) { + var result = task.Result; + if (result != null) { + int triggerWordLength = result.TriggerWordLength; + + if (triggerWordLength > 0 && (triggerWordLength < caretOffset + || (triggerWordLength == 1 && caretOffset == 1))) { + currentCompletionContext = CompletionWidget.CreateCodeCompletionContext (caretOffset - triggerWordLength); + currentCompletionContext.TriggerWordLength = triggerWordLength; + } + + if (!CompletionWindowManager.ShowWindow (this, descriptor.KeyChar, result, CompletionWidget, currentCompletionContext)) + currentCompletionContext = null; + } else { + currentCompletionContext = null; + } + } else { currentCompletionContext = null; - } else { + } + } catch (TaskCanceledException) { + currentCompletionContext = null; + } catch (AggregateException) { currentCompletionContext = null; } } - - if (/*EnableParameterInsight &&*/ CompletionWidget != null) { + + if (CompletionWidget != null) { CodeCompletionContext ctx = CompletionWidget.CurrentCodeCompletionContext; - var paramProvider = HandleParameterCompletion (ctx, keyChar); - if (paramProvider != null) - ParameterInformationWindowManager.ShowWindow (this, CompletionWidget, ctx, paramProvider); - } -/* autoHideCompletionWindow = true; - autoHideParameterWindow = keyChar != ':';*/ + parameterHintingSrc.Cancel (); + parameterHintingSrc = new CancellationTokenSource (); + var token = parameterHintingSrc.Token; + try { + var task = HandleParameterCompletionAsync (ctx, descriptor.KeyChar, token); + if (task != null) { + var result = task.Result; + if (result != null) { + ParameterInformationWindowManager.ShowWindow (this, CompletionWidget, ctx, result); + } + } + } catch (TaskCanceledException) { + } catch (AggregateException) { + } + + }
+ /* autoHideCompletionWindow = true; + autoHideParameterWindow = keyChar != ':';*/
return res; } - + protected void ShowCompletion (ICompletionDataList completionList, int triggerWordLength, char keyChar) { - if (Document.Editor.SelectionMode == Mono.TextEditor.SelectionMode.Block) + if (Editor.SelectionMode == SelectionMode.Block) return; if (CompletionWidget != null && currentCompletionContext == null) { currentCompletionContext = CompletionWidget.CurrentCodeCompletionContext; - if (triggerWordLength > 0 && triggerWordLength < Editor.Caret.Offset) { + if (triggerWordLength > 0 && triggerWordLength < Editor.CaretOffset) { currentCompletionContext = - CompletionWidget.CreateCodeCompletionContext (Editor.Caret.Offset - triggerWordLength); + CompletionWidget.CreateCodeCompletionContext (Editor.CaretOffset - triggerWordLength); currentCompletionContext.TriggerWordLength = triggerWordLength; } if (completionList != null) @@ -179,12 +213,12 @@ namespace MonoDevelop.Ide.Gui.Content } autoHideCompletionWindow = autoHideParameterWindow = true; } - + public virtual int GetCurrentParameterIndex (int startOffset) { return -1; } - + protected void OnCompletionContextChanged (object o, EventArgs a) { @@ -195,24 +229,24 @@ namespace MonoDevelop.Ide.Gui.Content ParameterInformationWindowManager.UpdateCursorPosition (this, CompletionWidget); } - [CommandUpdateHandler (TextEditorCommands.ShowCompletionWindow)] + [CommandUpdateHandler(TextEditorCommands.ShowCompletionWindow)] internal void OnUpdateCompletionCommand (CommandInfo info) { info.Bypass = !CanRunCompletionCommand () && !CompletionWindowManager.IsVisible; } - - [CommandUpdateHandler (TextEditorCommands.ShowParameterCompletionWindow)] + + [CommandUpdateHandler(TextEditorCommands.ShowParameterCompletionWindow)] internal void OnUpdateParameterCompletionCommand (CommandInfo info) { info.Bypass = !CanRunParameterCompletionCommand (); } - - [CommandHandler (TextEditorCommands.ShowCompletionWindow)] + + [CommandHandler(TextEditorCommands.ShowCompletionWindow)] public virtual void RunCompletionCommand () { - if (Document.Editor.SelectionMode == Mono.TextEditor.SelectionMode.Block) + if (Editor.SelectionMode == SelectionMode.Block) return; - + if (CompletionWindowManager.IsVisible) { CompletionWindowManager.Wnd.ToggleCategoryMode (); return; @@ -220,7 +254,7 @@ namespace MonoDevelop.Ide.Gui.Content ICompletionDataList completionList = null; int cpos, wlen; if (!GetCompletionCommandOffset (out cpos, out wlen)) { - cpos = Editor.Caret.Offset; + cpos = Editor.CaretOffset; wlen = 0; } currentCompletionContext = CompletionWidget.CreateCodeCompletionContext (cpos); @@ -231,20 +265,20 @@ namespace MonoDevelop.Ide.Gui.Content else currentCompletionContext = null; } - - [CommandHandler (TextEditorCommands.ShowCodeTemplateWindow)] + + [CommandHandler(TextEditorCommands.ShowCodeTemplateWindow)] public virtual void RunShowCodeTemplatesWindow () { ICompletionDataList completionList = null; int cpos, wlen; if (!GetCompletionCommandOffset (out cpos, out wlen)) { - cpos = Editor.Caret.Offset; + cpos = Editor.CaretOffset; wlen = 0; } - + var ctx = CompletionWidget.CreateCodeCompletionContext (cpos); ctx.TriggerWordLength = wlen; - completionList = Document.Editor.IsSomethingSelected ? ShowCodeSurroundingsCommand (ctx) : ShowCodeTemplatesCommand (ctx); + completionList = Editor.IsSomethingSelected ? ShowCodeSurroundingsCommand (ctx) : ShowCodeTemplatesCommand (ctx); if (completionList == null) { return; } @@ -256,74 +290,72 @@ namespace MonoDevelop.Ide.Gui.Content wnd.Extension = this; wnd.ShowListWindow ((char)0, completionList, CompletionWidget, ctx); } - - [CommandUpdateHandler (TextEditorCommands.ShowCodeTemplateWindow)] + + [CommandUpdateHandler(TextEditorCommands.ShowCodeTemplateWindow)] internal void OnUpdateShowCodeTemplatesWindow (CommandInfo info) { ICompletionDataList completionList = null; int cpos, wlen; if (!GetCompletionCommandOffset (out cpos, out wlen)) { - cpos = Editor.Caret.Offset; + cpos = Editor.CaretOffset; wlen = 0; } try { var ctx = CompletionWidget.CreateCodeCompletionContext (cpos); ctx.TriggerWordLength = wlen; - completionList = Document.Editor.IsSomethingSelected ? ShowCodeSurroundingsCommand (ctx) : ShowCodeTemplatesCommand (ctx); + completionList = Editor.IsSomethingSelected ? ShowCodeSurroundingsCommand (ctx) : ShowCodeTemplatesCommand (ctx); info.Bypass = completionList == null; - info.Text = Document.Editor.IsSomethingSelected ? GettextCatalog.GetString ("_Surround With...") : GettextCatalog.GetString ("I_nsert Template..."); + info.Text = Editor.IsSomethingSelected ? GettextCatalog.GetString ("_Surround With...") : GettextCatalog.GetString ("I_nsert Template..."); } catch (Exception e) { LoggingService.LogError ("Error while update show code templates window", e); info.Bypass = true; } } - - - [CommandHandler (TextEditorCommands.ShowParameterCompletionWindow)] + + + [CommandHandler(TextEditorCommands.ShowParameterCompletionWindow)] public virtual void RunParameterCompletionCommand () { - if (Document.Editor.SelectionMode == Mono.TextEditor.SelectionMode.Block || CompletionWidget == null) + if (Editor.SelectionMode == SelectionMode.Block || CompletionWidget == null) return; - ParameterDataProvider cp = null; - int cpos; - if (!GetParameterCompletionCommandOffset (out cpos)) - cpos = Editor.Caret.Offset; + ParameterHintingResult cp = null; + int cpos = Editor.CaretOffset; CodeCompletionContext ctx = CompletionWidget.CreateCodeCompletionContext (cpos); cp = ParameterCompletionCommand (ctx); if (cp != null) { ParameterInformationWindowManager.ShowWindow (this, CompletionWidget, ctx, cp); - ParameterInformationWindowManager.PostProcessKeyEvent (this, CompletionWidget, Gdk.Key.F, Gdk.ModifierType.None); + ParameterInformationWindowManager.PostProcessKeyEvent (this, CompletionWidget, KeyDescriptor.FromGtk (Gdk.Key.F, 'f', Gdk.ModifierType.None)); } } - + public virtual bool CanRunCompletionCommand () { return (CompletionWidget != null && currentCompletionContext == null); } - + public virtual bool CanRunParameterCompletionCommand () { return (CompletionWidget != null && !ParameterInformationWindowManager.IsWindowVisible); } - - - public virtual ICompletionDataList HandleCodeCompletion (CodeCompletionContext completionContext, - char completionChar, ref int triggerWordLength) + + static readonly ICompletionDataList emptyList = new CompletionDataList (); + + public virtual Task<ICompletionDataList> HandleCodeCompletionAsync (CodeCompletionContext completionContext, char completionChar, CancellationToken token = default(CancellationToken)) { - return null; + return Task.FromResult (emptyList); } - - public virtual ParameterDataProvider HandleParameterCompletion (CodeCompletionContext completionContext, char completionChar) + + public virtual Task<ParameterHintingResult> HandleParameterCompletionAsync (CodeCompletionContext completionContext, char completionChar, CancellationToken token = default(CancellationToken)) { - return null; - } - - // return false if completion can't be shown + return Task.FromResult (ParameterHintingResult.Empty); + }
+
+ // return false if completion can't be shown
public virtual bool GetCompletionCommandOffset (out int cpos, out int wlen) { cpos = wlen = 0; - int pos = Editor.Caret.Offset - 1; + int pos = Editor.CaretOffset - 1; while (pos >= 0) { char c = Editor.GetCharAt (pos); if (!char.IsLetterOrDigit (c) && c != '_') @@ -332,11 +364,11 @@ namespace MonoDevelop.Ide.Gui.Content } if (pos == -1) return false; - + pos++; cpos = pos; int len = Editor.Length; - + while (pos < len) { char c = Editor.GetCharAt (pos); if (!char.IsLetterOrDigit (c) && c != '_') @@ -347,45 +379,40 @@ namespace MonoDevelop.Ide.Gui.Content return true; } - public virtual bool GetParameterCompletionCommandOffset (out int cpos) - { - cpos = 0; - return false; - } - + public virtual ICompletionDataList ShowCodeSurroundingsCommand (CodeCompletionContext completionContext) { CompletionDataList list = new CompletionDataList (); list.AutoSelect = true; list.AutoCompleteEmptyMatch = true; list.CompletionSelectionMode = CompletionSelectionMode.OwnTextField; - var templateWidget = Document.GetContent<ICodeTemplateContextProvider> (); + var templateWidget = DocumentContext.GetContent<ICodeTemplateContextProvider> (); CodeTemplateContext ctx = CodeTemplateContext.Standard; if (templateWidget != null) ctx = templateWidget.GetCodeTemplateContext (); - foreach (CodeTemplate template in CodeTemplateService.GetCodeTemplatesForFile (Document.FileName)) { - if ((template.CodeTemplateType & CodeTemplateType.SurroundsWith) == CodeTemplateType.SurroundsWith) { + foreach (CodeTemplate template in CodeTemplateService.GetCodeTemplatesForFile (DocumentContext.Name)) { + if ((template.CodeTemplateType & CodeTemplateType.SurroundsWith) == CodeTemplateType.SurroundsWith) { if (ctx == template.CodeTemplateContext) - list.Add (new CodeTemplateCompletionData (Document, template)); + list.Add (new CodeTemplateCompletionData (this, template)); } } return list; } - + public virtual ICompletionDataList ShowCodeTemplatesCommand (CodeCompletionContext completionContext) { CompletionDataList list = new CompletionDataList (); list.AutoSelect = true; list.AutoCompleteEmptyMatch = true; list.CompletionSelectionMode = CompletionSelectionMode.OwnTextField; - foreach (CodeTemplate template in CodeTemplateService.GetCodeTemplatesForFile (Document.FileName)) { - if (template.CodeTemplateType != CodeTemplateType.SurroundsWith) { - list.Add (new CodeTemplateCompletionData (Document, template)); + foreach (CodeTemplate template in CodeTemplateService.GetCodeTemplatesForFile (DocumentContext.Name)) { + if (template.CodeTemplateType != CodeTemplateType.SurroundsWith) { + list.Add (new CodeTemplateCompletionData (this, template)); } } return list; } - + const int CompletionTimeoutInMs = 500; public virtual ICompletionDataList CodeCompletionCommand (CodeCompletionContext completionContext) { @@ -395,15 +422,25 @@ namespace MonoDevelop.Ide.Gui.Content int pos = completionContext.TriggerOffset; if (pos > 0) { char ch = Editor.GetCharAt (pos - 1); - int triggerWordLength = completionContext.TriggerWordLength; - ICompletionDataList completionList = HandleCodeCompletion (completionContext, ch, ref triggerWordLength); - if (completionList != null) - return completionList; + var csc = new CancellationTokenSource (CompletionTimeoutInMs); + try { + var task = HandleCodeCompletionAsync (completionContext, ch, csc.Token); + if (task == null) + return null; + task.Wait (csc.Token); + if (!task.IsCompleted) + return null; + var completionList = task.Result; + if (completionList != null) + return completionList; + } catch (TaskCanceledException) { + } catch (AggregateException) { + } } return null; } - public virtual ParameterDataProvider ParameterCompletionCommand (CodeCompletionContext completionContext) + public virtual ParameterHintingResult ParameterCompletionCommand (CodeCompletionContext completionContext) { // This default implementation of ParameterCompletionCommand calls HandleParameterCompletion providing // the char at the cursor position. If it returns a provider, just return it. @@ -411,22 +448,34 @@ namespace MonoDevelop.Ide.Gui.Content int pos = completionContext.TriggerOffset; if (pos <= 0) return null; - var cp = HandleParameterCompletion (completionContext, Editor.Document.GetCharAt (pos - 1)); - if (cp != null) - return cp; + var csc = new CancellationTokenSource (CompletionTimeoutInMs); + try { + var task = HandleParameterCompletionAsync (completionContext, Editor.GetCharAt (pos - 1), csc.Token); + if (task == null) + return null; + task.Wait (csc.Token); + if (!task.IsCompleted) + return null; + var cp = task.Result; + if (cp != null) + return cp; + } catch (TaskCanceledException) { + } catch (AggregateException) { + } return null; } - public virtual int GuessBestMethodOverload (IParameterDataProvider provider, int currentOverload) + public virtual int GuessBestMethodOverload (ParameterHintingResult provider, int currentOverload) { int cparam = GetCurrentParameterIndex (provider.StartOffset); - if (cparam > provider.GetParameterCount (currentOverload) && !provider.AllowParameterList (currentOverload)) { + var currentHintingData = provider [currentOverload]; + if (cparam > currentHintingData.ParameterCount && !currentHintingData.IsParameterListAllowed) { // Look for an overload which has more parameters int bestOverload = -1; int bestParamCount = int.MaxValue; for (int n=0; n<provider.Count; n++) { - int pc = provider.GetParameterCount (n); + int pc = provider[n].ParameterCount; if (pc < bestParamCount && pc >= cparam) { bestOverload = n; bestParamCount = pc; @@ -434,7 +483,7 @@ namespace MonoDevelop.Ide.Gui.Content } if (bestOverload == -1) { for (int n=0; n<provider.Count; n++) { - if (provider.AllowParameterList (n)) { + if (provider[n].IsParameterListAllowed) { bestOverload = n; break; } @@ -445,32 +494,37 @@ namespace MonoDevelop.Ide.Gui.Content return -1; } - void HandlePaste (int insertionOffset, string text, int insertedChars) - { - ParameterInformationWindowManager.HideWindow (this, CompletionWidget); - CompletionWindowManager.HideWindow (); - } - - void HandleFocusOutEvent (object o, Gtk.FocusOutEventArgs args) - { - ParameterInformationWindowManager.HideWindow (this, CompletionWidget); - CompletionWindowManager.HideWindow (); - } +// void HandlePaste (int insertionOffset, string text, int insertedChars) +// { +// ParameterInformationWindowManager.HideWindow (this, CompletionWidget); +// CompletionWindowManager.HideWindow (); +// } +// +// void HandleFocusOutEvent (object o, Gtk.FocusOutEventArgs args) +// { +// ParameterInformationWindowManager.HideWindow (this, CompletionWidget); +// CompletionWindowManager.HideWindow (); +// } - public override void Initialize () + protected override void Initialize () { base.Initialize (); CompletionWindowManager.WindowClosed += HandleWindowClosed; - CompletionWidget = Document.GetContent <ICompletionWidget> (); + CompletionWidget = DocumentContext.GetContent <ICompletionWidget> (); if (CompletionWidget != null) CompletionWidget.CompletionContextChanged += OnCompletionContextChanged; - document.Editor.Caret.PositionChanged += HandlePositionChanged; - document.Editor.Paste += HandlePaste; - if (document.Editor.Parent != null) - document.Editor.Parent.TextArea.FocusOutEvent += HandleFocusOutEvent; + Editor.CaretPositionChanged += HandlePositionChanged; +// document.Editor.Paste += HandlePaste; +// if (document.Editor.Parent != null) +// document.Editor.Parent.TextArea.FocusOutEvent += HandleFocusOutEvent; + } + + internal void InternalInitialize () + { + Initialize (); } - void HandlePositionChanged (object sender, Mono.TextEditor.DocumentLocationEventArgs e) + void HandlePositionChanged (object sender, EventArgs e) { CompletionWindowManager.UpdateCursorPosition (); } @@ -488,10 +542,10 @@ namespace MonoDevelop.Ide.Gui.Content ParameterInformationWindowManager.HideWindow (this, CompletionWidget); disposed = true; - if (document.Editor.Parent != null) - document.Editor.Parent.TextArea.FocusOutEvent -= HandleFocusOutEvent; - document.Editor.Paste -= HandlePaste; - document.Editor.Caret.PositionChanged -= HandlePositionChanged; +// if (document.Editor.Parent != null) +// document.Editor.Parent.TextArea.FocusOutEvent -= HandleFocusOutEvent; +// document.Editor.Paste -= HandlePaste; + Editor.CaretPositionChanged -= HandlePositionChanged; CompletionWindowManager.WindowClosed -= HandleWindowClosed; if (CompletionWidget != null) CompletionWidget.CompletionContextChanged -= OnCompletionContextChanged; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ParsedFileEventArgs.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/IQuickTaskProvider.cs index 50300be800..62a4b236d4 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ParsedFileEventArgs.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/IQuickTaskProvider.cs @@ -1,10 +1,10 @@ // -// ParsedFileEventArgs.cs +// IQuickTaskProvider.cs // // Author: -// Mike Krüger <mkrueger@novell.com> +// Mike Krüger <mkrueger@xamarin.com> // -// Copyright (c) 2011 Mike Krüger <mkrueger@novell.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 @@ -25,20 +25,16 @@ // THE SOFTWARE. using System; -using ICSharpCode.NRefactory.TypeSystem; +using System.Collections.Generic; -namespace MonoDevelop.Ide.TypeSystem +namespace MonoDevelop.Ide.Editor.Extension { - public class ParsedFileEventArgs : EventArgs + public interface IQuickTaskProvider { - public IUnresolvedFile File { + IEnumerable<QuickTask> QuickTasks { get; - private set; - } - - public ParsedFileEventArgs (IUnresolvedFile file) - { - this.File = file; } + + event EventHandler TasksUpdated; } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/IndentationTracker.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/IndentationTracker.cs new file mode 100644 index 0000000000..25e47fa9ad --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/IndentationTracker.cs @@ -0,0 +1,40 @@ +// +// IndentationTracker.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 MonoDevelop.Core.Text; + +namespace MonoDevelop.Ide.Editor.Extension +{ + /// <summary> + /// The indentation tracker is for giving the editor information about virtual line indentations. + /// </summary> + public abstract class IndentationTracker + { + /// <summary> + /// Gets the indentation string for a given line. + /// </summary> + public abstract string GetIndentationString (int lineNumber); + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/KeyDescriptor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/KeyDescriptor.cs new file mode 100644 index 0000000000..743364b33b --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/KeyDescriptor.cs @@ -0,0 +1,222 @@ +// +// KeyDescriptor.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; +#if MAC +using AppKit; +#endif + +namespace MonoDevelop.Ide.Editor.Extension +{ + public struct KeyDescriptor + { + public static KeyDescriptor Empty = new KeyDescriptor (SpecialKey.None, '\0', ModifierKeys.None); + + public readonly SpecialKey SpecialKey; + public readonly char KeyChar; + public readonly ModifierKeys ModifierKeys; + + KeyDescriptor (SpecialKey specialKey, char keyChar, ModifierKeys modifierKeys) + { + SpecialKey = specialKey; + KeyChar = keyChar; + ModifierKeys = modifierKeys; + } + + public override string ToString () + { + return string.Format ("[KeyDescriptor: SpecialKey={0}, KeyChar={1}, ModifierKeys={2}]", SpecialKey, KeyChar, ModifierKeys); + } + + #region GTK + public static KeyDescriptor FromGtk (Gdk.Key key, char ch, Gdk.ModifierType state) + { + return new KeyDescriptor (ConvertKey (key), ch, ConvertModifiers (state)); + } + + static SpecialKey ConvertKey (Gdk.Key key) + { + switch (key) { + case Gdk.Key.BackSpace: + return SpecialKey.BackSpace; + case Gdk.Key.Tab: + case Gdk.Key.KP_Tab: + case Gdk.Key.ISO_Left_Tab: + return SpecialKey.Tab; + case Gdk.Key.Return: + case Gdk.Key.KP_Enter: + case Gdk.Key.ISO_Enter: + return SpecialKey.Return; + case Gdk.Key.Escape: + return SpecialKey.Escape; + case Gdk.Key.space: + case Gdk.Key.KP_Space: + return SpecialKey.Space; + case Gdk.Key.Page_Up: + case Gdk.Key.KP_Page_Up: + return SpecialKey.PageUp; + case Gdk.Key.Page_Down: + case Gdk.Key.KP_Page_Down: + return SpecialKey.PageDown; + case Gdk.Key.End: + case Gdk.Key.KP_End: + return SpecialKey.End; + case Gdk.Key.Begin: + case Gdk.Key.KP_Begin: + return SpecialKey.Begin; + case Gdk.Key.Home: + case Gdk.Key.KP_Home: + return SpecialKey.Home; + case Gdk.Key.Left: + case Gdk.Key.KP_Left: + return SpecialKey.Left; + case Gdk.Key.Up: + case Gdk.Key.KP_Up: + return SpecialKey.Up; + case Gdk.Key.Right: + case Gdk.Key.KP_Right: + return SpecialKey.Right; + case Gdk.Key.Down: + case Gdk.Key.KP_Down: + return SpecialKey.Down; + case Gdk.Key.Delete: + case Gdk.Key.KP_Delete: + return SpecialKey.Delete; + } + return SpecialKey.None; + } + + static ModifierKeys ConvertModifiers (Gdk.ModifierType s) + { + ModifierKeys m = ModifierKeys.None; + if ((s & Gdk.ModifierType.ShiftMask) != 0) + m |= ModifierKeys.Shift; + if ((s & Gdk.ModifierType.ControlMask) != 0) + m |= ModifierKeys.Control; + if ((s & Gdk.ModifierType.Mod1Mask) != 0) + m |= ModifierKeys.Alt; + if ((s & Gdk.ModifierType.Mod2Mask) != 0) + m |= ModifierKeys.Command; + return m; + } + #endregion + + #if MAC + + public static KeyDescriptor FromMac (char ch, NSEventModifierMask state) + { + var specialKey = ConvertKey (ref ch); + var keyDescriptor = new KeyDescriptor (specialKey, ch, ConvertModifiers (state)); + return keyDescriptor; + } + + static SpecialKey ConvertKey (ref char ch) + { + if (ch == '\n' || ch == '\r') { + ch = '\0'; + return SpecialKey.Return; + } + if (ch == '\t') { + ch = '\0'; + return SpecialKey.Tab; + } + if (ch == '\b' || ch == (char)127) { + ch = '\0'; + return SpecialKey.BackSpace; + } + if (ch == (char)27) { + ch = '\0'; + return SpecialKey.Escape; + } + switch ((NSKey)ch) { + case NSKey.Delete: + ch = '\0'; + return SpecialKey.BackSpace; + case NSKey.Tab: + ch = '\0'; + return SpecialKey.Tab; + case NSKey.Return: + case NSKey.KeypadEnter: + ch = '\0'; + return SpecialKey.Return; + case NSKey.Escape: + ch = '\0'; + return SpecialKey.Escape; + case NSKey.Space: + ch = '\0'; + return SpecialKey.Space; + case NSKey.PageUp: + ch = '\0'; + return SpecialKey.PageUp; + case NSKey.PageDown: + ch = '\0'; + return SpecialKey.PageDown; + case NSKey.End: + ch = '\0'; + return SpecialKey.End; + case NSKey.Begin: + ch = '\0'; + return SpecialKey.Begin; + case NSKey.Home: + ch = '\0'; + return SpecialKey.Home; + case NSKey.LeftArrow: + ch = '\0'; + return SpecialKey.Left; + case NSKey.UpArrow: + ch = '\0'; + return SpecialKey.Up; + case NSKey.RightArrow: + ch = '\0'; + return SpecialKey.Right; + case NSKey.DownArrow: + ch = '\0'; + return SpecialKey.Down; + case NSKey.DeleteChar: + ch = '\0'; + return SpecialKey.Delete; + } + return SpecialKey.None; + } + + + + static ModifierKeys ConvertModifiers (NSEventModifierMask e) + { + var m = ModifierKeys.None; + if ((e & NSEventModifierMask.ControlKeyMask) != 0) + m |= ModifierKeys.Control; + if ((e & NSEventModifierMask.AlternateKeyMask) != 0) + m |= ModifierKeys.Alt; + if ((e & NSEventModifierMask.CommandKeyMask) != 0) + m |= ModifierKeys.Command; + if ((e & NSEventModifierMask.ShiftKeyMask) != 0) + m |= ModifierKeys.Shift; + return m; + } + #endif + } +} + diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/ModifierKeys.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/ModifierKeys.cs new file mode 100644 index 0000000000..4b902cdf0f --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/ModifierKeys.cs @@ -0,0 +1,40 @@ +// +// ModifierKeys.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.Extension +{ + [Flags] + public enum ModifierKeys + { + None = 0, + Alt = 0x1 << 0, + Control = 0x1 << 1, + Shift = 0x1 << 2, + Command = 0x1 << 3 + } +} + diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/QuickTask.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/QuickTask.cs new file mode 100644 index 0000000000..96162525b1 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/QuickTask.cs @@ -0,0 +1,76 @@ +// +// QuickTask.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 Gtk; +using System.Collections.Generic; +using Gdk; +using MonoDevelop.Core; +using MonoDevelop.Ide; +using MonoDevelop.Components.Commands; +using Microsoft.CodeAnalysis; + +namespace MonoDevelop.Ide.Editor.Extension +{ + public sealed class QuickTask + { + Lazy<string> description; + public string Description { + get { + return description.Value; + } + } + + public int Location { + get; + private set; + } + + public DiagnosticSeverity Severity { + get; + private set; + } + + public QuickTask (Func<string> descriptionFunc, int location, DiagnosticSeverity severity) + { + this.description = new Lazy<string> (descriptionFunc); + this.Location = location; + this.Severity = severity; + } + + public QuickTask (string description, int location, DiagnosticSeverity severity) + { + this.description = new Lazy<string> (() => description); + this.Location = location; + this.Severity = severity; + } + + public override string ToString () + { + return string.Format ("[QuickTask: Description={0}, Location={1}, Severity={2}]", Description, Location, Severity); + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/SelectionSurroundingProvider.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/SelectionSurroundingProvider.cs new file mode 100644 index 0000000000..903e05a6ba --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/SelectionSurroundingProvider.cs @@ -0,0 +1,57 @@ +// +// ISelectionSurroundingProvider.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.Components.PropertyGrid.PropertyEditors; + +namespace MonoDevelop.Ide.Editor.Extension +{ + /// <summary> + /// A selection surrounding provider handles a special handling how the text editor behaves when the user + /// types a key with a selection. The selection can be surrounded instead of beeing replaced. + /// </summary> + public abstract class SelectionSurroundingProvider + { + /// <summary> + /// Gets the selection surroundings for a given unicode key. + /// </summary> + /// <returns> + /// true, if the key is valid for a surrounding action. + /// </returns> + /// <param name='unicodeKey'> + /// The key to handle. + /// </param> + /// <param name='start'> + /// The start of the surrounding + /// </param> + /// <param name='end'> + /// The end of the surrounding + /// </param> + public abstract bool GetSelectionSurroundings (uint unicodeKey, out string start, out string end); + + public abstract void HandleSpecialSelectionKey (uint unicodeKey); + } +} + diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/SpecialKey.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/SpecialKey.cs new file mode 100644 index 0000000000..5157fec2b5 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/SpecialKey.cs @@ -0,0 +1,49 @@ +// +// SpecialKey.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.Extension +{ + public enum SpecialKey + { + None, + BackSpace = 0xff08, + Tab = 0xff09, + Return = 0xff0d, + Escape = 0xff1b, + Space = 0x20, + PageUp = 0xff55, + PageDown = 0xff56, + End = 0xff57, + Begin = 0xff58, + Home = 0xff50, + Left = 0xff51, + Up = 0xff52, + Right = 0xff53, + Down = 0xff54, + Delete = 0xffff + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/TextEditorExtension.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/TextEditorExtension.cs new file mode 100644 index 0000000000..6dcb523e19 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/TextEditorExtension.cs @@ -0,0 +1,107 @@ +// +// AbstractEditorExtension.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.Components.Commands; +using MonoDevelop.Core; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Ide.Gui; +using MonoDevelop.Ide.TypeSystem; + +namespace MonoDevelop.Ide.Editor.Extension +{ + public abstract class TextEditorExtension : ICommandRouter, IDisposable + { + public DocumentContext DocumentContext { + get; + protected set; + } + + public TextEditor Editor { + get; + protected set; + } + + internal TextEditorExtension Next { + get; + set; + } + + protected internal void Initialize (TextEditor editor, DocumentContext context) + { + if (editor == null) + throw new ArgumentNullException ("editor"); + if (context == null) + throw new ArgumentNullException ("context"); + if (DocumentContext != null) + throw new InvalidOperationException ("Extension is already initialized."); + DocumentContext = context; + Editor = editor; + Initialize (); + } + + protected virtual void Initialize () + { + } + + public virtual bool IsValidInContext (DocumentContext context) + { + return true; + } + + /// <summary> + /// Return true if the key press should be processed by the editor. + /// When a key is pressed, and before the key is processed by the editor, this method will be invoked. + /// </summary> + public virtual bool KeyPress (KeyDescriptor descriptor) + { + return Next == null || Next.KeyPress (descriptor); + } + + public virtual void Dispose () + { + } + + void CheckInitialized () + { + if (DocumentContext == null) + throw new InvalidOperationException ("Editor extension not yet initialized"); + } + + object ICommandRouter.GetNextCommandTarget () + { + return Next; + } + } + + class TextEditorExtensionMarker : TextEditorExtension + { + public override bool IsValidInContext (DocumentContext context) + { + return false; + } + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/TextPasteHandler.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/TextPasteHandler.cs new file mode 100644 index 0000000000..ee3518ecc0 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/TextPasteHandler.cs @@ -0,0 +1,64 @@ +// +// TextPasteHandler.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.Extension +{ + /// <summary> + /// The text paste handler can do formattings to a text that is about to be pasted + /// into the text document. + /// </summary> + public abstract class TextPasteHandler + { + /// <summary> + /// Formats plain text that is inserted at a specified offset. + /// </summary> + /// <returns> + /// The text that will get inserted at that position. + /// </returns> + /// <param name="offset">The offset where the text will be inserted.</param> + /// <param name="text">The text to be inserted.</param> + /// <param name="copyData">Additional data in case the text was copied from a Mono.TextEditor.</param> + public abstract string FormatPlainText(int offset, string text, byte[] copyData); + + /// <summary> + /// Gets the copy data for a specific segment inside the document. This can contain meta data about the text pasted. + /// For example 'text pasted from string'. + /// </summary> + /// <param name = "offset">The copy offset.</param> + /// <param name = "length">The length of the copied text.</param> + public abstract byte[] GetCopyData(int offset, int length); + + /// <summary> + /// This is called after text was pasted. This is useful for creating an additional undo step for the paste command. + /// </summary> + /// <param name="offset">The offset the text was pasted at.</param> + /// <param name="length">The length of the text pasted.</param> + public abstract void PostFomatPastedText (int offset, int length); + } +} + diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/Usage.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/Usage.cs new file mode 100644 index 0000000000..ea8dade4d3 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/Usage.cs @@ -0,0 +1,49 @@ +// +// IUsageProvider.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 System.Collections.Generic; +using ICSharpCode.NRefactory; +using MonoDevelop.Core.Text; +using MonoDevelop.Ide.FindInFiles; + +namespace MonoDevelop.Ide.Editor.Extension +{ + public sealed class Usage : AbstractSegment + { + public readonly ReferenceUsageType UsageType; + + public Usage (TextSegment segment, ReferenceUsageType usageType) : base (segment.Offset, segment.Length) + { + UsageType = usageType; + } + + public Usage (int offset, int length, ReferenceUsageType usageType) : base (offset, length) + { + UsageType = usageType; + } + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/AmbientColor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/AmbientColor.cs new file mode 100644 index 0000000000..b65a66a3de --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/AmbientColor.cs @@ -0,0 +1,188 @@ +// +// AmbientColor.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.IO; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; +using System.Xml.XPath; +using System.Reflection; +using MonoDevelop.Components; + +namespace MonoDevelop.Ide.Editor.Highlighting +{ + public sealed class AmbientColor + { + public string Name { get; private set; } + public readonly List<Tuple<string, HslColor>> Colors = new List<Tuple<string, HslColor>> (); + + public HslColor Color { + get { + return GetColor ("color"); + } + set { + for (int i = 0; i < Colors.Count; i++) { + var t = Colors [i]; + if (t.Item1 == "color") { + Colors [i] = Tuple.Create ("color", value); + return; + } + } + Colors.Add (Tuple.Create ("color", value)); + } + } + + public HslColor SecondColor { + get { + return GetColor ("secondcolor"); + } + set { + for (int i = 0; i < Colors.Count; i++) { + var t = Colors [i]; + if (t.Item1 == "secondcolor") { + Colors [i] = Tuple.Create ("secondcolor", value); + return; + } + } + Colors.Add (Tuple.Create ("secondcolor", value)); + } + } + + public bool HasSecondColor { + get { + return Colors.Any (c => c.Item1 == "secondcolor"); + } + } + + public HslColor BorderColor { + get { + return GetColor ("bordercolor"); + } + set { + for (int i = 0; i < Colors.Count; i++) { + var t = Colors [i]; + if (t.Item1 == "bordercolor") { + Colors [i] = Tuple.Create ("bordercolor", value); + return; + } + } + Colors.Add (Tuple.Create ("bordercolor", value)); + } + } + + public bool HasBorderColor { + get { + return Colors.Any (c => c.Item1 == "bordercolor"); + } + } + + public HslColor GetColor (string name) + { + foreach (var color in Colors) { + if (color.Item1 == name) + return color.Item2; + } + + return new HslColor (0, 0, 0); + } + + public static AmbientColor Create (XElement element, Dictionary<string, HslColor> palette) + { + var result = new AmbientColor (); + foreach (var node in element.DescendantNodes ()) { + if (node.NodeType == System.Xml.XmlNodeType.Element) { + var el = (XElement)node; + switch (el.Name.LocalName) { + case "name": + result.Name = el.Value; + break; + default: + result.Colors.Add (Tuple.Create (el.Name.LocalName, ColorScheme.ParsePaletteColor (palette, el.Value))); + break; + } + } + } + + return result; + } + + public override bool Equals (object obj) + { + if (obj == null) + return false; + if (ReferenceEquals (this, obj)) + return true; + if (obj.GetType () != typeof(AmbientColor)) + return false; + AmbientColor other = (AmbientColor)obj; + return Colors.Equals (other.Colors) && Name == other.Name; + } + + public override int GetHashCode () + { + unchecked { + return (Colors != null ? Colors.GetHashCode () : 0) ^ (Name != null ? Name.GetHashCode () : 0); + } + } + + + public static AmbientColor Import (Dictionary<string, ColorScheme.VSSettingColor> colors, string vsSetting) + { + var result = new AmbientColor (); + var attrs = vsSetting.Split (','); + foreach (var attr in attrs) { + var info = attr.Split ('='); + if (info.Length != 2) + continue; + var idx = info [1].LastIndexOf ('/'); + var source = info [1].Substring (0, idx); + var dest = info [1].Substring (idx + 1); + + ColorScheme.VSSettingColor color; + if (!colors.TryGetValue (source, out color)) + continue; + result.Name = color.Name; + string colorString; + switch (dest) { + case "Foreground": + colorString = color.Foreground; + break; + case "Background": + colorString = color.Background; + break; + default: + throw new InvalidDataException ("Invalid attribute source: " + dest); + } + result.Colors.Add (Tuple.Create (info [0], ColorScheme.ImportVsColor (colorString))); + } + if (result.Colors.Count == 0) + return null; + return result; + } + } + +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/ChunkStyle.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/ChunkStyle.cs new file mode 100644 index 0000000000..bed02ad91d --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/ChunkStyle.cs @@ -0,0 +1,186 @@ +// +// ChunkStyle.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.IO; +using System.Collections.Generic; +using System.Xml.Linq; +using MonoDevelop.Components; + +namespace MonoDevelop.Ide.Editor.Highlighting +{ + public sealed class ChunkStyle + { + public string Name { get; set; } + public HslColor Foreground { get; set; } + public HslColor Background { get; set; } + + public bool TransparentForeground { + get { + return Foreground.Alpha == 0.0; + + } + } + public bool TransparentBackground { + get { + return Background.Alpha == 0.0; + } + } + + public Xwt.Drawing.FontWeight FontWeight { get; set; } + + public Xwt.Drawing.FontStyle FontStyle { get; set; } + + [Obsolete("Will be removed - use FontWeight")] + public bool Bold { + get { + return FontWeight == Xwt.Drawing.FontWeight.Bold; + } + } + + [Obsolete("Will be removed - use FontStyle")] + public bool Italic { + get { + return FontStyle == Xwt.Drawing.FontStyle.Italic; + } + } + + public bool Underline { + get; set; + } + + public ChunkStyle () + { + Foreground = Background = new HslColor (0, 0, 0, 0); + FontWeight = Xwt.Drawing.FontWeight.Normal; + FontStyle = Xwt.Drawing.FontStyle.Normal; + } + + public ChunkStyle (ChunkStyle baseStyle) + { + this.Name = baseStyle.Name; + this.Foreground = baseStyle.Foreground; + this.Background = baseStyle.Background; + this.FontWeight = baseStyle.FontWeight; + this.FontStyle = baseStyle.FontStyle; + this.Underline = baseStyle.Underline; + } + + public override bool Equals (object obj) + { + if (obj == null) + return false; + if (ReferenceEquals (this, obj)) + return true; + if (obj.GetType () != typeof(ChunkStyle)) + return false; + ChunkStyle other = (ChunkStyle)obj; + return Name == other.Name && Foreground.Equals (other.Foreground) && Background.Equals (other.Background) && FontWeight == other.FontWeight && FontStyle == other.FontStyle; + } + + public override int GetHashCode () + { + unchecked { + return (Name != null ? Name.GetHashCode () : 0) ^ Foreground.GetHashCode () ^ Background.GetHashCode () ^ FontWeight.GetHashCode () ^ FontStyle.GetHashCode (); + } + } + + public static ChunkStyle Create (XElement element, Dictionary<string, HslColor> palette) + { + var result = new ChunkStyle (); + + foreach (var node in element.DescendantNodes ()) { + if (node.NodeType == System.Xml.XmlNodeType.Element) { + var el = (XElement)node; + switch (el.Name.LocalName) { + case "name": + result.Name = el.Value; + break; + case "fore": + result.Foreground = ColorScheme.ParsePaletteColor (palette, el.Value); + break; + case "back": + result.Background = ColorScheme.ParsePaletteColor (palette, el.Value); + break; + case "weight": + Xwt.Drawing.FontWeight weight; + if (!Enum.TryParse<Xwt.Drawing.FontWeight> (el.Value, true, out weight)) + throw new InvalidDataException (el.Value + " is no valid text weight values are: " + string.Join (",", Enum.GetNames (typeof(Xwt.Drawing.FontWeight))) ); + result.FontWeight = weight; + break; + case "style": + Xwt.Drawing.FontStyle style; + if (!Enum.TryParse<Xwt.Drawing.FontStyle> (el.Value, true, out style)) + throw new InvalidDataException (el.Value + " is no valid text weight values are: " + string.Join (",", Enum.GetNames (typeof(Xwt.Drawing.FontStyle))) ); + result.FontStyle = style; + break; + default: + throw new InvalidDataException ("Invalid element in text color:" + el.Name); + } + } + } + + return result; + } + + public Gdk.GC CreateBgGC (Gdk.Drawable drawable) + { + return new Gdk.GC (drawable) { RgbBgColor = (HslColor)Foreground, RgbFgColor = (HslColor)Background }; + } + + public Gdk.GC CreateFgGC (Gdk.Drawable drawable) + { + return new Gdk.GC (drawable) { RgbBgColor = (HslColor)Background, RgbFgColor = (HslColor)Foreground }; + } + + public override string ToString () + { + return string.Format ("[ChunkStyle: Name={0}, CairoColor={1}, CairoBackgroundColor={2}, FontWeight={3}, FontStyle={4}]", Name, Foreground, Background, FontWeight, FontStyle); + } + + public static ChunkStyle Import (string name, ColorScheme.VSSettingColor vsc) + { + var textColor = new ChunkStyle (); + textColor.Name = name; + if (!string.IsNullOrEmpty (vsc.Foreground) && vsc.Foreground != "0x02000000") { + textColor.Foreground = ColorScheme.ImportVsColor (vsc.Foreground); + if (textColor.TransparentForeground && name != "Selected Text" && name != "Selected Text(Inactive)") + textColor.Foreground = new HslColor (0, 0, 0); + } + if (!string.IsNullOrEmpty (vsc.Background) && vsc.Background != "0x02000000") + textColor.Background = ColorScheme.ImportVsColor (vsc.Background); + if (vsc.BoldFont) + textColor.FontWeight = Xwt.Drawing.FontWeight.Bold; + return textColor; + } + + public ChunkStyle Clone () + { + return (ChunkStyle)this.MemberwiseClone (); + } + } + +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/PreProcessorDefine.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/ColorDescriptionAttribute.cs index 45f3b48a2d..d69e2013c9 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/PreProcessorDefine.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/ColorDescriptionAttribute.cs @@ -1,9 +1,10 @@ -// PreProcessorDefine.cs +// +// ColorDescriptionAttribute.cs // // Author: -// Mike Krüger <mkrueger@novell.com> +// Mike Krüger <mkrueger@xamarin.com> // -// Copyright (c) 2008 Novell, Inc (http://www.novell.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 @@ -22,37 +23,25 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -// + using System; -using ICSharpCode.NRefactory; -namespace MonoDevelop.Ide.TypeSystem +namespace MonoDevelop.Ide.Editor.Highlighting { - public class PreProcessorDefine + public sealed class ColorDescriptionAttribute : Attribute { - public string Define { - get; - set; - } - - public TextLocation Location { - get; - set; - } - - public PreProcessorDefine () - { - } - - public PreProcessorDefine (string define, TextLocation location) + public string Name { get; private set; } + public string Description { get; set; } + public string VSSetting { get; set; } + + public ColorDescriptionAttribute (string name) { - this.Define = define; - this.Location = location; + Name = name; } - + public override string ToString () { - return string.Format ("[PreProcessorDefine: Define={0}, Location={1}]", Define, Location); + return string.Format ("[ColorDescriptionAttribute: Name={0}, Description={1}, VSSetting={2}]", Name, Description, VSSetting); } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/ColorScheme.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/ColorScheme.cs new file mode 100644 index 0000000000..6714d9abe9 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/ColorScheme.cs @@ -0,0 +1,1030 @@ +// +// ColorScheme.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.IO; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; +using System.Xml.XPath; +using System.Reflection; +using System.Text; +using System.Xml; +using MonoDevelop.Components; + +namespace MonoDevelop.Ide.Editor.Highlighting +{ + public sealed class ColorScheme + { + public string Name { get; set; } + public string Description { get; set; } + public string Originator { get; set; } + public string BaseScheme { get; set; } + public string FileName { get; set; } + + #region Ambient Colors + + [ColorDescription("Background(Read Only)",VSSetting="color=Plain Text/Background")] + public AmbientColor BackgroundReadOnly { get; private set; } + + [ColorDescription("Search result background")] + public AmbientColor SearchResult { get; private set; } + + [ColorDescription("Search result background (highlighted)")] + public AmbientColor SearchResultMain { get; private set; } + + [ColorDescription("Fold Square", VSSetting="color=outlining.verticalrule/Foreground")] + public AmbientColor FoldLineColor { get; private set; } + + [ColorDescription("Fold Cross", VSSetting="color=outlining.square/Foreground,secondcolor=outlining.square/Background")] + public AmbientColor FoldCross { get; private set; } + + [ColorDescription("Indentation Guide")] // not defined + public AmbientColor IndentationGuide { get; private set; } + + [ColorDescription("Indicator Margin", VSSetting="color=Indicator Margin/Background")] + public AmbientColor IndicatorMargin { get; private set; } + + [ColorDescription("Indicator Margin(Separator)", VSSetting="color=Indicator Margin/Background")] + public AmbientColor IndicatorMarginSeparator { get; private set; } + + [ColorDescription("Tooltip Border")] + public AmbientColor TooltipBorder { get; private set; } + + [ColorDescription("Tooltip Pager Top")] + public AmbientColor TooltipPagerTop { get; private set; } + + [ColorDescription("Tooltip Pager Bottom")] + public AmbientColor TooltipPagerBottom { get; private set; } + + [ColorDescription("Tooltip Pager Triangle")] + public AmbientColor TooltipPagerTriangle { get; private set; } + + [ColorDescription("Tooltip Pager Text")] + public AmbientColor TooltipPagerText { get; private set; } + + [ColorDescription("Notification Border")] + public AmbientColor NotificationBorder { get; private set; } + + [ColorDescription("Bookmarks")] + public AmbientColor Bookmarks { get; private set; } + + [ColorDescription("Underline(Error)", VSSetting="color=Syntax Error/Foreground")] + public AmbientColor UnderlineError { get; private set; } + + [ColorDescription("Underline(Warning)", VSSetting="color=Warning/Foreground")] + public AmbientColor UnderlineWarning { get; private set; } + + [ColorDescription("Underline(Suggestion)", VSSetting="color=Other Error/Foreground")] + public AmbientColor UnderlineSuggestion { get; private set; } + + [ColorDescription("Underline(Hint)", VSSetting="color=Other Error/Foreground")] + public AmbientColor UnderlineHint { get; private set; } + + [ColorDescription("Quick Diff(Dirty)")] + public AmbientColor QuickDiffDirty { get; private set; } + + [ColorDescription("Quick Diff(Changed)")] + public AmbientColor QuickDiffChanged { get; private set; } + + [ColorDescription("Brace Matching(Rectangle)", VSSetting="color=Brace Matching (Rectangle)/Background,secondcolor=Brace Matching (Rectangle)/Foreground")] + public AmbientColor BraceMatchingRectangle { get; private set; } + + [ColorDescription("Usages(Rectangle)", VSSetting="color=MarkerFormatDefinition/HighlightedReference/Background,secondcolor=MarkerFormatDefinition/HighlightedReference/Background,bordercolor=MarkerFormatDefinition/HighlightedReference/Background")] + public AmbientColor UsagesRectangle { get; private set; } + + [ColorDescription("Changing usages(Rectangle)", VSSetting="color=MarkerFormatDefinition/HighlightedReference/Background,secondcolor=MarkerFormatDefinition/HighlightedReference/Background,bordercolor=MarkerFormatDefinition/HighlightedReference/Background")] + public AmbientColor ChangingUsagesRectangle { get; private set; } + + [ColorDescription("Breakpoint Marker", VSSetting = "color=Breakpoint (Enabled)/Background")] + public AmbientColor BreakpointMarker { get; private set; } + + [ColorDescription("Breakpoint Marker(Invalid)", VSSetting = "color=Breakpoint (Disabled)/Background")] + public AmbientColor BreakpointMarkerInvalid { get; private set; } + + [ColorDescription("Breakpoint Marker(Disabled)")] + public AmbientColor BreakpointMarkerDisabled { get; private set; } + + [ColorDescription("Debugger Current Line Marker", VSSetting = "color=Current Statement/Background")] + public AmbientColor DebuggerCurrentLineMarker { get; private set; } + + [ColorDescription("Debugger Stack Line Marker")] + public AmbientColor DebuggerStackLineMarker { get; private set; } + + [ColorDescription("Primary Link", VSSetting = "color=Refactoring Dependent Field/Background" )] + public AmbientColor PrimaryTemplate { get; private set; } + + [ColorDescription("Primary Link(Highlighted)", VSSetting = "color=Refactoring Current Field/Background")] + public AmbientColor PrimaryTemplateHighlighted { get; private set; } + + [ColorDescription("Secondary Link")] // not defined + public AmbientColor SecondaryTemplate { get; private set; } + + [ColorDescription("Secondary Link(Highlighted)")] // not defined + public AmbientColor SecondaryTemplateHighlighted { get; private set; } + + [ColorDescription("Current Line Marker", VSSetting = "color=CurrentLineActiveFormat/Background,secondcolor=CurrentLineActiveFormat/Foreground")] + public AmbientColor LineMarker { get; private set; } + + [ColorDescription("Current Line Marker(Inactive)", VSSetting = "color=CurrentLineInactiveFormat/Background,secondcolor=CurrentLineInactiveFormat/Foreground")] + public AmbientColor LineMarkerInactive { get; private set; } + + [ColorDescription("Column Ruler")] // not defined + public AmbientColor Ruler { get; private set; } + + [ColorDescription("Completion Matching Substring")] + public AmbientColor CompletionHighlight { get; private set; } + + [ColorDescription("Completion Border")] + public AmbientColor CompletionBorder { get; private set; } + + [ColorDescription("Completion Border(Inactive)")] + public AmbientColor CompletionInactiveBorder { get; private set; } + + [ColorDescription("Message Bubble Error Marker")] + public AmbientColor MessageBubbleErrorMarker { get; private set; } + + [ColorDescription("Message Bubble Error Tag")] + public AmbientColor MessageBubbleErrorTag { get; private set; } + + [ColorDescription("Message Bubble Error Tooltip")] + public AmbientColor MessageBubbleErrorTooltip { get; private set; } + + [ColorDescription("Message Bubble Error Line")] + public AmbientColor MessageBubbleErrorLine { get; private set; } + + [ColorDescription("Message Bubble Error Counter")] + public AmbientColor MessageBubbleErrorCounter { get; private set; } + + [ColorDescription("Message Bubble Error IconMargin")] + public AmbientColor MessageBubbleErrorIconMargin { get; private set; } + + [ColorDescription("Message Bubble Warning Marker")] + public AmbientColor MessageBubbleWarningMarker { get; private set; } + + [ColorDescription("Message Bubble Warning Tag")] + public AmbientColor MessageBubbleWarningTag { get; private set; } + + [ColorDescription("Message Bubble Warning Tooltip")] + public AmbientColor MessageBubbleWarningTooltip { get; private set; } + + [ColorDescription("Message Bubble Warning Line")] + public AmbientColor MessageBubbleWarningLine { get; private set; } + + [ColorDescription("Message Bubble Warning Counter")] + public AmbientColor MessageBubbleWarningCounter { get; private set; } + + [ColorDescription("Message Bubble Warning IconMargin")] + public AmbientColor MessageBubbleWarningIconMargin { get; private set; } + + #endregion + + #region Text Colors + + public const string PlainTextKey = "Plain Text"; + + [ColorDescription(PlainTextKey, VSSetting = "Plain Text")] + public ChunkStyle PlainText { get; private set; } + + public const string SelectedTextKey = "Selected Text"; + [ColorDescription(SelectedTextKey, VSSetting = "Selected Text")] + public ChunkStyle SelectedText { get; private set; } + + public const string SelectedInactiveTextKey = "Selected Text(Inactive)"; + [ColorDescription(SelectedInactiveTextKey, VSSetting = "Inactive Selected Text")] + public ChunkStyle SelectedInactiveText { get; private set; } + + public const string CollapsedTextKey = "Collapsed Text"; + [ColorDescription(CollapsedTextKey, VSSetting = "Collapsible Text")] + public ChunkStyle CollapsedText { get; private set; } + + public const string LineNumbersKey = "Line Numbers"; + [ColorDescription(LineNumbersKey, VSSetting = "Line Numbers")] + public ChunkStyle LineNumbers { get; private set; } + + public const string PunctuationKey = "Punctuation"; + [ColorDescription(PunctuationKey, VSSetting = "Operator")] + public ChunkStyle Punctuation { get; private set; } + + public const string PunctuationForBracketsKey = "Punctuation(Brackets)"; + [ColorDescription(PunctuationForBracketsKey, VSSetting = "Plain Text")] + public ChunkStyle PunctuationForBrackets { get; private set; } + + public const string CommentsSingleLineKey = "Comment(Line)"; + [ColorDescription(CommentsSingleLineKey, VSSetting = "Comment")] + public ChunkStyle CommentsSingleLine { get; private set; } + + public const string CommentsBlockKey = "Comment(Block)"; + [ColorDescription(CommentsBlockKey, VSSetting = "Comment")] + public ChunkStyle CommentsBlock { get; private set; } + + public const string CommentsForDocumentationKey = "Comment(Doc)"; + [ColorDescription(CommentsForDocumentationKey, VSSetting = "XML Doc Comment")] + public ChunkStyle CommentsForDocumentation { get; private set; } + + public const string CommentsForDocumentationTagsKey = "Comment(DocTag)"; + [ColorDescription(CommentsForDocumentationTagsKey, VSSetting = "XML Doc Tag")] + public ChunkStyle CommentsForDocumentationTags { get; private set; } + + public const string CommentTagsKey = "Comment Tag"; + [ColorDescription(CommentTagsKey, VSSetting = "Comment")] + public ChunkStyle CommentTags { get; private set; } + + public const string ExcludedCodeKey = "Excluded Code"; + [ColorDescription(ExcludedCodeKey, VSSetting = "Excluded Code")] + public ChunkStyle ExcludedCode { get; private set; } + + public const string StringKey = "String"; + [ColorDescription(StringKey, VSSetting = "String")] + public ChunkStyle String { get; private set; } + + public const string StringEscapeSequenceKey = "String(Escape)"; + [ColorDescription(StringEscapeSequenceKey, VSSetting = "String")] + public ChunkStyle StringEscapeSequence { get; private set; } + + public const string StringVerbatimKey = "String(C# @ Verbatim)"; + [ColorDescription(StringVerbatimKey, VSSetting = "String(C# @ Verbatim)")] + public ChunkStyle StringVerbatim { get; private set; } + + public const string NumberKey = "Number"; + [ColorDescription(NumberKey, VSSetting = "Number")] + public ChunkStyle Number { get; private set; } + + public const string PreprocessorKey = "Preprocessor"; + [ColorDescription(PreprocessorKey, VSSetting = "Preprocessor Keyword")] + public ChunkStyle Preprocessor { get; private set; } + + public const string PreprocessorRegionNameKey = "Preprocessor(Region Name)"; + [ColorDescription(PreprocessorRegionNameKey, VSSetting = "Plain Text")] + public ChunkStyle PreprocessorRegionName { get; private set; } + + public const string XmlTextKey = "Xml Text"; + [ColorDescription(XmlTextKey, VSSetting = "XML Text")] + public ChunkStyle XmlText { get; private set; } + + public const string XmlDelimiterKey = "Xml Delimiter"; + [ColorDescription(XmlDelimiterKey, VSSetting = "XML Delimiter")] + public ChunkStyle XmlDelimiter { get; private set; } + + public const string XmlNameKey = "Xml Name"; + [ColorDescription(XmlNameKey, VSSetting ="XML Name")] + public ChunkStyle XmlName { get; private set; } + + public const string XmlAttributeKey = "Xml Attribute"; + [ColorDescription(XmlAttributeKey, VSSetting = "XML Attribute")] + public ChunkStyle XmlAttribute { get; private set; } + + public const string XmlAttributeQuotesKey = "Xml Attribute Quotes"; + [ColorDescription(XmlAttributeQuotesKey, VSSetting = "XML Attribute Quotes")] + public ChunkStyle XmlAttributeQuotes { get; private set; } + + public const string XmlAttributeValueKey = "Xml Attribute Value"; + [ColorDescription(XmlAttributeValueKey, VSSetting = "XML Attribute Value")] + public ChunkStyle XmlAttributeValue { get; private set; } + + public const string XmlCommentKey = "Xml Comment"; + [ColorDescription(XmlCommentKey, VSSetting = "XML Comment")] + public ChunkStyle XmlComment { get; private set; } + + public const string XmlCDataSectionKey = "Xml CData Section"; + [ColorDescription(XmlCDataSectionKey, VSSetting = "XML CData Section")] + public ChunkStyle XmlCDataSection { get; private set; } + + public const string TooltipTextKey = "Tooltip Text"; + [ColorDescription(TooltipTextKey)] // not defined in vs.net + public ChunkStyle TooltipText { get; private set; } + + public const string NotificationTextKey = "Notification Text"; + [ColorDescription(NotificationTextKey)] // not defined in vs.net + public ChunkStyle NotificationText { get; private set; } + + public const string CompletionTextKey = "Completion Text"; + [ColorDescription(CompletionTextKey)] //not defined in vs.net + public ChunkStyle CompletionText { get; private set; } + + public const string CompletionSelectedTextKey = "Completion Selected Text"; + [ColorDescription(CompletionSelectedTextKey)] //not defined in vs.net + public ChunkStyle CompletionSelectedText { get; private set; } + + public const string CompletionSelectedInactiveTextKey = "Completion Selected Text(Inactive)"; + [ColorDescription(CompletionSelectedInactiveTextKey)] //not defined in vs.net + public ChunkStyle CompletionSelectedInactiveText { get; private set; } + + public const string KeywordAccessorsKey = "Keyword(Access)"; + [ColorDescription(KeywordAccessorsKey, VSSetting = "Keyword")] + public ChunkStyle KeywordAccessors { get; private set; } + + public const string KeywordTypesKey = "Keyword(Type)"; + [ColorDescription(KeywordTypesKey, VSSetting = "Keyword")] + public ChunkStyle KeywordTypes { get; private set; } + + public const string KeywordOperatorsKey = "Keyword(Operator)"; + [ColorDescription(KeywordOperatorsKey, VSSetting = "Keyword")] + public ChunkStyle KeywordOperators { get; private set; } + + public const string KeywordSelectionKey = "Keyword(Selection)"; + [ColorDescription(KeywordSelectionKey, VSSetting = "Keyword")] + public ChunkStyle KeywordSelection { get; private set; } + + public const string KeywordIterationKey = "Keyword(Iteration)"; + [ColorDescription(KeywordIterationKey, VSSetting = "Keyword")] + public ChunkStyle KeywordIteration { get; private set; } + + public const string KeywordJumpKey = "Keyword(Jump)"; + [ColorDescription(KeywordJumpKey, VSSetting = "Keyword")] + public ChunkStyle KeywordJump { get; private set; } + + public const string KeywordContextKey = "Keyword(Context)"; + [ColorDescription(KeywordContextKey, VSSetting = "Keyword")] + public ChunkStyle KeywordContext { get; private set; } + + public const string KeywordExceptionKey = "Keyword(Exception)"; + [ColorDescription(KeywordExceptionKey, VSSetting = "Keyword")] + public ChunkStyle KeywordException { get; private set; } + + public const string KeywordModifiersKey = "Keyword(Modifiers)"; + [ColorDescription(KeywordModifiersKey, VSSetting = "Keyword")] + public ChunkStyle KeywordModifiers { get; private set; } + + public const string KeywordConstantsKey = "Keyword(Constants)"; + [ColorDescription(KeywordConstantsKey, VSSetting = "Keyword")] + public ChunkStyle KeywordConstants { get; private set; } + + public const string KeywordVoidKey = "Keyword(Void)"; + [ColorDescription(KeywordVoidKey, VSSetting = "Keyword")] + public ChunkStyle KeywordVoid { get; private set; } + + public const string KeywordNamespaceKey = "Keyword(Namespace)"; + [ColorDescription(KeywordNamespaceKey, VSSetting = "Keyword")] + public ChunkStyle KeywordNamespace { get; private set; } + + public const string KeywordPropertyKey = "Keyword(Property)"; + [ColorDescription(KeywordPropertyKey, VSSetting = "Keyword")] + public ChunkStyle KeywordProperty { get; private set; } + + public const string KeywordDeclarationKey = "Keyword(Declaration)"; + [ColorDescription(KeywordDeclarationKey, VSSetting = "Keyword")] + public ChunkStyle KeywordDeclaration { get; private set; } + + public const string KeywordParameterKey = "Keyword(Parameter)"; + [ColorDescription(KeywordParameterKey, VSSetting = "Keyword")] + public ChunkStyle KeywordParameter { get; private set; } + + public const string KeywordOperatorDeclarationKey = "Keyword(Operator Declaration)"; + [ColorDescription(KeywordOperatorDeclarationKey, VSSetting = "Keyword")] + public ChunkStyle KeywordOperatorDeclaration { get; private set; } + + public const string KeywordOtherKey = "Keyword(Other)"; + [ColorDescription(KeywordOtherKey, VSSetting = "Keyword")] + public ChunkStyle KeywordOther { get; private set; } + + public const string UserTypesKey = "User Types"; + [ColorDescription(UserTypesKey, VSSetting = "User Types")] + public ChunkStyle UserTypes { get; private set; } + + public const string UserTypesEnumsKey = "User Types(Enums)"; + [ColorDescription(UserTypesEnumsKey, VSSetting = "User Types(Enums)")] + public ChunkStyle UserTypesEnums { get; private set; } + + public const string UserTypesInterfacesKey = "User Types(Interfaces)"; + [ColorDescription(UserTypesInterfacesKey, VSSetting = "User Types(Interfaces)")] + public ChunkStyle UserTypesInterfaces { get; private set; } + + public const string UserTypesDelegatesKey = "User Types(Delegates)"; + [ColorDescription(UserTypesDelegatesKey, VSSetting = "User Types(Delegates)")] + public ChunkStyle UserTypesDelegates { get; private set; } + + public const string UserTypesValueTypesKey = "User Types(Value types)"; + [ColorDescription(UserTypesValueTypesKey, VSSetting = "User Types(Value types)")] + public ChunkStyle UserTypesValueTypes { get; private set; } + + public const string UserTypesTypeParametersKey = "User Types(Type parameters)"; + [ColorDescription(UserTypesTypeParametersKey, VSSetting = "User Types(Type parameters)")] + public ChunkStyle UserTypesTypeParameters { get; private set; } + + public const string UserFieldUsageKey = "User Field Usage"; + [ColorDescription(UserFieldUsageKey, VSSetting = "Identifier")] + public ChunkStyle UserFieldUsage { get; private set; } + + public const string UserFieldDeclarationKey = "User Field Declaration"; + [ColorDescription(UserFieldDeclarationKey, VSSetting = "Identifier")] + public ChunkStyle UserFieldDeclaration { get; private set; } + + public const string UserPropertyUsageKey = "User Property Usage"; + [ColorDescription(UserPropertyUsageKey, VSSetting = "Identifier")] + public ChunkStyle UserPropertyUsage { get; private set; } + + public const string UserPropertyDeclarationKey = "User Property Declaration"; + [ColorDescription(UserPropertyDeclarationKey, VSSetting = "Identifier")] + public ChunkStyle UserPropertyDeclaration { get; private set; } + + public const string UserEventUsageKey = "User Event Usage"; + [ColorDescription(UserEventUsageKey, VSSetting = "Identifier")] + public ChunkStyle UserEventUsage { get; private set; } + + public const string UserEventDeclarationKey = "User Event Declaration"; + [ColorDescription(UserEventDeclarationKey, VSSetting = "Identifier")] + public ChunkStyle UserEventDeclaration { get; private set; } + + public const string UserMethodUsageKey = "User Method Usage"; + [ColorDescription(UserMethodUsageKey, VSSetting = "Identifier")] + public ChunkStyle UserMethodUsage { get; private set; } + + public const string UserMethodDeclarationKey = "User Method Declaration"; + [ColorDescription(UserMethodDeclarationKey, VSSetting = "Identifier")] + public ChunkStyle UserMethodDeclaration { get; private set; } + + public const string UserParameterUsageKey = "User Parameter Usage"; + [ColorDescription(UserParameterUsageKey, VSSetting = "Identifier")] + public ChunkStyle UserParameterUsage { get; private set; } + + public const string UserParameterDeclarationKey = "User Parameter Declaration"; + [ColorDescription(UserParameterDeclarationKey, VSSetting = "Identifier")] + public ChunkStyle UserParameterDeclaration { get; private set; } + + public const string UserVariableUsageKey = "User Variable Usage"; + [ColorDescription(UserVariableUsageKey, VSSetting = "Identifier")] + public ChunkStyle UserVariableUsage { get; private set; } + + public const string UserVariableDeclarationKey = "User Variable Declaration"; + [ColorDescription(UserVariableDeclarationKey, VSSetting = "Identifier")] + public ChunkStyle UserVariableDeclaration { get; private set; } + + public const string SyntaxErrorKey = "Syntax Error"; + [ColorDescription(SyntaxErrorKey, VSSetting = "Syntax Error")] + public ChunkStyle SyntaxError { get; private set; } + + public const string StringFormatItemsKey = "String Format Items"; + [ColorDescription(StringFormatItemsKey, VSSetting = "String")] + public ChunkStyle StringFormatItems { get; private set; } + + public const string BreakpointTextKey = "Breakpoint Text"; + [ColorDescription(BreakpointTextKey, VSSetting = "Breakpoint (Enabled)")] + public ChunkStyle BreakpointText { get; private set; } + + public const string DebuggerCurrentLineKey = "Debugger Current Statement"; + [ColorDescription(DebuggerCurrentLineKey, VSSetting = "Current Statement")] + public ChunkStyle DebuggerCurrentLine { get; private set; } + + public const string DebuggerStackLineKey = "Debugger Stack Line"; + [ColorDescription(DebuggerStackLineKey)] // not defined + public ChunkStyle DebuggerStackLine { get; private set; } + + public const string DiffLineAddedKey = "Diff Line(Added)"; + [ColorDescription(DiffLineAddedKey)] //not defined + public ChunkStyle DiffLineAdded { get; private set; } + + public const string DiffLineRemovedKey = "Diff Line(Removed)"; + [ColorDescription(DiffLineRemovedKey)] //not defined + public ChunkStyle DiffLineRemoved { get; private set; } + + public const string DiffLineChangedKey = "Diff Line(Changed)"; + [ColorDescription(DiffLineChangedKey)] //not defined + public ChunkStyle DiffLineChanged { get; private set; } + + public const string DiffHeaderKey = "Diff Header"; + [ColorDescription(DiffHeaderKey)] //not defined + public ChunkStyle DiffHeader { get; private set; } + + public const string DiffHeaderSeparatorKey = "Diff Header(Separator)"; + [ColorDescription(DiffHeaderSeparatorKey)] //not defined + public ChunkStyle DiffHeaderSeparator { get; private set; } + + public const string DiffHeaderOldKey = "Diff Header(Old)"; + [ColorDescription(DiffHeaderOldKey)] //not defined + public ChunkStyle DiffHeaderOld { get; private set; } + + public const string DiffHeaderNewKey = "Diff Header(New)"; + [ColorDescription(DiffHeaderNewKey)] //not defined + public ChunkStyle DiffHeaderNew { get; private set; } + + public const string DiffLocationKey = "Diff Location"; + [ColorDescription(DiffLocationKey)] //not defined + public ChunkStyle DiffLocation { get; private set; } + + public const string HtmlAttributeNameKey = "Html Attribute Name"; + [ColorDescription(HtmlAttributeNameKey, VSSetting="HTML Attribute")] + public ChunkStyle HtmlAttributeName { get; private set; } + + public const string HtmlAttributeValueKey = "Html Attribute Value"; + [ColorDescription(HtmlAttributeValueKey, VSSetting="HTML Attribute Value")] + public ChunkStyle HtmlAttributeValue { get; private set; } + + public const string HtmlCommentKey = "Html Comment"; + [ColorDescription(HtmlCommentKey, VSSetting="HTML Comment")] + public ChunkStyle HtmlComment { get; private set; } + + public const string HtmlElementNameKey = "Html Element Name"; + [ColorDescription(HtmlElementNameKey, VSSetting="HTML Element Name")] + public ChunkStyle HtmlElementName { get; private set; } + + public const string HtmlEntityKey = "Html Entity"; + [ColorDescription(HtmlEntityKey, VSSetting="HTML Entity")] + public ChunkStyle HtmlEntity { get; private set; } + + public const string HtmlOperatorKey = "Html Operator"; + [ColorDescription(HtmlOperatorKey, VSSetting="HTML Operator")] + public ChunkStyle HtmlOperator { get; private set; } + + public const string HtmlServerSideScriptKey = "Html Server-Side Script"; + [ColorDescription(HtmlServerSideScriptKey, VSSetting="HTML Server-Side Script")] + public ChunkStyle HtmlServerSideScript { get; private set; } + + public const string HtmlTagDelimiterKey = "Html Tag Delimiter"; + [ColorDescription(HtmlTagDelimiterKey, VSSetting="HTML Tag Delimiter")] + public ChunkStyle HtmlTagDelimiter { get; private set; } + + public const string RazorCodeKey = "Razor Code"; + [ColorDescription(RazorCodeKey, VSSetting="Razor Code")] + public ChunkStyle RazorCode { get; private set; } + + public const string CssCommentKey = "Css Comment"; + [ColorDescription(CssCommentKey, VSSetting="CSS Comment")] + public ChunkStyle CssComment { get; private set; } + + public const string CssPropertyNameKey = "Css Property Name"; + [ColorDescription(CssPropertyNameKey, VSSetting="CSS Property Name")] + public ChunkStyle CssPropertyName { get; private set; } + + public const string CssPropertyValueKey = "Css Property Value"; + [ColorDescription(CssPropertyValueKey, VSSetting="CSS Property Value")] + public ChunkStyle CssPropertyValue { get; private set; } + + public const string CssSelectorKey = "Css Selector"; + [ColorDescription(CssSelectorKey, VSSetting="CSS Selector")] + public ChunkStyle CssSelector { get; private set; } + + public const string CssStringValueKey = "Css String Value"; + [ColorDescription(CssStringValueKey, VSSetting="CSS String Value")] + public ChunkStyle CssStringValue { get; private set; } + + public const string CssKeywordKey = "Css Keyword"; + [ColorDescription(CssKeywordKey, VSSetting="CSS Keyword")] + public ChunkStyle CssKeyword { get; private set; } + + public const string ScriptCommentKey = "Script Comment"; + [ColorDescription(ScriptCommentKey, VSSetting="Script Comment")] + public ChunkStyle ScriptComment { get; private set; } + + public const string ScriptIdentifierKey = "Script Identifier"; + [ColorDescription(ScriptIdentifierKey, VSSetting="Script Identifier")] + public ChunkStyle ScriptIdentifier { get; private set; } + + public const string ScriptKeywordKey = "Script Keyword"; + [ColorDescription(ScriptKeywordKey, VSSetting="Script Keyword")] + public ChunkStyle ScriptKeyword { get; private set; } + + public const string ScriptNumberKey = "Script Number"; + [ColorDescription(ScriptNumberKey, VSSetting="Script Number")] + public ChunkStyle ScriptNumber { get; private set; } + + public const string ScriptOperatorKey = "Script Operator"; + [ColorDescription(ScriptOperatorKey, VSSetting="Script Operator")] + public ChunkStyle ScriptOperator { get; private set; } + + public const string ScriptStringKey = "Script String"; + [ColorDescription(ScriptStringKey, VSSetting="Script String")] + public ChunkStyle ScriptString { get; private set; } + + #endregion + + public class PropertyDecsription + { + public readonly PropertyInfo Info; + public readonly ColorDescriptionAttribute Attribute; + + public PropertyDecsription (PropertyInfo info, ColorDescriptionAttribute attribute) + { + this.Info = info; + this.Attribute = attribute; + } + } + + static Dictionary<string, PropertyDecsription> textColors = new Dictionary<string, PropertyDecsription> (); + + public static IEnumerable<PropertyDecsription> TextColors { + get { + return textColors.Values; + } + } + + static Dictionary<string, PropertyDecsription> ambientColors = new Dictionary<string, PropertyDecsription> (); + + public static IEnumerable<PropertyDecsription> AmbientColors { + get { + return ambientColors.Values; + } + } + + static ColorScheme () + { + foreach (var property in typeof(ColorScheme).GetProperties ()) { + var description = property.GetCustomAttributes (false).FirstOrDefault (p => p is ColorDescriptionAttribute) as ColorDescriptionAttribute; + if (description == null) + continue; + if (property.PropertyType == typeof (ChunkStyle)) { + textColors.Add (description.Name, new PropertyDecsription (property, description)); + } else { + ambientColors.Add (description.Name, new PropertyDecsription (property, description)); + } + } + } + + public ColorScheme Clone () + { + var result = new ColorScheme () { + Name = this.Name, + BaseScheme = this.BaseScheme, + Originator = this.Originator, + Description = this.Description + }; + result.CopyValues (this); + return result; + } + + static HslColor ParseColor (string value) + { + if (value.Length == 9 && value.StartsWith ("#", StringComparison.Ordinal)) { + double r = ((double) int.Parse (value.Substring (1,2), System.Globalization.NumberStyles.HexNumber)) / 255; + double g = ((double) int.Parse (value.Substring (3,2), System.Globalization.NumberStyles.HexNumber)) / 255; + double b = ((double) int.Parse (value.Substring (5,2), System.Globalization.NumberStyles.HexNumber)) / 255; + double a = ((double) int.Parse (value.Substring (7,2), System.Globalization.NumberStyles.HexNumber)) / 255; + return new HslColor (r, g, b, a); + } + return HslColor.Parse (value); + } + + public static HslColor ParsePaletteColor (Dictionary<string, HslColor> palette, string value) + { + HslColor result; + if (palette.TryGetValue (value, out result)) + return result; + return ParseColor (value); + } + + public ChunkStyle GetChunkStyle (string color) + { + if (color == null) + return GetChunkStyle ("Plain Text"); + PropertyDecsription val; + if (!textColors.TryGetValue (color, out val)) { + Console.WriteLine ("Chunk style : " + color + " is undefined."); + return GetChunkStyle ("Plain Text"); + } + return val.Info.GetValue (this, null) as ChunkStyle; + } + + void CopyValues (ColorScheme baseScheme) + { + foreach (var color in textColors.Values) + color.Info.SetValue (this, color.Info.GetValue (baseScheme, null), null); + foreach (var color in ambientColors.Values) + color.Info.SetValue (this, color.Info.GetValue (baseScheme, null), null); + } + + public static ColorScheme LoadFrom (Stream stream) + { + var result = new ColorScheme (); + var reader = System.Runtime.Serialization.Json.JsonReaderWriterFactory.CreateJsonReader (stream, new System.Xml.XmlDictionaryReaderQuotas ()); + + var root = XElement.Load(reader); + + // The fields we'd like to extract + result.Name = root.XPathSelectElement("name").Value; + + if (result.Name != "Default") + result.CopyValues (SyntaxModeService.DefaultColorStyle); + + var version = Version.Parse (root.XPathSelectElement("version").Value); + if (version.Major != 1) + return null; + var el = root.XPathSelectElement ("description"); + if (el != null) + result.Description = el.Value; + el = root.XPathSelectElement ("originator"); + if (el != null) + result.Originator = el.Value; + el = root.XPathSelectElement ("baseScheme"); + if (el != null) + result.BaseScheme = el.Value; + + if (result.BaseScheme != null) { + var baseScheme = SyntaxModeService.GetColorStyle (result.BaseScheme); + if (baseScheme != null) + result.CopyValues (baseScheme); + } + + var palette = new Dictionary<string, HslColor> (); + foreach (var color in root.XPathSelectElements("palette/*")) { + var name = color.XPathSelectElement ("name").Value; + if (palette.ContainsKey (name)) + throw new InvalidDataException ("Duplicate palette color definition for: " + name); + palette.Add ( + name, + ParseColor (color.XPathSelectElement ("value").Value) + ); + } + + foreach (var colorElement in root.XPathSelectElements("//colors/*")) { + var color = AmbientColor.Create (colorElement, palette); + PropertyDecsription info; + if (!ambientColors.TryGetValue (color.Name, out info)) { + Console.WriteLine ("Ambient color:" + color.Name + " not found."); + continue; + } + info.Info.SetValue (result, color, null); + } + + foreach (var textColorElement in root.XPathSelectElements("//text/*")) { + var color = ChunkStyle.Create (textColorElement, palette); + PropertyDecsription info; + if (!textColors.TryGetValue (color.Name, out info)) { + Console.WriteLine ("Text color:" + color.Name + " not found."); + continue; + } + info.Info.SetValue (result, color, null); + } + + // Check scheme + bool valid = true; + foreach (var color in textColors.Values) { + if (color.Info.GetValue (result, null) == null) { + Console.WriteLine (color.Attribute.Name + " == null"); + valid = false; + } + } + foreach (var color in ambientColors.Values) { + if (color.Info.GetValue (result, null) == null) { + Console.WriteLine (color.Attribute.Name + " == null"); + valid = false; + } + } + if (!valid) + throw new InvalidDataException ("Scheme " + result.Name + " is not valid."); + return result; + } + + public static string ColorToMarkup (HslColor color) + { + return color.ToMarkup (); + } + + + public void Save (string fileName) + { + using (var writer = new StreamWriter (fileName)) { + writer.WriteLine ("{"); + writer.WriteLine ("\t\"name\":\"{0}\",", Name); + writer.WriteLine ("\t\"version\":\"1.0\","); + if (!string.IsNullOrEmpty (Description)) + writer.WriteLine ("\t\"description\":\"{0}\",", Description); + if (!string.IsNullOrEmpty (Originator)) + writer.WriteLine ("\t\"originator\":\"{0}\",", Originator); + if (!string.IsNullOrEmpty (BaseScheme)) + writer.WriteLine ("\t\"baseScheme\":\"{0}\",", BaseScheme); + + var baseStyle = SyntaxModeService.GetColorStyle (BaseScheme ?? "Default"); + + writer.WriteLine ("\t\"colors\":["); + bool first = true; + foreach (var ambient in ambientColors) { + var thisValue = ambient.Value.Info.GetValue (this, null) as AmbientColor; + if (thisValue == null) + continue; + var baseValue = ambient.Value.Info.GetValue (baseStyle, null) as AmbientColor; + if (thisValue.Equals (baseValue)) { + continue; + } + + var colorString = new StringBuilder (); + foreach (var color in thisValue.Colors) { + if (colorString.Length > 0) + colorString.Append (", "); + colorString.Append (string.Format ("\"{0}\":\"{1}\"", color.Item1, ColorToMarkup (color.Item2))); + } + if (colorString.Length == 0) { + Console.WriteLine ("Invalid ambient color :" + thisValue); + continue; + } + if (!first) { + writer.WriteLine (","); + } else { + first = false; + } + writer.Write ("\t\t{"); + writer.Write ("\"name\": \"{0}\", {1}", ambient.Value.Attribute.Name, colorString); + writer.Write (" }"); + } + + writer.WriteLine ("\t],"); + first = true; + writer.WriteLine ("\t\"text\":["); + foreach (var textColor in textColors) { + var thisValue = textColor.Value.Info.GetValue (this, null) as ChunkStyle; + if (thisValue == null) + continue; + var baseValue = textColor.Value.Info.GetValue (baseStyle, null) as ChunkStyle; + if (thisValue.Equals (baseValue)) { + continue; + } + var colorString = new StringBuilder (); + if (!thisValue.TransparentForeground) + colorString.Append (string.Format ("\"fore\":\"{0}\"", ColorToMarkup (thisValue.Foreground))); + if (!thisValue.TransparentBackground) { + if (colorString.Length > 0) + colorString.Append (", "); + colorString.Append (string.Format ("\"back\":\"{0}\"", ColorToMarkup (thisValue.Background))); + } + if (thisValue.FontWeight != Xwt.Drawing.FontWeight.Normal) { + if (colorString.Length > 0) + colorString.Append (", "); + colorString.Append (string.Format ("\"weight\":\"{0}\"", thisValue.FontWeight)); + } + if (thisValue.FontStyle != Xwt.Drawing.FontStyle.Normal) { + if (colorString.Length > 0) + colorString.Append (", "); + colorString.Append (string.Format ("\"style\":\"{0}\"", thisValue.FontStyle)); + } + if (!first) { + writer.WriteLine (","); + } else { + first = false; + } + writer.Write ("\t\t{"); + if (colorString.Length == 0) { + writer.Write ("\"name\": \"{0}\"", textColor.Value.Attribute.Name); + } else { + writer.Write ("\"name\": \"{0}\", {1}", textColor.Value.Attribute.Name, colorString); + } + writer.Write (" }"); + } + writer.WriteLine (); + writer.WriteLine ("\t]"); + + writer.WriteLine ("}"); + } + } + + internal static HslColor ImportVsColor (string colorString) + { + if (colorString == "0x02000000") + return new HslColor (0, 0, 0, 0); + string color = "#" + colorString.Substring (8, 2) + colorString.Substring (6, 2) + colorString.Substring (4, 2); + return HslColor.Parse (color); + } + + public class VSSettingColor + { + public string Name { get; private set; } + public string Foreground { get; private set; } + public string Background { get; private set; } + public bool BoldFont { get; private set; } + + public static VSSettingColor Create (XmlReader reader) + { + return new VSSettingColor { + Name = reader.GetAttribute ("Name"), + Foreground = reader.GetAttribute ("Foreground"), + Background = reader.GetAttribute ("Background"), + BoldFont = reader.GetAttribute ("BoldFont") == "Yes" + }; + } + } + + public static HslColor AlphaBlend (HslColor fore, HslColor back, double alpha) + { + var fc = (Cairo.Color)fore; + var bc = (Cairo.Color)back; + return new HslColor ( + (1.0 - alpha) * bc.R + alpha * fc.R, + (1.0 - alpha) * bc.G + alpha * fc.G, + (1.0 - alpha) * bc.B + alpha * fc.B); + } + + public static ColorScheme Import (string fileName, Stream stream) + { + var result = new ColorScheme (); + result.Name = Path.GetFileNameWithoutExtension (fileName); + result.Description = "Imported color scheme"; + result.Originator = "Imported from " + fileName; + + var colors = new Dictionary<string, VSSettingColor> (); + using (var reader = XmlReader.Create (stream)) { + while (reader.Read ()) { + if (reader.LocalName == "Item") { + var color = VSSettingColor.Create (reader); + if (colors.ContainsKey (color.Name)) { + Console.WriteLine ("Warning: {0} is defined twice in vssettings.", color.Name); + continue; + } + colors[color.Name] = color; + } + } + } + + + HashSet<string> importedAmbientColors = new HashSet<string> (); + // convert ambient colors + foreach (var ambient in ambientColors.Values) { + if (!string.IsNullOrEmpty (ambient.Attribute.VSSetting)) { + var import = AmbientColor.Import (colors, ambient.Attribute.VSSetting); + if (import != null) { + importedAmbientColors.Add (import.Name); + ambient.Info.SetValue (result, import, null); + continue; + } + } + } + + // convert text colors + foreach (var vsc in colors.Values) { + bool found = false; + foreach (var color in textColors) { + if (color.Value.Attribute.VSSetting == null) + continue; + var split = color.Value.Attribute.VSSetting.Split ('?'); + foreach (var s in split) { + if (s == vsc.Name) { + /* if (vsc.Foreground == "0x02000000" && vsc.Background == "0x02000000") { + color.Value.Info.SetValue (result, result.PlainText, null); + found = true; + continue; + }*/ + var textColor = ChunkStyle.Import (color.Value.Attribute.Name, vsc); + if (textColor != null) { + color.Value.Info.SetValue (result, textColor, null); + found = true; + } + } + } + } + if (!found && !importedAmbientColors.Contains (vsc.Name)) + Console.WriteLine (vsc.Name + " not imported!"); + } + + result.IndentationGuide = new AmbientColor (); + result.IndentationGuide.Colors.Add (Tuple.Create ("color", AlphaBlend (result.PlainText.Foreground, result.PlainText.Background, 0.3))); + + result.TooltipText = result.PlainText.Clone (); + var h = (HslColor)result.TooltipText.Background; + h.L += 0.01; + result.TooltipText.Background = h; + + result.TooltipPagerTop = new AmbientColor (); + result.TooltipPagerTop.Colors.Add (Tuple.Create ("color", result.TooltipText.Background)); + + result.TooltipPagerBottom = new AmbientColor (); + result.TooltipPagerBottom.Colors.Add (Tuple.Create ("color", result.TooltipText.Background)); + + result.TooltipPagerTriangle = new AmbientColor (); + result.TooltipPagerTriangle.Colors.Add (Tuple.Create ("color", AlphaBlend (result.PlainText.Foreground, result.PlainText.Background, 0.8))); + + result.TooltipBorder = new AmbientColor (); + result.TooltipBorder.Colors.Add (Tuple.Create ("color", AlphaBlend (result.PlainText.Foreground, result.PlainText.Background, 0.5))); + + var defaultStyle = SyntaxModeService.GetColorStyle (HslColor.Brightness (result.PlainText.Background) < 0.5 ? "Monokai" : "Default"); + + foreach (var color in textColors.Values) { + if (color.Info.GetValue (result, null) == null) + color.Info.SetValue (result, color.Info.GetValue (defaultStyle, null), null); + } + foreach (var color in ambientColors.Values) { + if (color.Info.GetValue (result, null) == null) + color.Info.SetValue (result, color.Info.GetValue (defaultStyle, null), null); + } + if (result.PlainText.TransparentForeground) + result.PlainText.Foreground = new HslColor (0, 0, 0); + return result; + } + + public HslColor GetForeground (ChunkStyle chunkStyle) + { + if (chunkStyle.TransparentForeground) + return PlainText.Foreground; + return chunkStyle.Foreground; + } + + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/ColoredSegment.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/ColoredSegment.cs new file mode 100644 index 0000000000..9f67c54602 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/ColoredSegment.cs @@ -0,0 +1,59 @@ +// +// ColoredSegment.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; + +namespace MonoDevelop.Ide.Editor.Highlighting +{ + /// <summary> + /// A colored segment is used in the highlighter to specify a color scheme style to a specfic part of text. + /// </summary> + public sealed class ColoredSegment : AbstractSegment + { + readonly string colorStyleKey; + + //// <summary> + /// Gets the color style. The style is looked up in the current color scheme. + /// </summary> + public string ColorStyleKey { + get { + return colorStyleKey; + } + } + + public ColoredSegment (int offset, int length, string colorStyleKey) : base (offset, length) + { + this.colorStyleKey = colorStyleKey; + } + + public ColoredSegment (ISegment segment, string colorStyleKey) : base (segment) + { + this.colorStyleKey = colorStyleKey; + } + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/IStreamProvider.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/IStreamProvider.cs new file mode 100644 index 0000000000..97126a1219 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/IStreamProvider.cs @@ -0,0 +1,97 @@ +// IXmlProvider.cs +// +// Author: +// Mike Krüger <mkrueger@novell.com> +// +// Copyright (c) 2008 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +using System; +using System.IO; +using System.Reflection; +using System.Xml; + +namespace MonoDevelop.Ide.Editor.Highlighting +{ + public interface IStreamProvider + { + Stream Open (); + } + + [Obsolete("Do not use this anymore. Use ResourceStreamProvider.")] + public class ResourceXmlProvider : ResourceStreamProvider + { + public ResourceXmlProvider (Assembly assembly, string manifestResourceName) : base(assembly, manifestResourceName) + { + + } + } + public class ResourceStreamProvider : IStreamProvider + { + Assembly assembly; + string manifestResourceName; + + public string ManifestResourceName { + get { + return manifestResourceName; + } + } + + public Assembly Assembly { + get { + return assembly; + } + } + + public ResourceStreamProvider (Assembly assembly, string manifestResourceName) + { + this.assembly = assembly; + this.manifestResourceName = manifestResourceName; + } + + public Stream Open () + { + return assembly.GetManifestResourceStream (this.ManifestResourceName); + } + } + + public class UrlStreamProvider : IStreamProvider + { + string url; + + public string Url { + get { + return url; + } + } + + public UrlStreamProvider (string url) + { + this.url = url; + } + + public Stream Open () + { + return File.OpenRead (url); + } + } + +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/SemanticHighlighting.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/SemanticHighlighting.cs new file mode 100644 index 0000000000..f0f27f39f6 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/SemanticHighlighting.cs @@ -0,0 +1,76 @@ +// +// SemanticHighlighting.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; + +namespace MonoDevelop.Ide.Editor.Highlighting +{ + /// <summary> + /// Semantic highlighting adds the ability to add highlighting for things that require + /// a background parser to be colored. For example type names. + /// </summary> + public abstract class SemanticHighlighting : IDisposable + { + protected readonly internal TextEditor editor; + protected readonly internal DocumentContext documentContext; + + protected SemanticHighlighting (TextEditor editor, DocumentContext documentContext) + { + this.editor = editor; + this.documentContext = documentContext; + this.documentContext.DocumentParsed += HandleDocumentParsed; + } + + protected abstract void DocumentParsed (); + + public void NotifySemanticHighlightingUpdate () + { + var handler = SemanticHighlightingUpdated; + if (handler != null) + handler (this, EventArgs.Empty); + } + + /// <summary> + /// Colorize the specified offset, count and colorizeCallback. + /// </summary> + /// <param name="segment">The area to run the colorizer in.</param> + public abstract IEnumerable<ColoredSegment> GetColoredSegments (ISegment segment); + + void HandleDocumentParsed (object sender, EventArgs e) + { + if (DefaultSourceEditorOptions.Instance.EnableSemanticHighlighting) + DocumentParsed (); + } + + public void Dispose () + { + documentContext.DocumentParsed -= HandleDocumentParsed; + } + + internal event EventHandler SemanticHighlightingUpdated; + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/SyntaxModeService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/SyntaxModeService.cs new file mode 100644 index 0000000000..539bcc5c6a --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/SyntaxModeService.cs @@ -0,0 +1,216 @@ +// SyntaxModeService.cs +// +// Author: +// Mike Krüger <mkrueger@novell.com> +// +// Copyright (c) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Reflection; +using System.Threading; +using System.Xml; +using System.Xml.Schema; +using System.Linq; +using Mono.Addins; +using MonoDevelop.Core; + +namespace MonoDevelop.Ide.Editor.Highlighting +{ + public static class SyntaxModeService + { + static Dictionary<string, ColorScheme> styles = new Dictionary<string, ColorScheme> (); + static Dictionary<string, IStreamProvider> styleLookup = new Dictionary<string, IStreamProvider> (); + + public static string[] Styles { + get { + List<string> result = new List<string> (); + foreach (string style in styles.Keys) { + if (!result.Contains (style)) + result.Add (style); + } + foreach (string style in styleLookup.Keys) { + if (!result.Contains (style)) + result.Add (style); + } + return result.ToArray (); + } + } + + public static ColorScheme GetColorStyle (string name) + { + if (styles.ContainsKey (name)) + return styles [name]; + if (styleLookup.ContainsKey (name)) { + LoadStyle (name); + return GetColorStyle (name); + } + return GetColorStyle ("Default"); + } + + public static IStreamProvider GetProvider (ColorScheme style) + { + if (styleLookup.ContainsKey (style.Name)) + return styleLookup[style.Name]; + return null; + } + + static void LoadStyle (string name) + { + if (!styleLookup.ContainsKey (name)) + throw new System.ArgumentException ("Style " + name + " not found", "name"); + var provider = styleLookup [name]; + styleLookup.Remove (name); + var stream = provider.Open (); + try { + if (provider is UrlStreamProvider) { + var usp = provider as UrlStreamProvider; + if (usp.Url.EndsWith (".vssettings", StringComparison.Ordinal)) { + styles [name] = ColorScheme.Import (usp.Url, stream); + } else { + styles [name] = ColorScheme.LoadFrom (stream); + } + styles [name].FileName = usp.Url; + } else { + styles [name] = ColorScheme.LoadFrom (stream); + } + } catch (Exception e) { + throw new IOException ("Error while loading style :" + name, e); + } finally { + stream.Close (); + } + } + + + public static void Remove (ColorScheme style) + { + if (styleLookup.ContainsKey (style.Name)) + styleLookup.Remove (style.Name); + + foreach (var kv in styles) { + if (kv.Value == style) { + styles.Remove (kv.Key); + return; + } + } + } + + + public static List<ValidationEventArgs> ValidateStyleFile (string fileName) + { + List<ValidationEventArgs> result = new List<ValidationEventArgs> (); + return result; + } + + + public static void LoadStylesAndModes (string path) + { + foreach (string file in Directory.GetFiles (path)) { + if (file.EndsWith (".json", StringComparison.Ordinal)) { + using (var stream = File.OpenRead (file)) { + string styleName = ScanStyle (stream); + if (!string.IsNullOrEmpty (styleName)) { + styleLookup [styleName] = new UrlStreamProvider (file); + } else { + Console.WriteLine ("Invalid .json syntax sheme file : " + file); + } + } + } else if (file.EndsWith (".vssettings", StringComparison.Ordinal)) { + using (var stream = File.OpenRead (file)) { + string styleName = Path.GetFileNameWithoutExtension (file); + styleLookup [styleName] = new UrlStreamProvider (file); + } + } + } + } + + public static void LoadStylesAndModes (Assembly assembly) + { + foreach (string resource in assembly.GetManifestResourceNames ()) { + if (resource.EndsWith ("Style.json", StringComparison.Ordinal)) { + using (Stream stream = assembly.GetManifestResourceStream (resource)) { + string styleName = ScanStyle (stream); + styleLookup [styleName] = new ResourceStreamProvider (assembly, resource); + } + } + } + } + static System.Text.RegularExpressions.Regex nameRegex = new System.Text.RegularExpressions.Regex ("\\s*\"name\"\\s*:\\s*\"(.*)\"\\s*,"); + + static string ScanStyle (Stream stream) + { + try { + var file = new StreamReader (stream); + file.ReadLine (); + var nameLine = file.ReadLine (); + var match = nameRegex.Match (nameLine); + if (!match.Success) + return null; + return match.Groups[1].Value; + } catch (Exception e) { + Console.WriteLine ("Error while scanning json:"); + Console.WriteLine (e); + return null; + } + } + + public static void AddStyle (ColorScheme style) + { + styles [style.Name] = style; + } + + public static void AddStyle (IStreamProvider provider) + { + using (var stream = provider.Open ()) { + string styleName = ScanStyle (stream); + styleLookup [styleName] = provider; + } + } + + public static void RemoveStyle (IStreamProvider provider) + { + using (var stream = provider.Open ()) { + string styleName = ScanStyle (stream); + styleLookup.Remove (styleName); + } + } + + public static ColorScheme DefaultColorStyle { + get { + return GetColorStyle ("Default"); + } + } + + static SyntaxModeService () + { + var textEditorAssembly = Assembly.Load ("Mono.TextEditor"); + if (textEditorAssembly != null) { + LoadStylesAndModes (textEditorAssembly); + } else { + LoggingService.LogError ("Can't lookup Mono.TextEditor assembly. Default styles won't be loaded."); + } + } + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ConditionalRegion.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/TemplateCodon.cs index f0d1bb4c98..bd57ac22d0 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ConditionalRegion.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/TemplateCodon.cs @@ -1,4 +1,4 @@ -// ConditionalRegion.cs +// TemplateCodon.cs // // Author: // Mike Krüger <mkrueger@novell.com> @@ -23,66 +23,42 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // +using System.IO; using System; -using System.Collections.Generic; -using ICSharpCode.NRefactory.TypeSystem; -using ICSharpCode.NRefactory; +using System.Xml; -namespace MonoDevelop.Ide.TypeSystem +using Mono.Addins; + +namespace MonoDevelop.Ide.Editor.Highlighting { - [Serializable] - public class ConditionBlock + [ExtensionNode (Description="A template for color and syntax shemes.")] + class TemplateCodon : ExtensionNode, IStreamProvider { - public string Flag { - get; - set; - } + [NodeAttribute("resource", "Name of the resource where the template is stored.")] + string resource; - public DomRegion Region { - get; - set; - } - - public TextLocation Start { - get; - set; - } + [NodeAttribute("file", "Name of the file where the template is stored.")] + string file; - public TextLocation End { - get; - set; - } - - public ConditionBlock (string flag) : this (flag, TextLocation.Empty) - { - } - - public ConditionBlock (string flag, TextLocation start) + public TemplateCodon () { - this.Flag = flag; - this.Start = start; - this.Region = DomRegion.Empty; - } - } - - [Serializable] - public class ConditionalRegion : ConditionBlock - { - public DomRegion ElseBlock { - get; - set; + resource = file = null; } - List<ConditionBlock> conditionBlocks = new List<ConditionBlock> (); - - public List<ConditionBlock> ConditionBlocks { - get { - return conditionBlocks; - } - } - - public ConditionalRegion (string flag) : base (flag) + public Stream Open () { + Stream stream; + if (!string.IsNullOrEmpty (file)) { + stream = File.OpenRead (Addin.GetFilePath (file)); + } else if (!string.IsNullOrEmpty (resource)) { + stream = Addin.GetResource (resource); + if (stream == null) + throw new ApplicationException ("Template " + resource + " not found"); + } else { + throw new InvalidOperationException ("Template file or resource not provided"); + } + + return stream; } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/TemplateExtensionNodeLoader.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/TemplateExtensionNodeLoader.cs new file mode 100644 index 0000000000..ddf704e991 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/TemplateExtensionNodeLoader.cs @@ -0,0 +1,55 @@ +// TemplateExtensionNodeLoader.cs +// +// Author: +// Mike Krüger <mkrueger@novell.com> +// +// Copyright (c) 2008 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +using System; +using Mono.Addins; +using MonoDevelop.Ide.Editor.Highlighting; + +namespace MonoDevelop.Ide.Editor.Highlighting +{ + public static class TemplateExtensionNodeLoader + { + static bool initialized = false; + + public static void Init () + { + if (initialized) + return; + initialized = true; + AddinManager.AddExtensionNodeHandler ("/MonoDevelop/SourceEditor2/Styles", OnStylesExtensionChanged); + } + + static void OnStylesExtensionChanged (object s, ExtensionNodeEventArgs args) + { + TemplateCodon codon = (TemplateCodon)args.ExtensionNode; + if (args.Change == ExtensionChange.Add) { + SyntaxModeService.AddStyle (codon); + } else { + SyntaxModeService.RemoveStyle (codon); + } + } + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Util/Diff.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Util/Diff.cs new file mode 100644 index 0000000000..364b0f2288 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Util/Diff.cs @@ -0,0 +1,587 @@ +// +// Diff.cs +// +// Author: +// Matthias Hertel, http://www.mathertel.de// +// some tweaks made by Mike Krüger <mkrueger@novell.com> +// +// Copyright (c) by Matthias Hertel, http://www.mathertel.de// +// +// 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. +// +// diff.cs: A port of the algorythm to C# +// Copyright (c) by Matthias Hertel, http://www.mathertel.de +// This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx +// +// This Class implements the Difference Algorithm published in +// "An O(ND) Difference Algorithm and its Variations" by Eugene Myers +// Algorithmica Vol. 1 No. 2, 1986, p 251. +// +// There are many C, Java, Lisp implementations public available but they all seem to come +// from the same source (diffutils) that is under the (unfree) GNU public License +// and cannot be reused as a sourcecode for a commercial application. +// There are very old C implementations that use other (worse) algorithms. +// Microsoft also published sourcecode of a diff-tool (windiff) that uses some tree data. +// Also, a direct transfer from a C source to C# is not easy because there is a lot of pointer +// arithmetic in the typical C solutions and i need a managed solution. +// These are the reasons why I implemented the original published algorithm from the scratch and +// make it avaliable without the GNU license limitations. +// I do not need a high performance diff tool because it is used only sometimes. +// I will do some performace tweaking when needed. +// +// The algorithm itself is comparing 2 arrays of numbers so when comparing 2 text documents +// each line is converted into a (hash) number. See DiffText(). +// +// Some chages to the original algorithm: +// The original algorithm was described using a recursive approach and comparing zero indexed arrays. +// Extracting sub-arrays and rejoining them is very performance and memory intensive so the same +// (readonly) data arrays are passed arround together with their lower and upper bounds. +// This circumstance makes the LCS and SMS functions more complicate. +// I added some code to the LCS function to get a fast response on sub-arrays that are identical, +// completely deleted or inserted. +// +// The result from a comparisation is stored in 2 arrays that flag for modified (deleted or inserted) +// lines in the 2 data arrays. These bits are then analysed to produce a array of Hunk objects. +// +// Further possible optimizations: +// (first rule: don't do it; second: don't do it yet) +// The arrays DataA and DataB are passed as parameters, but are never changed after the creation +// so they can be members of the class to avoid the paramter overhead. +// In SMS is a lot of boundary arithmetic in the for-D and for-k loops that can be done by increment +// and decrement of local variables. +// The DownVector and UpVector arrays are alywas created and destroyed each time the SMS gets called. +// It is possible to reuse tehm when transfering them to members of the class. +// See TODO: hints. +// +// Changes: +// 2002.09.20 There was a "hang" in some situations. +// Now I undestand a little bit more of the SMS algorithm. +// There have been overlapping boxes; that where analyzed partial differently. +// One return-point is enough. +// A assertion was added in CreateDiffs when in debug-mode, that counts the number of equal (no modified) lines in both arrays. +// They must be identical. +// +// 2003.02.07 Out of bounds error in the Up/Down vector arrays in some situations. +// The two vetors are now accessed using different offsets that are adjusted using the start k-Line. +// A test case is added. +// +// 2006.03.05 Some documentation and a direct Diff entry point. +// +// 2006.03.08 Refactored the API to static methods on the Diff class to make usage simpler. +// 2006.03.10 using the standard Debug class for self-test now. +// compile with: csc /target:exe /out:diffTest.exe /d:DEBUG /d:TRACE /d:SELFTEST Diff.cs +// 2007.01.06 license agreement changed to a BSD style license. +// 2007.06.03 added the Optimize method. +// 2007.09.23 UpVector and DownVector optimization by Jan Stoklasa (). +// 2008.05.31 Adjusted the testing code that failed because of the Optimize method (not a bug in the diff algorithm). +// 2008.10.08 Fixing a test case and adding a new test case. + +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace MonoDevelop.Ide.Editor.Util +{ + /// <summary> + /// A DiffHunk represents a single change in a diff between two files. + /// </summary> + public struct DiffHunk + { + public static readonly DiffHunk Empty = new DiffHunk (0, 0, 0, 0); + + /// <summary> + /// Gets a value indicating whether this instance is empty. + /// </summary> + /// <value><c>true</c> if this instance is empty; otherwise, <c>false</c>.</value> + public bool IsEmpty { + get { + return InsertStart <= 0; + } + } + + // TODO: Add option to change this value. + internal readonly int Context; + + /// <summary> + /// Line number where the insertion starts. + /// </summary> + public readonly int InsertStart; + + /// <summary> + /// Line number where the removal starts. + /// </summary> + public readonly int RemoveStart; + + /// <summary> + /// Number of lines removed. + /// </summary> + public readonly int Removed; + + /// <summary> + /// Number of lines inserted. + /// </summary> + public readonly int Inserted; + + public DiffHunk (int removeStart, int insertStart, int removed, int inserted) + { + this.InsertStart = insertStart; + this.RemoveStart = removeStart; + this.Removed = removed; + this.Inserted = inserted; + this.Context = 3; + } + + public int DistanceTo(DiffHunk other) + { + return other.RemoveStart - (this.RemoveStart + this.Removed); + } + + public bool Overlaps(DiffHunk other) + { + return DistanceTo (other) < this.Context * 2; + } + + public static bool operator ==(DiffHunk left, DiffHunk right) + { + return left.InsertStart == right.InsertStart && left.RemoveStart == right.RemoveStart && + left.Removed == right.Removed && left.Inserted == right.Inserted; + } + + public static bool operator !=(DiffHunk left, DiffHunk right) + { + return !(left == right); + } + + public override bool Equals (object obj) + { + if (!(obj is DiffHunk)) + return false; + return ((DiffHunk)obj) == this; + } + + public override int GetHashCode () + { + return InsertStart ^ RemoveStart ^ Inserted ^ Removed; + } + + public override string ToString () + { + if (IsEmpty) + return"[Hunk: Empty]"; + return string.Format ("[Hunk: InsertStart={0}, RemoveStart={1}, Removed={2}, Inserted={3}]", InsertStart, RemoveStart, Removed, Inserted); + } + } + + sealed class Diff + { + /// <summary> + /// Shortest Middle Snake Return Data + /// </summary> + struct SMSRD + { + internal int x, y; + } + + static void Optimize<T> (DiffData<T> data) + { + int startPos = 0; + while (startPos < data.Length) { + while (startPos < data.Length && data.Modified[startPos] == false) + startPos++; + int endPos = startPos; + while (endPos < data.Length && data.Modified[endPos] == true) + endPos++; + + if (endPos < data.Length && data.Data[startPos].Equals (data.Data[endPos])) { + data.Modified[startPos] = false; + data.Modified[endPos] = true; + } else { + startPos = endPos; + } + } + } + + public static IEnumerable<DiffHunk> CharDiff (string left, string right) + { + return GetDiff (left != null ? left.ToCharArray () : new char[0], right != null ? right.ToCharArray () : new char[0]); + } + + public static IEnumerable<DiffHunk> GetDiff<T> (T[] baseArray, T[] changedArray) + { + // The A-Version of the data (original data) to be compared. + var dataA = new DiffData<T> (baseArray); + + // The B-Version of the data (modified data) to be compared. + var dataB = new DiffData<T> (changedArray); + + int MAX = dataA.Length + dataB.Length + 1; + // vector for the (0,0) to (x,y) search + int[] downVector = new int[2 * MAX + 2]; + // vector for the (u,v) to (N,M) search + int[] upVector = new int[2 * MAX + 2]; + + LCS (dataA, 0, dataA.Length, dataB, 0, dataB.Length, downVector, upVector); + return CreateDiffs (dataA, dataB); + } + + /// <summary>Scan the tables of which lines are inserted and deleted, + /// producing an edit script in forward order. + /// </summary> + /// dynamic array + static IEnumerable<DiffHunk> CreateDiffs<T> (DiffData<T> baseData, DiffData<T> changedData) + { + int lineA = 0; + int lineB = 0; + while (lineA < baseData.Length || lineB < changedData + .Length) { + if (lineA < baseData.Length && !baseData.Modified[lineA] && lineB < changedData + .Length && !changedData + .Modified[lineB]) { + // equal lines + lineA++; + lineB++; + + } else { + // maybe deleted and/or inserted lines + int startA = lineA; + int startB = lineB; + + while (lineA < baseData.Length && (lineB >= changedData + .Length || baseData.Modified[lineA])) + // while (LineA < DataA.Length && DataA.Modified[LineA]) + lineA++; + + while (lineB < changedData + .Length && (lineA >= baseData.Length || changedData + .Modified[lineB])) + // while (LineB < DataB.Length && DataB.Modified[LineB]) + lineB++; + + if (startA < lineA || startB < lineB) { + // store a new difference-item + yield return new DiffHunk (startA + 1, startB + 1, lineA - startA, lineB - startB); + } + // if + } + // if + } + // while + } + + /// <summary> + /// This is the algorithm to find the Shortest Middle Snake (SMS). + /// </summary> + /// <param name="dataA">sequence A</param> + /// <param name="lowerA">lower bound of the actual range in DataA</param> + /// <param name="upperA">upper bound of the actual range in DataA (exclusive)</param> + /// <param name="dataB">sequence B</param> + /// <param name="lowerB">lower bound of the actual range in DataB</param> + /// <param name="upperB">upper bound of the actual range in DataB (exclusive)</param> + /// <param name="downVector">a vector for the (0,0) to (x,y) search. Passed as a parameter for speed reasons.</param> + /// <param name="upVector">a vector for the (u,v) to (N,M) search. Passed as a parameter for speed reasons.</param> + /// <returns>a MiddleSnakeData record containing x,y and u,v</returns> + static SMSRD SMS<T> (DiffData<T> dataA, int lowerA, int upperA, DiffData<T> dataB, int lowerB, int upperB, int[] downVector, int[] upVector) + { + SMSRD ret; + int MAX = dataA.Length + dataB.Length + 1; + + int downK = lowerA - lowerB; + // the k-line to start the forward search + int upK = upperA - upperB; + // the k-line to start the reverse search + int delta = (upperA - lowerA) - (upperB - lowerB); + bool oddDelta = (delta & 1) != 0; + + // The vectors in the publication accepts negative indexes. the vectors implemented here are 0-based + // and are access using a specific offset: UpOffset UpVector and DownOffset for DownVektor + int downOffset = MAX - downK; + int upOffset = MAX - upK; + + int MaxD = ((upperA - lowerA + upperB - lowerB) / 2) + 1; + + // Debug.Write(2, "SMS", String.Format("Search the box: A[{0}-{1}] to B[{2}-{3}]", LowerA, UpperA, LowerB, UpperB)); + + // init vectors + downVector[downOffset + downK + 1] = lowerA; + upVector[upOffset + upK - 1] = upperA; + + for (int D = 0; D <= MaxD; D++) { + + // Extend the forward path. + for (int k = downK - D; k <= downK + D; k += 2) { + // Debug.Write(0, "SMS", "extend forward path " + k.ToString()); + + // find the only or better starting point + int x, y; + if (k == downK - D) { + x = downVector[downOffset + k + 1]; + // down + } else { + x = downVector[downOffset + k - 1] + 1; + // a step to the right + if (k < downK + D && downVector[downOffset + k + 1] >= x) + x = downVector[downOffset + k + 1]; + // down + } + y = x - k; + + // find the end of the furthest reaching forward D-path in diagonal k. + while (x < upperA && y < upperB && dataA.Data[x].Equals (dataB.Data[y])) { + x++; + y++; + } + downVector[downOffset + k] = x; + + // overlap ? + if (oddDelta && upK - D < k && k < upK + D) { + if (upVector[upOffset + k] <= downVector[downOffset + k]) { + ret.x = downVector[downOffset + k]; + ret.y = downVector[downOffset + k] - k; + // ret.u = UpVector[UpOffset + k]; // 2002.09.20: no need for 2 points + // ret.v = UpVector[UpOffset + k] - k; + return (ret); + } + // if + } + // if + } + // for k + // Extend the reverse path. + for (int k = upK - D; k <= upK + D; k += 2) { + // Debug.Write(0, "SMS", "extend reverse path " + k.ToString()); + + // find the only or better starting point + int x, y; + if (k == upK + D) { + x = upVector[upOffset + k - 1]; + // up + } else { + x = upVector[upOffset + k + 1] - 1; + // left + if (k > upK - D && upVector[upOffset + k - 1] < x) + x = upVector[upOffset + k - 1]; + // up + } + // if + y = x - k; + + while (x > lowerA && y > lowerB && dataA.Data[x - 1].Equals (dataB.Data[y - 1])) { + x--; + y--; + // diagonal + } + upVector[upOffset + k] = x; + + // overlap ? + if (!oddDelta && downK - D <= k && k <= downK + D) { + if (upVector[upOffset + k] <= downVector[downOffset + k]) { + ret.x = downVector[downOffset + k]; + ret.y = downVector[downOffset + k] - k; + // ret.u = UpVector[UpOffset + k]; // 2002.09.20: no need for 2 points + // ret.v = UpVector[UpOffset + k] - k; + return (ret); + } + // if + } + // if + } + // for k + } + // for D + throw new ApplicationException ("the algorithm should never come here."); + } + // SMS + + /// <summary> + /// This is the divide-and-conquer implementation of the longest common-subsequence (LCS) + /// algorithm. + /// The published algorithm passes recursively parts of the A and B sequences. + /// To avoid copying these arrays the lower and upper bounds are passed while the sequences stay constant. + /// </summary> + /// <param name="dataA">sequence A</param> + /// <param name="lowerA">lower bound of the actual range in DataA</param> + /// <param name="upperA">upper bound of the actual range in DataA (exclusive)</param> + /// <param name="dataB">sequence B</param> + /// <param name="lowerB">lower bound of the actual range in DataB</param> + /// <param name="upperB">upper bound of the actual range in DataB (exclusive)</param> + /// <param name="downVector">a vector for the (0,0) to (x,y) search. Passed as a parameter for speed reasons.</param> + /// <param name="upVector">a vector for the (u,v) to (N,M) search. Passed as a parameter for speed reasons.</param> + static void LCS<T> (DiffData<T> dataA, int lowerA, int upperA, DiffData<T> dataB, int lowerB, int upperB, int[] downVector, int[] upVector) + { + // Fast walkthrough equal lines at the start + while (lowerA < upperA && lowerB < upperB && dataA.Data[lowerA].Equals (dataB.Data[lowerB])) { + lowerA++; + lowerB++; + } + + // Fast walkthrough equal lines at the end + while (lowerA < upperA && lowerB < upperB && dataA.Data[upperA - 1].Equals (dataB.Data[upperB - 1])) { + --upperA; + --upperB; + } + + if (lowerA == upperA) { + // mark as inserted lines. + while (lowerB < upperB) + dataB.Modified[lowerB++] = true; + + } else if (lowerB == upperB) { + // mark as deleted lines. + while (lowerA < upperA) + dataA.Modified[lowerA++] = true; + + } else { + // Find the middle snakea and length of an optimal path for A and B + SMSRD smsrd = SMS (dataA, lowerA, upperA, dataB, lowerB, upperB, downVector, upVector); + // Debug.Write(2, "MiddleSnakeData", String.Format("{0},{1}", smsrd.x, smsrd.y)); + + // The path is from LowerX to (x,y) and (x,y) to UpperX + LCS (dataA, lowerA, smsrd.x, dataB, lowerB, smsrd.y, downVector, upVector); + LCS (dataA, smsrd.x, upperA, dataB, smsrd.y, upperB, downVector, upVector); + // 2002.09.20: no need for 2 points + } + } + // LCS() + + static void WriteHunks (Queue<DiffHunk> qh, IReadonlyTextDocument baseDocument, IReadonlyTextDocument changedDocument, StringBuilder sb) + { + DiffHunk item; + int remStart; + int insStart; + int distance = 0; + + do { + item = qh.Dequeue (); + remStart = System.Math.Max (1, item.RemoveStart - (distance != 0 ? distance : item.Context)); + insStart = System.Math.Max (1, item.InsertStart - (distance != 0 ? distance : item.Context)); + + for (int i = System.Math.Min (remStart, insStart); i < item.RemoveStart; i++) { + sb.AppendLine (" " + baseDocument.GetLineText (i, false)); + } + for (int i = item.RemoveStart; i < item.RemoveStart + item.Removed; i++) { + sb.AppendLine ("-" + baseDocument.GetLineText (i, false)); + } + for (int i = item.InsertStart; i < item.InsertStart + item.Inserted; i++) { + sb.AppendLine ("+" + changedDocument.GetLineText (i, false)); + } + + if (qh.Count != 0) + distance = item.DistanceTo (qh.Peek ()); + } while (qh.Count != 0); + + int remEnd = System.Math.Min (baseDocument.LineCount, item.RemoveStart + item.Removed + item.Context); + for (int i = item.RemoveStart + item.Removed; i < remEnd; i++) { + sb.AppendLine (" " + baseDocument.GetLineText (i, false)); + } + } + + public static string GetDiffString (IReadonlyTextDocument baseDocument, IReadonlyTextDocument changedDocument) + { + return GetDiffString (baseDocument.GetDiff (changedDocument), baseDocument, changedDocument, baseDocument.FileName, changedDocument.FileName); + } + + public static string GetDiffString (IEnumerable<DiffHunk> diff, IReadonlyTextDocument baseDocument, IReadonlyTextDocument changedDocument, string baseFileName, string changedFileName) + { + if (diff == null) + return ""; + + StringBuilder sb = new StringBuilder (); + IEnumerator<DiffHunk> he = diff.GetEnumerator (); + he.MoveNext (); + + Queue<DiffHunk> qh = new Queue<DiffHunk> (); + DiffHunk current; + DiffHunk next; + + if (he.Current.IsEmpty) + return ""; + + sb.AppendLine ("--- " + baseFileName); + sb.AppendLine ("+++ " + changedFileName); + + current = he.Current; + + qh.Enqueue (current); + int remStart = System.Math.Max (1, current.RemoveStart - current.Context); + int remEnd = System.Math.Min (baseDocument.LineCount, current.RemoveStart + current.Removed + current.Context); + int insStart = System.Math.Max (1, current.InsertStart - current.Context); + int insEnd = System.Math.Min (changedDocument.LineCount, current.InsertStart + current.Inserted + current.Context); + + while (he.MoveNext ()) { + next = he.Current; + + if (current.Overlaps (next)) { + // Change upper bounds. + remEnd = System.Math.Min (baseDocument.LineCount, next.RemoveStart + next.Removed + next.Context); + insEnd = System.Math.Min (changedDocument.LineCount, next.InsertStart + next.Inserted + next.Context); + } else { + sb.AppendLine ("@@ -" + remStart + "," + (remEnd - remStart) + " +" + insStart + "," + (insEnd - insStart) + " @@"); + WriteHunks (qh, baseDocument, changedDocument, sb); + + remStart = System.Math.Max (1, next.RemoveStart - next.Context); + remEnd = System.Math.Min (baseDocument.LineCount, next.RemoveStart + next.Removed + next.Context); + insStart = System.Math.Max (1, next.InsertStart - next.Context); + insEnd = System.Math.Min (changedDocument.LineCount, next.InsertStart + next.Inserted + next.Context); + } + qh.Enqueue (next); + + current = next; + } + + if (qh.Count != 0) { + sb.AppendLine ("@@ -" + remStart + "," + (remEnd - remStart) + " +" + insStart + "," + (insEnd - insStart) + " @@"); + WriteHunks (qh, baseDocument, changedDocument, sb); + } + return sb.ToString (); + } + } + + /// <summary>Data on one input file being compared. + /// </summary> + class DiffData<T> + { + /// <summary>Number of elements (lines).</summary> + public readonly int Length; + + /// <summary>Buffer of numbers that will be compared.</summary> + public readonly T[] Data; + + /// <summary> + /// Array of booleans that flag for modified data. + /// This is the result of the diff. + /// This means deletedA in the first Data or inserted in the second Data. + /// </summary> + public readonly bool[] Modified; + + /// <summary> + /// Initialize the Diff-Data buffer. + /// </summary> + /// <param name="initData">reference to the buffer</param> + public DiffData (T[] initData) + { + Data = initData; + Length = initData.Length; + Modified = new bool[Length + 2]; + } + // DiffData + } + // class DiffData +} +// namespace diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Util/SimpleBracketMatcher.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Util/SimpleBracketMatcher.cs new file mode 100644 index 0000000000..b7cc92bb45 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Util/SimpleBracketMatcher.cs @@ -0,0 +1,273 @@ +// +// SimpleBracketMatcher.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; +using MonoDevelop.Ide.Editor.Highlighting; + +namespace MonoDevelop.Ide.Editor.Util +{ + public static class SimpleBracketMatcher + { + const string openBrackets = "([{<"; + const string closingBrackets = ")]}>"; + + + public static int GetMatchingBracketOffset (IReadonlyTextDocument document, int offset) + { + if (offset < 0 || offset >= document.Length) + return -1; + char ch = document.GetCharAt (offset); + int bracket = openBrackets.IndexOf (ch); + int result; + if (bracket >= 0) { + result = SearchMatchingBracketForward (document, offset + 1, closingBrackets [bracket], openBrackets [bracket]); + } else { + bracket = closingBrackets.IndexOf (ch); + if (bracket >= 0) { + result = SearchMatchingBracketBackward (document, offset - 1, openBrackets [bracket], closingBrackets [bracket]); + } else { + result = -1; + } + } + return result; + } + + static readonly string [] emptyList = new string [0]; + static string [] GetList (IReadonlyTextDocument document, string name) + { + return TextEditorFactory.GetSyntaxProperties (document.MimeType, name) ?? emptyList; + } + + static int StartsWithListMember (IReadonlyTextDocument document, IList<string> list, int offset) + { + if (document == null) + throw new ArgumentNullException ("document"); + if (list == null) + throw new ArgumentNullException ("list"); + for (int i = 0; i < list.Count; i++) { + string item = list [i]; + if (offset + item.Length < document.Length) { + if (document.GetTextAt (offset, item.Length) == item) + return i; + } + } + return -1; + } + + static int SearchMatchingBracketForward (IReadonlyTextDocument document, int offset, char openBracket, char closingBracket) + { + bool isInBlockComment = false; + bool isInLineComment = false; + int curStringQuote = -1; + + bool startsInLineComment = StartsInLineComment (document, offset); + + var lineComments = GetList (document, "LineComment"); + var blockCommentStarts = GetList (document, "BlockCommentStart"); + var blockCommentEnds = GetList (document, "BlockCommentEnd"); + var stringQuotes = GetList (document, "StringQuote"); + int depth = -1; + while (offset >= 0 && offset < document.Length) { + if (curStringQuote < 0) { + // check line comments + if (!isInBlockComment && !isInLineComment) + isInLineComment = StartsWithListMember (document, lineComments, offset) >= 0; + + // check block comments + if (!isInLineComment) { + if (!isInBlockComment) { + isInBlockComment = StartsWithListMember (document, blockCommentStarts, offset) >= 0; + } else { + isInBlockComment = StartsWithListMember (document, blockCommentEnds, offset) < 0; + } + } + } + + if (!isInBlockComment && !isInLineComment) { + int i = StartsWithListMember (document, stringQuotes, offset); + if (i >= 0) { + if (curStringQuote >= 0) { + if (curStringQuote == i) + curStringQuote = -1; + } else { + curStringQuote = i; + } + } + } + + char ch = document.GetCharAt (offset); + switch (ch) { + case '\n': + case '\r': + if (startsInLineComment) + return -1; + isInLineComment = false; + break; + default: + if (ch == closingBracket) { + if (!(isInLineComment || curStringQuote >= 0 || isInBlockComment)) + --depth; + } else if (ch == openBracket) { + if (!(isInLineComment || curStringQuote >= 0 || isInBlockComment)) { + ++depth; + if (depth == 0) + return offset; + } + } + break; + } + offset++; + } + return -1; + } + + static bool StartsInLineComment (IReadonlyTextDocument document, int offset) + { + IList<string> lineComments = GetList (document, "LineComment"); + var line = document.GetLineByOffset (offset); + for (int i = line.Offset; i < offset; i++) { + if (StartsWithListMember (document, lineComments, i) >= 0) + return true; + } + return false; + } + + static int GetLastSourceCodePosition (IReadonlyTextDocument document, int lineOffset) + { + var line = document.GetLineByOffset (lineOffset); + bool isInBlockComment = false; + bool isInLineComment = false; + int curStringQuote = -1; + + IList<string> lineComments = GetList (document, "LineComment"); + IList<string> blockCommentStarts = GetList (document, "BlockCommentStart"); + IList<string> blockCommentEnds = GetList (document, "BlockCommentEnd"); + IList<string> stringQuotes = GetList (document, "StringQuote"); + + for (int i = 0; i < line.Length; i++) { + int offset = line.Offset + i; + // check line comments + if (!isInBlockComment && curStringQuote < 0) { + isInLineComment = StartsWithListMember (document, lineComments, offset) >= 0; + if (isInLineComment) + return System.Math.Min (offset, lineOffset); + } + // check block comments + if (!isInLineComment && curStringQuote < 0) { + if (!isInBlockComment) { + isInBlockComment = StartsWithListMember (document, blockCommentStarts, offset) >= 0; + } else { + isInBlockComment = StartsWithListMember (document, blockCommentEnds, offset) < 0; + } + } + + if (!isInBlockComment && !isInLineComment) { + int j = StartsWithListMember (document, stringQuotes, offset); + if (j >= 0) { + if (curStringQuote >= 0) { + if (curStringQuote == j) + curStringQuote = -1; + } else { + curStringQuote = j; + } + } + } + } + return lineOffset; + } + + static int SearchMatchingBracketBackward (IReadonlyTextDocument document, int offset, char openBracket, char closingBracket) + { + bool isInBlockComment = false; + bool isInLineComment = false; + int curStringQuote = -1; + + IList<string> blockCommentStarts = GetList (document, "BlockCommentStart"); + IList<string> blockCommentEnds = GetList (document, "BlockCommentEnd"); + IList<string> stringQuotes = GetList (document, "StringQuote"); + + bool startsInLineComment = StartsInLineComment (document, offset); + int depth = -1; + + if (!startsInLineComment) + offset = GetLastSourceCodePosition (document, offset); + + while (offset >= 0 && offset < document.Length) { + char ch = document.GetCharAt (offset); + + // check block comments + if (!isInLineComment && curStringQuote < 0) { + if (!isInBlockComment) { + isInBlockComment = StartsWithListMember (document, blockCommentEnds, offset) >= 0; + } else { + isInBlockComment = StartsWithListMember (document, blockCommentStarts, offset) < 0; + } + } + + if (!isInBlockComment && !isInLineComment) { + int i = StartsWithListMember (document, stringQuotes, offset); + if (i >= 0) { + if (curStringQuote >= 0) { + if (curStringQuote == i) + curStringQuote = -1; + } else { + curStringQuote = i; + } + } + } + + switch (ch) { + case '\n': + case '\r': + if (startsInLineComment) + return -1; + offset--; + while (offset > 0 && (document.GetCharAt (offset) == '\n' || document.GetCharAt (offset) == '\r')) { + offset--; + } + offset = GetLastSourceCodePosition (document, offset) + 1; + break; + default: + if (ch == closingBracket) { + if (!(curStringQuote >= 0 || isInBlockComment)) + --depth; + } else if (ch == openBracket) { + if (!(curStringQuote >= 0 || isInBlockComment)) { + ++depth; + if (depth == 0) + return offset; + } + } + break; + } + offset--; + } + return -1; + } + } + +} + diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Util/SimpleReadonlyDocument.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Util/SimpleReadonlyDocument.cs new file mode 100644 index 0000000000..2b03c40335 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Util/SimpleReadonlyDocument.cs @@ -0,0 +1,414 @@ +// +// SimpleReadonlyDocument.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.Threading.Tasks; +using MonoDevelop.Core; +using System.Threading; + +namespace MonoDevelop.Ide.Editor.Util +{ + /// <summary> + /// A simple and fast implementation for a read only text document. + /// </summary> + public class SimpleReadonlyDocument : IReadonlyTextDocument + { + readonly ITextSource textSource; + readonly List<Delimiter> delimiters = new List<Delimiter> (); + + SimpleReadonlyDocument (ITextSource readOnlyTextSource, string fileName, string mimeType) + { + textSource = readOnlyTextSource; + FileName = fileName; + MimeType = mimeType; + Initalize (readOnlyTextSource.Text); + } + + /// <summary> + /// Creates a new readonly document. Note that the text source is not copied - it needs to be read only. + /// </summary> + /// <returns>The readonly document async.</returns> + /// <param name="readOnlyTextSource">Read only text source.</param> + /// <param name="fileName">File name.</param> + public static Task<IReadonlyTextDocument> CreateReadonlyDocumentAsync (ITextSource readOnlyTextSource, string fileName = null, string mimeType = null, CancellationToken cancellationToken = default(CancellationToken)) + { + return Task.Run (delegate { + return (IReadonlyTextDocument)new SimpleReadonlyDocument (readOnlyTextSource, fileName, mimeType); + }, cancellationToken); + } + + void Initalize (string text) + { + int offset = 0; + while (true) { + var delimiter = NextDelimiter (text, offset); + if (delimiter.IsInvalid) + break; + + delimiters.Add (delimiter); + + offset = delimiter.EndOffset; + } + } + + static unsafe Delimiter NextDelimiter (string text, int offset) + { + fixed (char* start = text) { + char* p = start + offset; + char* endPtr = start + text.Length; + + while (p < endPtr) { + switch (*p) { + case NewLine.CR: + char* nextp = p + 1; + if (nextp < endPtr && *nextp == NewLine.LF) + return new Delimiter ((int)(p - start), UnicodeNewline.CRLF); + return new Delimiter ((int)(p - start), UnicodeNewline.CR); + case NewLine.LF: + return new Delimiter ((int)(p - start), UnicodeNewline.LF); + case NewLine.NEL: + return new Delimiter ((int)(p - start), UnicodeNewline.NEL); + case NewLine.VT: + return new Delimiter ((int)(p - start), UnicodeNewline.VT); + case NewLine.FF: + return new Delimiter ((int)(p - start), UnicodeNewline.FF); + case NewLine.LS: + return new Delimiter ((int)(p - start), UnicodeNewline.LS); + case NewLine.PS: + return new Delimiter ((int)(p - start), UnicodeNewline.PS); + } + p++; + } + return Delimiter.Invalid; + } + } + + struct Delimiter + { + public static readonly Delimiter Invalid = new Delimiter (-1, 0); + + public readonly int Offset; + public readonly UnicodeNewline UnicodeNewline; + + public int Length { + get { + return UnicodeNewline == UnicodeNewline.CRLF ? 2 : 1; + } + } + + public int EndOffset { + get { return Offset + Length; } + } + + public bool IsInvalid { + get { + return Offset < 0; + } + } + + public Delimiter (int offset, UnicodeNewline unicodeNewline) + { + Offset = offset; + UnicodeNewline = unicodeNewline; + } + } + + int OffsetToLineNumber (int offset) + { + for (int i = 0; i < delimiters.Count; i++) { + var delimiter = delimiters[i]; + if (offset <= delimiter.Offset) + return i + 1; + } + return delimiters.Count + 1; + } + + #region IReadonlyTextDocument implementation + + /// <inheritdoc/> + public int LocationToOffset (int line, int column) + { + if (line > LineCount || line < DocumentLocation.MinLine) + return -1; + var documentLine = GetLine (line); + return Math.Min (Length, documentLine.Offset + Math.Max (0, Math.Min (documentLine.Length, column - 1))); + } + + /// <inheritdoc/> + public DocumentLocation OffsetToLocation (int offset) + { + int lineNr = OffsetToLineNumber (offset); + if (lineNr < 1) + return DocumentLocation.Empty; + var line = GetLine (lineNr); + var col = Math.Max (1, Math.Min (line.LengthIncludingDelimiter, offset - line.Offset) + 1); + return new DocumentLocation (lineNr, col); + } + + /// <inheritdoc/> + public IDocumentLine GetLine (int number) + { + number--; + if (number < 0) + return null; + int startOffset = number > 0 ? delimiters[number - 1].EndOffset : 0; + int endOffset; + UnicodeNewline newLine; + if (number < delimiters.Count) { + endOffset = delimiters[number].EndOffset; + newLine = delimiters[number].UnicodeNewline; + } else { + endOffset = Length; + newLine = UnicodeNewline.Unknown; + } + return new SimpleLineSegment (this, number + 1, startOffset, endOffset - startOffset, newLine); + } + + sealed class SimpleLineSegment : IDocumentLine + { + readonly SimpleReadonlyDocument splitter; + + public SimpleLineSegment (SimpleReadonlyDocument splitter, int lineNumber, int offset, int length, UnicodeNewline newLine) + { + this.splitter = splitter; + LineNumber = lineNumber; + LengthIncludingDelimiter = length; + UnicodeNewline = newLine; + Offset = offset; + } + + #region IDocumentLine implementation + + public int LengthIncludingDelimiter { + get; + private set; + } + + public int EndOffsetIncludingDelimiter { + get { + return Offset + LengthIncludingDelimiter; + } + } + + public ISegment SegmentIncludingDelimiter { + get { + return new TextSegment (Offset, LengthIncludingDelimiter); + } + } + + public UnicodeNewline UnicodeNewline { + get; + private set; + } + + public int DelimiterLength { + get { + switch (UnicodeNewline) { + case UnicodeNewline.Unknown: + return 0; + case UnicodeNewline.CRLF: + return 2; + default: + return 1; + } + } + } + + public int LineNumber { + get; + private set; + } + + public IDocumentLine PreviousLine { + get { + if (LineNumber == 1) + return null; + return splitter.GetLine (LineNumber - 1); + } + } + + public IDocumentLine NextLine { + get { + if (LineNumber >= splitter.LineCount) + return null; + return splitter.GetLine (LineNumber + 1); + } + } + + public bool IsDeleted { + get { + return false; + } + } + #endregion + + #region ISegment implementation + + public int Offset { + get; + private set; + } + + public int Length { + get { + return LengthIncludingDelimiter - DelimiterLength; + } + } + + public int EndOffset { + get { + return Offset + Length; + } + } + #endregion + } + + /// <inheritdoc/> + public IDocumentLine GetLineByOffset (int offset) + { + return GetLine (OffsetToLineNumber (offset)); + } + + /// <inheritdoc/> + public bool IsReadOnly { + get { + return true; + } + } + + /// <inheritdoc/> + public FilePath FileName { + get; + private set; + } + + /// <inheritdoc/> + public string MimeType { + get; + private set; + } + + /// <inheritdoc/> + public int LineCount { + get { + return delimiters.Count + 1; + } + } + #endregion + + #region ITextSource implementation + + /// <inheritdoc/> + public char GetCharAt (int offset) + { + return textSource.GetCharAt (offset); + } + + public char this [int offset] { + get { + return textSource.GetCharAt (offset); + } + } + + /// <inheritdoc/> + public string GetTextAt (int offset, int length) + { + return textSource.GetTextAt (offset, length); + } + + /// <inheritdoc/> + public System.IO.TextReader CreateReader () + { + return textSource.CreateReader (); + } + + /// <inheritdoc/> + public System.IO.TextReader CreateReader (int offset, int length) + { + return textSource.CreateReader (offset, length); + } + + /// <inheritdoc/> + public void WriteTextTo (System.IO.TextWriter writer) + { + textSource.WriteTextTo (writer); + } + + /// <inheritdoc/> + public void WriteTextTo (System.IO.TextWriter writer, int offset, int length) + { + textSource.WriteTextTo (writer, offset, length); + } + + /// <inheritdoc/> + public ITextSourceVersion Version { + get { + return textSource.Version; + } + } + + /// <inheritdoc/> + public bool UseBOM { + get { + return textSource.UseBOM; + } + } + + /// <inheritdoc/> + public System.Text.Encoding Encoding { + get { + return textSource.Encoding; + } + } + + /// <inheritdoc/> + public int Length { + get { + return textSource.Length; + } + } + + /// <inheritdoc/> + public string Text { + get { + return textSource.Text; + } + } + + public ITextSource CreateSnapshot () + { + return this; + } + + public ITextSource CreateSnapshot (int offset, int length) + { + return new StringTextSource (Text.Substring (offset, length)); + } + #endregion + + } +}
\ No newline at end of file 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 & 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 + } +} + diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/FileProvider.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/FileProvider.cs index 4d2ee32f6f..c67934a5d8 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/FileProvider.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/FileProvider.cs @@ -31,7 +31,7 @@ using MonoDevelop.Ide.Gui; using System.Text; using MonoDevelop.Core; using System; -using Mono.TextEditor.Utils; +using MonoDevelop.Core.Text; namespace MonoDevelop.Ide.FindInFiles { @@ -145,7 +145,7 @@ namespace MonoDevelop.Ide.FindInFiles buffer.Insert (offset, replacement); if (document != null) { Gtk.Application.Invoke (delegate { - document.Editor.Replace (offset, length, replacement); + document.Editor.ReplaceText (offset, length, replacement); }); return; } @@ -159,7 +159,8 @@ namespace MonoDevelop.Ide.FindInFiles undoGroup.Dispose (); undoGroup = null; } - document.Editor.Document.CommitUpdateAll (); }); + /*document.Editor.Document.CommitUpdateAll (); */ + }); return; } if (buffer != null && somethingReplaced) { @@ -167,6 +168,7 @@ namespace MonoDevelop.Ide.FindInFiles TextFileUtility.WriteText (FileName, buffer.ToString (), encoding, hadBom); DesktopService.SetFileAttributes (FileName, attributes); } + FileService.NotifyFileChanged (FileName); buffer = null; } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/FindInFilesDialog.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/FindInFilesDialog.cs index f4e64d6990..451299685d 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/FindInFilesDialog.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/FindInFilesDialog.cs @@ -37,6 +37,11 @@ using MonoDevelop.Ide.Gui.Content; namespace MonoDevelop.Ide.FindInFiles { + public enum PathMode { + Absolute, + Hidden + } + public partial class FindInFilesDialog : Gtk.Dialog { readonly bool writeScope = true; @@ -191,7 +196,7 @@ namespace MonoDevelop.Ide.FindInFiles toggleFindInFiles.Toggle (); if (IdeApp.Workbench.ActiveDocument != null) { - var view = IdeApp.Workbench.ActiveDocument.GetContent<ITextBuffer> (); + var view = IdeApp.Workbench.ActiveDocument.Editor; if (view != null) { string selectedText = FormatPatternToSelectionOption (view.SelectedText, properties.Get ("RegexSearch", false)); if (!string.IsNullOrEmpty (selectedText)) { @@ -668,9 +673,17 @@ namespace MonoDevelop.Ide.FindInFiles switch ((SearchScope) comboboxScope.Active) { case SearchScope.CurrentDocument: + if (IdeApp.Workbench.ActiveDocument == null) { + MessageService.ShowError (GettextCatalog.GetString ("Currently there is no open document.")); + return null; + } scope = new DocumentScope (); break; case SearchScope.Selection: + if (IdeApp.Workbench.ActiveDocument == null) { + MessageService.ShowError (GettextCatalog.GetString ("Currently there is no open document.")); + return null; + } scope = new SelectionScope (); break; case SearchScope.WholeWorkspace: @@ -699,6 +712,10 @@ namespace MonoDevelop.Ide.FindInFiles MessageService.ShowError (GettextCatalog.GetString ("Currently there is no open solution.")); return null; case SearchScope.AllOpenFiles: + if (IdeApp.Workbench.Documents.Count == 0) { + MessageService.ShowError (GettextCatalog.GetString ("Currently there are no open documents.")); + return null; + } scope = new AllOpenFilesScope (); break; case SearchScope.Directories: @@ -790,6 +807,9 @@ namespace MonoDevelop.Ide.FindInFiles ThreadPool.QueueUserWorkItem (delegate { CancellationTokenSource cancelSource = new CancellationTokenSource (); using (SearchProgressMonitor searchMonitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, cancellationTokenSource:cancelSource)) { + + searchMonitor.PathMode = scope.PathMode; + searchMonitor.ReportStatus (scope.GetDescription (options, pattern, null)); lock (searchesInProgress) searchesInProgress.Add (cancelSource); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/ISearchProgressMonitor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/ISearchProgressMonitor.cs index 0dacb70a51..85d7601f9b 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/ISearchProgressMonitor.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/ISearchProgressMonitor.cs @@ -38,5 +38,6 @@ namespace MonoDevelop.Ide.FindInFiles void ReportResult (SearchResult result); void ReportResults (IEnumerable<SearchResult> result); void ReportStatus (string resultMessage); + PathMode PathMode { set; } } }
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/MemberCollector.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/MemberCollector.cs index ab63fa2d69..de5119e2be 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/MemberCollector.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/MemberCollector.cs @@ -1,236 +1,236 @@ -// -// MemberCollector.cs -// -// Author: -// Mansheng Yang <lightyang0@gmail.com> -// -// Copyright (c) 2012 Mansheng Yang -// -// 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.Projects; -using MonoDevelop.Ide.TypeSystem; -using ICSharpCode.NRefactory.TypeSystem; - -namespace MonoDevelop.Ide.FindInFiles -{ - public static class MemberCollector - { - static bool MatchParameters (IMember a, IMember b) - { - return MatchParameters (a as IParameterizedMember, b as IParameterizedMember); - } - - static bool MatchParameters (IParameterizedMember a, IParameterizedMember b) - { - if (a == null && b == null) return true; - if (a == null || b == null) return false; - - return Equals (a.Compilation, a.Parameters, b.Parameters); - } - - #region Code from NRefactory ParameterListComparer - - public static bool Equals(ICompilation comp, IList<IParameter> x, IList<IParameter> y) - { - if (x == y) - return true; - if (x == null || y == null || x.Count != y.Count) - return false; - for (int i = 0; i < x.Count; i++) { - var a = x[i]; - var b = y[i]; - if (a == null && b == null) - continue; - if (a == null || b == null) - return false; - - // We want to consider the parameter lists "Method<T>(T a)" and "Method<S>(S b)" as equal. - // However, the parameter types are not considered equal, as T is a different type parameter than S. - // In order to compare the method signatures, we will normalize all method type parameters. - IType aType = a.Type.AcceptVisitor(normalizationVisitor); - IType bType = b.Type.AcceptVisitor(normalizationVisitor); - bType = comp.Import (bType); - if (!aType.Equals(bType)) - return false; - } - return true; - } - - sealed class NormalizeTypeVisitor : TypeVisitor - { - public override IType VisitTypeParameter(ITypeParameter type) - { - if (type.OwnerType == SymbolKind.Method) { - return ICSharpCode.NRefactory.TypeSystem.Implementation.DummyTypeParameter.GetMethodTypeParameter(type.Index); - } else { - return base.VisitTypeParameter(type); - } - } - - public override IType VisitTypeDefinition(ITypeDefinition type) - { - if (type.KnownTypeCode == KnownTypeCode.Object) - return SpecialType.Dynamic; - return base.VisitTypeDefinition(type); - } - } - - static readonly NormalizeTypeVisitor normalizationVisitor = new NormalizeTypeVisitor(); - - #endregion - - /// <summary> - /// find all base types(types that are not derived from other types) in the specified types - /// </summary> - /// <param name="types"></param> - /// <returns></returns> - public static IEnumerable<ITypeDefinition> GetBaseTypes (IEnumerable<ITypeDefinition> types) - { - if (types == null) - yield break; - types = types.ToList (); - if (!types.Any ()) - yield break; - - var baseType = types.FirstOrDefault (); - var otherTypes = new List<ITypeDefinition> (); - - foreach (var type in types.Skip (1)) { - if (baseType.IsDerivedFrom (type)) { - baseType = type; - } else if (!type.IsDerivedFrom (baseType)) { - // this type is not directly related to baseType - otherTypes.Add (type); - } - } - yield return baseType; - foreach (var type in GetBaseTypes (otherTypes)) - yield return type; - } - - static IEnumerable<IMember> GetMembers (ITypeDefinition type, IMember member, bool ignoreInherited, - Func<IMember, bool> filter) - { - var options = ignoreInherited ? GetMemberOptions.IgnoreInheritedMembers : GetMemberOptions.None; - var members = type.GetMembers (m => m.Name == member.Name, options); - -/* // Filter out shadowed members. - // class A { public string Foo { get; set; } } class B : A { public string Foo { get; set; } } - if (member.SymbolKind == SymbolKind.Property || !(member is IParameterizedMember)) { - members = members.Where (m => m == member || m.DeclaringType.Kind == TypeKind.Interface); - }*/ - if (filter != null) - members = members.Where (filter); - return members; - } - - static IEnumerable<ITypeDefinition> Import (ICompilation compilation, IEnumerable<ITypeDefinition> types) - { - return types.Select (t => compilation.Import (t)); - } - - /// <summary> - /// collect members with the same signature/name(if overloads are included) as the specified member - /// in the inheritance tree - /// </summary> - public static IEnumerable<IMember> CollectMembers (Solution solution, IMember member, ReferenceFinder.RefactoryScope scope, - bool includeOverloads = true, bool matchDeclaringType = false) - { - if (solution == null || member.SymbolKind == SymbolKind.Destructor || member.SymbolKind == SymbolKind.Operator) - return new [] { member }; - - if (member.SymbolKind == SymbolKind.Constructor) { - if (includeOverloads) - return member.DeclaringType.GetMembers (m => m.SymbolKind == SymbolKind.Constructor, GetMemberOptions.IgnoreInheritedMembers); - return new [] { member }; - } - - Func<IMember, bool> memberFilter = null; - if (member is IParameterizedMember && !includeOverloads) - memberFilter = m => MatchParameters (m, member); - - var declaringType = member.DeclaringTypeDefinition; - if (declaringType == null) - return new [] { member }; - // only collect members in declaringType - if (matchDeclaringType) - return GetMembers (declaringType, member, true, memberFilter); - - if (declaringType.Kind != TypeKind.Class && declaringType.Kind != TypeKind.Interface) - return GetMembers (declaringType, member, false, memberFilter); - - var searchTypes = new List<ITypeDefinition> (); - if (includeOverloads) { - var interfaces = from t in declaringType.GetAllBaseTypeDefinitions () - where t.Kind == TypeKind.Interface && GetMembers (t, member, true, memberFilter).Any () - select t; - searchTypes.AddRange (GetBaseTypes (interfaces)); - } - - if (member.DeclaringType.Kind == TypeKind.Class) { - var members = GetMembers (declaringType, member, false, memberFilter).ToList (); - if (members.Any (m => m.IsOverridable)) - searchTypes.AddRange (GetBaseTypes (members.Select (m => m.DeclaringTypeDefinition))); - else if (searchTypes.Count == 0) - return members; - } - - IList<ICompilation> compilations; - if (scope == ReferenceFinder.RefactoryScope.Solution || scope == ReferenceFinder.RefactoryScope.Unknown) { - var projects = SearchCollector.CollectProjects (solution, searchTypes); - compilations = projects.Select (TypeSystemService.GetCompilation).ToList (); - } else { - compilations = new [] { member.Compilation }; - } - - var result = new List<IMember> (); - var mainAssemblies = new HashSet<string> (compilations.Select (c => c.MainAssembly.AssemblyName)); - var searchedAssemblies = new HashSet<string> (); - var searchedTypes = new HashSet<string> (); - - foreach (var compilation in compilations) { - var baseTypeImports = Import(compilation, searchTypes).Where (t => t != null).ToList (); - if (!baseTypeImports.Any ()) continue; - - foreach (var assembly in compilation.Assemblies) { - // search main assemblies in their projects' own compilation, to avoid possible resolving problems - if ((mainAssemblies.Contains(assembly.AssemblyName) && assembly != compilation.MainAssembly) || - !searchedAssemblies.Add (assembly.AssemblyName)) - continue; - - foreach (var type in assembly.GetAllTypeDefinitions ()) { - // members in base types will also be added - // because IsDerivedFrom return true for a type itself - if (!searchedTypes.Add (type.ReflectionName) || !baseTypeImports.Any (type.IsDerivedFrom)) - continue; - result.AddRange (GetMembers (type, member, true, memberFilter)); - } - } - } - if (!result.Contains (member)) - result.Add (member); - return result; - } - - } -} - +//// +//// MemberCollector.cs +//// +//// Author: +//// Mansheng Yang <lightyang0@gmail.com> +//// +//// Copyright (c) 2012 Mansheng Yang +//// +//// 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.Projects; +//using MonoDevelop.Ide.TypeSystem; +//using ICSharpCode.NRefactory.TypeSystem; +// +//namespace MonoDevelop.Ide.FindInFiles +//{ +// public static class MemberCollector +// { +// static bool MatchParameters (IMember a, IMember b) +// { +// return MatchParameters (a as IParameterizedMember, b as IParameterizedMember); +// } +// +// static bool MatchParameters (IParameterizedMember a, IParameterizedMember b) +// { +// if (a == null && b == null) return true; +// if (a == null || b == null) return false; +// +// return Equals (a.Compilation, a.Parameters, b.Parameters); +// } +// +// #region Code from NRefactory ParameterListComparer +// +// public static bool Equals(ICompilation comp, IList<IParameter> x, IList<IParameter> y) +// { +// if (x == y) +// return true; +// if (x == null || y == null || x.Count != y.Count) +// return false; +// for (int i = 0; i < x.Count; i++) { +// var a = x[i]; +// var b = y[i]; +// if (a == null && b == null) +// continue; +// if (a == null || b == null) +// return false; +// +// // We want to consider the parameter lists "Method<T>(T a)" and "Method<S>(S b)" as equal. +// // However, the parameter types are not considered equal, as T is a different type parameter than S. +// // In order to compare the method signatures, we will normalize all method type parameters. +// IType aType = a.Type.AcceptVisitor(normalizationVisitor); +// IType bType = b.Type.AcceptVisitor(normalizationVisitor); +// bType = comp.Import (bType); +// if (!aType.Equals(bType)) +// return false; +// } +// return true; +// } +// +// sealed class NormalizeTypeVisitor : TypeVisitor +// { +// public override IType VisitTypeParameter(ITypeParameter type) +// { +// if (type.OwnerType == SymbolKind.Method) { +// return ICSharpCode.NRefactory.TypeSystem.Implementation.DummyTypeParameter.GetMethodTypeParameter(type.Index); +// } else { +// return base.VisitTypeParameter(type); +// } +// } +// +// public override IType VisitTypeDefinition(ITypeDefinition type) +// { +// if (type.KnownTypeCode == KnownTypeCode.Object) +// return SpecialType.Dynamic; +// return base.VisitTypeDefinition(type); +// } +// } +// +// static readonly NormalizeTypeVisitor normalizationVisitor = new NormalizeTypeVisitor(); +// +// #endregion +// +// /// <summary> +// /// find all base types(types that are not derived from other types) in the specified types +// /// </summary> +// /// <param name="types"></param> +// /// <returns></returns> +// public static IEnumerable<ITypeDefinition> GetBaseTypes (IEnumerable<ITypeDefinition> types) +// { +// if (types == null) +// yield break; +// types = types.ToList (); +// if (!types.Any ()) +// yield break; +// +// var baseType = types.FirstOrDefault (); +// var otherTypes = new List<ITypeDefinition> (); +// +// foreach (var type in types.Skip (1)) { +// if (baseType.IsDerivedFrom (type)) { +// baseType = type; +// } else if (!type.IsDerivedFrom (baseType)) { +// // this type is not directly related to baseType +// otherTypes.Add (type); +// } +// } +// yield return baseType; +// foreach (var type in GetBaseTypes (otherTypes)) +// yield return type; +// } +// +// static IEnumerable<IMember> GetMembers (ITypeDefinition type, IMember member, bool ignoreInherited, +// Func<IMember, bool> filter) +// { +// var options = ignoreInherited ? GetMemberOptions.IgnoreInheritedMembers : GetMemberOptions.None; +// var members = type.GetMembers (m => m.Name == member.Name, options); +// +///* // Filter out shadowed members. +// // class A { public string Foo { get; set; } } class B : A { public string Foo { get; set; } } +// if (member.SymbolKind == SymbolKind.Property || !(member is IParameterizedMember)) { +// members = members.Where (m => m == member || m.DeclaringType.Kind == TypeKind.Interface); +// }*/ +// if (filter != null) +// members = members.Where (filter); +// return members; +// } +// +// static IEnumerable<ITypeDefinition> Import (ICompilation compilation, IEnumerable<ITypeDefinition> types) +// { +// return types.Select (t => compilation.Import (t)); +// } +// +// /// <summary> +// /// collect members with the same signature/name(if overloads are included) as the specified member +// /// in the inheritance tree +// /// </summary> +// public static IEnumerable<IMember> CollectMembers (Solution solution, IMember member, ReferenceFinder.RefactoryScope scope, +// bool includeOverloads = true, bool matchDeclaringType = false) +// { +// if (solution == null || member.SymbolKind == SymbolKind.Destructor || member.SymbolKind == SymbolKind.Operator) +// return new [] { member }; +// +// if (member.SymbolKind == SymbolKind.Constructor) { +// if (includeOverloads) +// return member.DeclaringType.GetMembers (m => m.SymbolKind == SymbolKind.Constructor, GetMemberOptions.IgnoreInheritedMembers); +// return new [] { member }; +// } +// +// Func<IMember, bool> memberFilter = null; +// if (member is IParameterizedMember && !includeOverloads) +// memberFilter = m => MatchParameters (m, member); +// +// var declaringType = member.DeclaringTypeDefinition; +// if (declaringType == null) +// return new [] { member }; +// // only collect members in declaringType +// if (matchDeclaringType) +// return GetMembers (declaringType, member, true, memberFilter); +// +// if (declaringType.Kind != TypeKind.Class && declaringType.Kind != TypeKind.Interface) +// return GetMembers (declaringType, member, false, memberFilter); +// +// var searchTypes = new List<ITypeDefinition> (); +// if (includeOverloads) { +// var interfaces = from t in declaringType.GetAllBaseTypeDefinitions () +// where t.Kind == TypeKind.Interface && GetMembers (t, member, true, memberFilter).Any () +// select t; +// searchTypes.AddRange (GetBaseTypes (interfaces)); +// } +// +// if (member.DeclaringType.Kind == TypeKind.Class) { +// var members = GetMembers (declaringType, member, false, memberFilter).ToList (); +// if (members.Any (m => m.IsOverridable)) +// searchTypes.AddRange (GetBaseTypes (members.Select (m => m.DeclaringTypeDefinition))); +// else if (searchTypes.Count == 0) +// return members; +// } +// +// IList<ICompilation> compilations; +// if (scope == ReferenceFinder.RefactoryScope.Solution || scope == ReferenceFinder.RefactoryScope.Unknown) { +// var projects = SearchCollector.CollectProjects (solution, searchTypes); +// compilations = projects.Select (TypeSystemService.GetCompilation).ToList (); +// } else { +// compilations = new [] { member.Compilation }; +// } +// +// var result = new List<IMember> (); +// var mainAssemblies = new HashSet<string> (compilations.Select (c => c.MainAssembly.AssemblyName)); +// var searchedAssemblies = new HashSet<string> (); +// var searchedTypes = new HashSet<string> (); +// +// foreach (var compilation in compilations) { +// var baseTypeImports = Import(compilation, searchTypes).Where (t => t != null).ToList (); +// if (!baseTypeImports.Any ()) continue; +// +// foreach (var assembly in compilation.Assemblies) { +// // search main assemblies in their projects' own compilation, to avoid possible resolving problems +// if ((mainAssemblies.Contains(assembly.AssemblyName) && assembly != compilation.MainAssembly) || +// !searchedAssemblies.Add (assembly.AssemblyName)) +// continue; +// +// foreach (var type in assembly.GetAllTypeDefinitions ()) { +// // members in base types will also be added +// // because IsDerivedFrom return true for a type itself +// if (!searchedTypes.Add (type.ReflectionName) || !baseTypeImports.Any (type.IsDerivedFrom)) +// continue; +// result.AddRange (GetMembers (type, member, true, memberFilter)); +// } +// } +// } +// if (!result.Contains (member)) +// result.Add (member); +// return result; +// } +// +// } +//} +// diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/MemberReference.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/MemberReference.cs index 44936f27f4..0e1b3915ae 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/MemberReference.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/MemberReference.cs @@ -25,16 +25,18 @@ // THE SOFTWARE. using System; using ICSharpCode.NRefactory.TypeSystem; -using Mono.TextEditor.Highlighting; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Ide.Editor.Highlighting; namespace MonoDevelop.Ide.FindInFiles { [Flags] public enum ReferenceUsageType { - Unknown = 0, - Read = 1, - Write = 2, - ReadWrite = Read | Write + Unknown = 0, + Read = 1, + Write = 2, + Declariton = 4, + ReadWrite = Read | Write } public class MemberReference : SearchResult @@ -44,23 +46,24 @@ namespace MonoDevelop.Ide.FindInFiles return new MonoDevelop.Ide.FindInFiles.FileProvider (FileName); } } - - public override string FileName { + readonly string fileName; + public override string FileName { get { - return Region.FileName; + return fileName; } } public ReferenceUsageType ReferenceUsageType { get; set; } public object EntityOrVariable { get; private set;} - public DomRegion Region { get; private set;} - public MemberReference (object entity, DomRegion region, int offset, int length) : base (offset, length) + public MemberReference (object entity, string fileName, int offset, int length) : base (offset, length) { if (entity == null) throw new System.ArgumentNullException ("entity"); + if (fileName == null) + throw new ArgumentNullException ("fileName"); EntityOrVariable = entity; - Region = region; + this.fileName = fileName; } public string GetName () @@ -79,7 +82,8 @@ namespace MonoDevelop.Ide.FindInFiles public override AmbientColor GetBackgroundMarkerColor (ColorScheme style) { - return (ReferenceUsageType & ReferenceUsageType.Write) != 0 ? + return (ReferenceUsageType & ReferenceUsageType.Write) != 0 || + (ReferenceUsageType & ReferenceUsageType.Declariton) != 0? style.ChangingUsagesRectangle : style.UsagesRectangle; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/ReferencesFinder.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/ReferencesFinder.cs index 9691a93978..c69477eb13 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/ReferencesFinder.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/ReferencesFinder.cs @@ -1,321 +1,323 @@ -// -// ReferenceFinder.cs -// -// Author: -// Mike Krüger <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 Mono.Addins; -using MonoDevelop.Core; -using MonoDevelop.Projects; -using ICSharpCode.NRefactory.TypeSystem; -using MonoDevelop.Ide.TypeSystem; -using System.Threading.Tasks; - -namespace MonoDevelop.Ide.FindInFiles -{ - public abstract class ReferenceFinder - { - public bool IncludeDocumentation { - get; - set; - } - - /* - Project project; - protected Project Project { - get { - if (project == null) - project = Content.GetProject (); - return project; - } - }*/ - - static List<ReferenceFinderCodon> referenceFinderCodons = new List<ReferenceFinderCodon> (); - - static ReferenceFinder () - { - AddinManager.AddExtensionNodeHandler ("/MonoDevelop/Ide/ReferenceFinder", delegate(object sender, ExtensionNodeEventArgs args) { - var codon = (ReferenceFinderCodon)args.ExtensionNode; - switch (args.Change) { - case ExtensionChange.Add: - referenceFinderCodons.Add (codon); - break; - case ExtensionChange.Remove: - referenceFinderCodons.Remove (codon); - break; - } - }); - } - - static ReferenceFinder GetReferenceFinder (string mimeType) - { - var codon = referenceFinderCodons.FirstOrDefault (c => c.SupportedMimeTypes.Any (mt => mt == mimeType)); - return codon != null ? codon.CreateFinder () : null; - } - - public static IEnumerable<MemberReference> FindReferences (object member, bool searchForAllOverloads, ProgressMonitor monitor = null) - { - return FindReferences (IdeApp.ProjectOperations.CurrentSelectedSolution, member, searchForAllOverloads, RefactoryScope.Unknown, monitor); - } - - public static IEnumerable<MemberReference> FindReferences (object member, bool searchForAllOverloads, RefactoryScope scope, ProgressMonitor monitor = null) - { - return FindReferences (IdeApp.ProjectOperations.CurrentSelectedSolution, member, searchForAllOverloads, scope, monitor); - } - - static SearchCollector.FileList GetFileList (string fileName) - { - var doc = IdeApp.Workbench.GetDocument (fileName); - if (doc != null) - return new SearchCollector.FileList (doc.Project, doc.ProjectContent, new [] { (FilePath)fileName }); - return null; - } - - static IEnumerable<SearchCollector.FileList> GetFileNames (Solution solution, object node, RefactoryScope scope, - ProgressMonitor monitor, IEnumerable<object> searchNodes) - { - if (!(node is IField) && !(node is IParameter) && node is IVariable || scope == RefactoryScope.File) { - string fileName; - if (node is IEntity) { - fileName = ((IEntity)node).Region.FileName; - } else if (node is ITypeParameter) { - fileName = ((ITypeParameter)node).Region.FileName; - } else { - fileName = ((IVariable)node).Region.FileName; - } - var fileList = GetFileList (fileName); - if (fileList != null) - yield return fileList; - yield break; - } - - if (node is ITypeParameter) { - var typeParameter = node as ITypeParameter; - if (typeParameter.Owner != null) { - yield return SearchCollector.CollectDeclaringFiles (typeParameter.Owner); - yield break; - } - var fileList = GetFileList (typeParameter.Region.FileName); - if (fileList != null) - yield return fileList; - yield break; - } - var par = node as IParameter; - if (par != null) { - node = par.Owner; - } - - var compilationProvider = (ICompilationProvider)node; - switch (scope) {
- case RefactoryScope.DeclaringType: - var entity = (IEntity)compilationProvider; - if (entity.DeclaringTypeDefinition != null) - yield return SearchCollector.CollectDeclaringFiles (entity.DeclaringTypeDefinition); - else - yield return SearchCollector.CollectDeclaringFiles (entity); - break; - case RefactoryScope.Project:
- var sourceProject = TypeSystemService.GetProject (compilationProvider.Compilation.MainAssembly.UnresolvedAssembly.Location); - foreach (var file in SearchCollector.CollectFiles (sourceProject, searchNodes)) - yield return file; - break; - default: - var files = SearchCollector.CollectFiles (solution, searchNodes).ToList (); - if (monitor != null) - monitor.BeginTask (GettextCatalog.GetString ("Searching for references in solution..."), files.Count); - foreach (var file in files) { - if (monitor != null && monitor.CancellationToken.IsCancellationRequested) - yield break; - yield return file; - if (monitor != null) - monitor.Step (1); - } - if (monitor != null) - monitor.EndTask (); - break; - } - } - - public static List<Project> GetAllReferencingProjects (Solution solution, Project sourceProject) - { - var projects = new List<Project> (); - projects.Add (sourceProject); - foreach (var project in solution.GetAllProjects ()) { - if (project.GetReferencedItems (ConfigurationSelector.Default).Any (prj => prj == sourceProject)) - projects.Add (project); - } - return projects; - } - - public static IEnumerable<MemberReference> FindReferences (Solution solution, object member, bool searchForAllOverloads, RefactoryScope scope = RefactoryScope.Unknown, ProgressMonitor monitor = null) - { - if (member == null) - yield break;
- if (solution == null && member is IEntity) {
- var project = TypeSystemService.GetProject ((member as IEntity).Compilation.MainAssembly.UnresolvedAssembly.Location);
- if (project == null)
- yield break;
- solution = project.ParentSolution;
- } - - IList<object> searchNodes = new [] { member }; - if (member is ITypeParameter) { - // nothing - } else if (member is IType) { - searchNodes = CollectMembers ((IType)member).ToList<object> (); - } else if (member is IEntity) { - var e = (IEntity)member; - if (e.SymbolKind == SymbolKind.Destructor) { - foreach (var r in FindReferences (solution, e.DeclaringType, searchForAllOverloads, scope, monitor)) { - yield return r; - } - yield break; - }
- if (member is IMember)
- searchNodes = CollectMembers (solution, (IMember)member, scope, searchForAllOverloads).ToList<object> (); - } - // prepare references finder - var preparedFinders = new List<Tuple<ReferenceFinder, Project, IProjectContent, List<FilePath>>> (); - var curList = new List<FilePath> (); - int totalFiles = 0; - foreach (var info in GetFileNames (solution, member, scope, monitor, searchNodes)) { - string oldMime = null; - foreach (var file in info.Files) { - if (monitor != null && monitor.CancellationToken.IsCancellationRequested) - yield break; - - string mime = DesktopService.GetMimeTypeForUri (file); - if (mime != oldMime) { - var finder = GetReferenceFinder (mime); - if (finder == null) - continue; - - oldMime = mime; - - curList = new List<FilePath> (); - preparedFinders.Add (Tuple.Create (finder, info.Project, info.Content, curList)); - } - curList.Add (file); - totalFiles++; - } - } - - // execute search - if (monitor != null) - monitor.BeginTask (GettextCatalog.GetString ("Analyzing files..."), totalFiles); - var foundOccurrences = new HashSet<Tuple<string, DomRegion>> (); - foreach (var tuple in preparedFinders) { - var finder = tuple.Item1; - foreach (var foundReference in finder.FindReferences (tuple.Item2, tuple.Item3, tuple.Item4, monitor, searchNodes)) { - if (monitor != null && monitor.CancellationToken.IsCancellationRequested) - yield break; - var tag = Tuple.Create (foundReference.FileName, foundReference.Region); - if (foundOccurrences.Contains (tag)) - continue; - foundOccurrences.Add (tag); - yield return foundReference; - } - } - if (monitor != null) - monitor.EndTask (); - } - - public abstract IEnumerable<MemberReference> FindReferences (Project project, IProjectContent content, IEnumerable<FilePath> files, ProgressMonitor monitor, IEnumerable<object> searchedMembers);
-
- internal static IEnumerable<IMember> CollectMembers (Solution solution, IMember member, RefactoryScope scope, bool includeOverloads = true)
- {
- return MemberCollector.CollectMembers (solution, member, scope, includeOverloads); - } - - internal static IEnumerable<IEntity> CollectMembers (IType type) - { - var typeDefinition = type.GetDefinition (); - if (typeDefinition == null) - yield break; - yield return (IEntity)typeDefinition; - foreach (var c in typeDefinition.GetMembers (m => m.SymbolKind == SymbolKind.Constructor, GetMemberOptions.IgnoreInheritedMembers)) { - if (!c.IsSynthetic) - yield return c; - } - - foreach (var m in type.GetMethods (m => m.IsDestructor, GetMemberOptions.IgnoreInheritedMembers)) { - yield return m; - } - } - - - public enum RefactoryScope{ Unknown, File, DeclaringType, Solution, Project} -// static RefactoryScope GetScope (object o) +//// +//// ReferenceFinder.cs +//// +//// Author: +//// Mike Krüger <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 Mono.Addins; +//using MonoDevelop.Core; +//using MonoDevelop.Projects; +//using ICSharpCode.NRefactory.TypeSystem; +//using MonoDevelop.Ide.TypeSystem; +//using System.Threading.Tasks; +// +//namespace MonoDevelop.Ide.FindInFiles +//{ +// public abstract class ReferenceFinder +// { +// public bool IncludeDocumentation { +// get; +// set; +// } +// +// /* +// Project project; +// protected Project Project { +// get { +// if (project == null) +// project = Content.GetProject (); +// return project; +// } +// }*/ +// +// static List<ReferenceFinderCodon> referenceFinderCodons = new List<ReferenceFinderCodon> (); +// +// static ReferenceFinder () +// { +// AddinManager.AddExtensionNodeHandler ("/MonoDevelop/Ide/ReferenceFinder", delegate(object sender, ExtensionNodeEventArgs args) { +// var codon = (ReferenceFinderCodon)args.ExtensionNode; +// switch (args.Change) { +// case ExtensionChange.Add: +// referenceFinderCodons.Add (codon); +// break; +// case ExtensionChange.Remove: +// referenceFinderCodons.Remove (codon); +// break; +// } +// }); +// } +// +// static ReferenceFinder GetReferenceFinder (string mimeType) +// { +// var codon = referenceFinderCodons.FirstOrDefault (c => c.SupportedMimeTypes.Any (mt => mt == mimeType)); +// return codon != null ? codon.CreateFinder () : null; +// } +// +// public static IEnumerable<MemberReference> FindReferences (object member, bool searchForAllOverloads, IProgressMonitor monitor = null) +// { +// return FindReferences (IdeApp.ProjectOperations.CurrentSelectedSolution, member, searchForAllOverloads, RefactoryScope.Unknown, monitor); +// } +// +// public static IEnumerable<MemberReference> FindReferences (object member, bool searchForAllOverloads, RefactoryScope scope, IProgressMonitor monitor = null) // { -// IEntity node = o as IEntity; -// if (node == null) -// return RefactoryScope.File; +// return FindReferences (IdeApp.ProjectOperations.CurrentSelectedSolution, member, searchForAllOverloads, scope, monitor); +// } +// +// static SearchCollector.FileList GetFileList (string fileName) +// { +// var doc = IdeApp.Workbench.GetDocument (fileName); +// if (doc != null) +// return new SearchCollector.FileList (doc.Project, null, new [] { (FilePath)fileName }); +// return null; +// } +// +// static IEnumerable<SearchCollector.FileList> GetFileNames (Solution solution, object node, RefactoryScope scope, +// IProgressMonitor monitor, IEnumerable<object> searchNodes) +// { +// if (!(node is IField) && !(node is IParameter) && node is IVariable || scope == RefactoryScope.File) { +// string fileName; +// if (node is IEntity) { +// fileName = ((IEntity)node).Region.FileName; +// } else if (node is ITypeParameter) { +// fileName = ((ITypeParameter)node).Region.FileName; +// } else { +// fileName = ((IVariable)node).Region.FileName; +// } +// var fileList = GetFileList (fileName); +// if (fileList != null) +// yield return fileList; +// yield break; +// } // -// // TODO: RefactoringsScope.Hierarchy
-// switch (node.Accessibility) {
-// case Accessibility.Public:
-// case Accessibility.Protected:
-// case Accessibility.ProtectedOrInternal:
-// if (node.DeclaringTypeDefinition != null) {
-// var scope = GetScope (node.DeclaringTypeDefinition);
-// if (scope != RefactoryScope.Solution)
-// return RefactoryScope.Project;
-// }
-// return RefactoryScope.Solution;
-// case Accessibility.Internal:
-// case Accessibility.ProtectedAndInternal:
-// return RefactoryScope.Project;
+// if (node is ITypeParameter) { +// var typeParameter = node as ITypeParameter; +// if (typeParameter.Owner != null) { +// yield return SearchCollector.CollectDeclaringFiles (typeParameter.Owner); +// yield break; +// } +// var fileList = GetFileList (typeParameter.Region.FileName); +// if (fileList != null) +// yield return fileList; +// yield break; +// } +// var par = node as IParameter; +// if (par != null) { +// node = par.Owner; +// } +// +// var compilationProvider = (ICompilationProvider)node; +// switch (scope) {
+// case RefactoryScope.DeclaringType: +// var entity = (IEntity)compilationProvider; +// if (entity.DeclaringTypeDefinition != null) +// yield return SearchCollector.CollectDeclaringFiles (entity.DeclaringTypeDefinition); +// else +// yield return SearchCollector.CollectDeclaringFiles (entity); +// break; +// case RefactoryScope.Project:
+// var sourceProject = TypeSystemService.GetProject (compilationProvider.Compilation.MainAssembly.UnresolvedAssembly.Location); +// foreach (var file in SearchCollector.CollectFiles (sourceProject, searchNodes)) +// yield return file; +// break; +// default: +// var files = SearchCollector.CollectFiles (solution, searchNodes).ToList (); +// if (monitor != null) +// monitor.BeginTask (GettextCatalog.GetString ("Searching for references in solution..."), files.Count); +// foreach (var file in files) { +// if (monitor != null && monitor.IsCancelRequested) +// yield break; +// yield return file; +// if (monitor != null) +// monitor.Step (1); +// } +// if (monitor != null) +// monitor.EndTask (); +// break; +// } +// } +// +// public static List<Project> GetAllReferencingProjects (Solution solution, Project sourceProject) +// { +// var projects = new List<Project> (); +// projects.Add (sourceProject); +// foreach (var project in solution.GetAllProjects ()) { +// if (project.GetReferencedItems (ConfigurationSelector.Default).Any (prj => prj == sourceProject)) +// projects.Add (project); +// } +// return projects; +// } +// +// public static IEnumerable<MemberReference> FindReferences (Solution solution, object member, bool searchForAllOverloads, RefactoryScope scope = RefactoryScope.Unknown, IProgressMonitor monitor = null) +// { +// yield break; +// +//// if (member == null) +//// yield break; +//// if (solution == null && member is IEntity) {
+//// var project = TypeSystemService.GetProject ((member as IEntity).Compilation.MainAssembly.UnresolvedAssembly.Location);
+//// if (project == null)
+//// yield break;
+//// solution = project.ParentSolution;
+//// } +//// +//// IList<object> searchNodes = new [] { member }; +//// if (member is ITypeParameter) { +//// // nothing +//// } else if (member is IType) { +//// searchNodes = CollectMembers ((IType)member).ToList<object> (); +//// } else if (member is IEntity) { +//// var e = (IEntity)member; +//// if (e.SymbolKind == SymbolKind.Destructor) { +//// foreach (var r in FindReferences (solution, e.DeclaringType, searchForAllOverloads, scope, monitor)) { +//// yield return r; +//// } +//// yield break; +//// }
+//// if (member is IMember)
+//// searchNodes = CollectMembers (solution, (IMember)member, scope, searchForAllOverloads).ToList<object> (); +//// } +//// // prepare references finder +//// var preparedFinders = new List<Tuple<ReferenceFinder, Project, IProjectContent, List<FilePath>>> (); +//// var curList = new List<FilePath> (); +//// int totalFiles = 0; +//// foreach (var info in GetFileNames (solution, member, scope, monitor, searchNodes)) { +//// string oldMime = null; +//// foreach (var file in info.Files) { +//// if (monitor != null && monitor.IsCancelRequested) +//// yield break; +//// +//// string mime = DesktopService.GetMimeTypeForUri (file); +//// if (mime != oldMime) { +//// var finder = GetReferenceFinder (mime); +//// if (finder == null) +//// continue; +//// +//// oldMime = mime; +//// +//// curList = new List<FilePath> (); +//// preparedFinders.Add (Tuple.Create (finder, info.Project, info.Content, curList)); +//// } +//// curList.Add (file); +//// totalFiles++; +//// } +//// } +//// +//// // execute search +//// if (monitor != null) +//// monitor.BeginTask (GettextCatalog.GetString ("Analyzing files..."), totalFiles); +//// var foundOccurrences = new HashSet<Tuple<string, DomRegion>> (); +//// foreach (var tuple in preparedFinders) { +//// var finder = tuple.Item1; +//// foreach (var foundReference in finder.FindReferences (tuple.Item2, tuple.Item3, tuple.Item4, monitor, searchNodes)) { +//// if (monitor != null && monitor.IsCancelRequested) +//// yield break; +//// var tag = Tuple.Create (foundReference.FileName, foundReference.Region); +//// if (foundOccurrences.Contains (tag)) +//// continue; +//// foundOccurrences.Add (tag); +//// yield return foundReference; +//// } +//// } +//// if (monitor != null) +//// monitor.EndTask (); +// } +// +// public abstract IEnumerable<MemberReference> FindReferences (Project project, IProjectContent content, IEnumerable<FilePath> files, IProgressMonitor monitor, IEnumerable<object> searchedMembers);
+//
+// internal static IEnumerable<IMember> CollectMembers (Solution solution, IMember member, RefactoryScope scope, bool includeOverloads = true)
+// {
+// return MemberCollector.CollectMembers (solution, member, scope, includeOverloads); +// } +// +// internal static IEnumerable<IEntity> CollectMembers (IType type) +// { +// var typeDefinition = type.GetDefinition (); +// if (typeDefinition == null) +// yield break; +// yield return (IEntity)typeDefinition; +// foreach (var c in typeDefinition.GetMembers (m => m.SymbolKind == SymbolKind.Constructor, GetMemberOptions.IgnoreInheritedMembers)) { +// if (!c.IsSynthetic) +// yield return c; // } -// return RefactoryScope.DeclaringType; +// +// foreach (var m in type.GetMethods (m => m.IsDestructor, GetMemberOptions.IgnoreInheritedMembers)) { +// yield return m; +// } +// } +// +// +// public enum RefactoryScope{ Unknown, File, DeclaringType, Solution, Project} +//// static RefactoryScope GetScope (object o) +//// { +//// IEntity node = o as IEntity; +//// if (node == null) +//// return RefactoryScope.File; +//// +//// // TODO: RefactoringsScope.Hierarchy
+//// switch (node.Accessibility) {
+//// case Accessibility.Public:
+//// case Accessibility.Protected:
+//// case Accessibility.ProtectedOrInternal:
+//// if (node.DeclaringTypeDefinition != null) {
+//// var scope = GetScope (node.DeclaringTypeDefinition);
+//// if (scope != RefactoryScope.Solution)
+//// return RefactoryScope.Project;
+//// }
+//// return RefactoryScope.Solution;
+//// case Accessibility.Internal:
+//// case Accessibility.ProtectedAndInternal:
+//// return RefactoryScope.Project;
+//// } +//// return RefactoryScope.DeclaringType; +//// } +// } +// +// [ExtensionNode (Description="A reference finder. The specified class needs to inherit from MonoDevelop.Projects.CodeGeneration.ReferenceFinder")] +// internal class ReferenceFinderCodon : TypeExtensionNode +// { +// [NodeAttribute("supportedmimetypes", "Mime types supported by this binding (to be shown in the Open File dialog)")] +// string[] supportedMimetypes; +// +// public string[] SupportedMimeTypes { +// get { +// return supportedMimetypes; +// } +// set { +// supportedMimetypes = value; +// } +// } +// +// public ReferenceFinder CreateFinder () +// { +// return (ReferenceFinder)CreateInstance (); +// } +// +// public override string ToString () +// { +// return string.Format ("[ReferenceFinderCodon: SupportedMimeTypes={0}]", SupportedMimeTypes); // } - } - - [ExtensionNode (Description="A reference finder. The specified class needs to inherit from MonoDevelop.Projects.CodeGeneration.ReferenceFinder")] - internal class ReferenceFinderCodon : TypeExtensionNode - { - [NodeAttribute("supportedmimetypes", "Mime types supported by this binding (to be shown in the Open File dialog)")] - string[] supportedMimetypes; - - public string[] SupportedMimeTypes { - get { - return supportedMimetypes; - } - set { - supportedMimetypes = value; - } - } - - public ReferenceFinder CreateFinder () - { - return (ReferenceFinder)CreateInstance (); - } - - public override string ToString () - { - return string.Format ("[ReferenceFinderCodon: SupportedMimeTypes={0}]", SupportedMimeTypes); - } - } -} +// } +//} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/Scope.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/Scope.cs index 68f55a9f4f..2ddc9dd417 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/Scope.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/Scope.cs @@ -44,6 +44,10 @@ namespace MonoDevelop.Ide.FindInFiles set; } + public virtual PathMode PathMode { + get { return PathMode.Absolute; } + } + public abstract int GetTotalWork (FilterOptions filterOptions); public abstract IEnumerable<FileProvider> GetFiles (ProgressMonitor monitor, FilterOptions filterOptions); public abstract string GetDescription (FilterOptions filterOptions, string pattern, string replacePattern); @@ -51,6 +55,10 @@ namespace MonoDevelop.Ide.FindInFiles public class DocumentScope : Scope { + public override PathMode PathMode { + get { return PathMode.Hidden; } + } + public override int GetTotalWork (FilterOptions filterOptions) { return 1; @@ -73,6 +81,10 @@ namespace MonoDevelop.Ide.FindInFiles public class SelectionScope : Scope { + public override PathMode PathMode { + get { return PathMode.Hidden; } + } + public override int GetTotalWork (FilterOptions filterOptions) { return 1; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/SearchCollector.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/SearchCollector.cs index e33d6099f4..053d55449f 100755 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/SearchCollector.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/SearchCollector.cs @@ -1,272 +1,272 @@ -// -// SearchCollector.cs -// -// Author: -// Mansheng Yang <lightyang0@gmail.com> -// -// Copyright (c) 2012 Mansheng Yang -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -using System; -using System.Collections.Generic; -using System.Linq; -using ICSharpCode.NRefactory.TypeSystem; -using MonoDevelop.Core; -using MonoDevelop.Projects; -using MonoDevelop.Ide.TypeSystem; - -namespace MonoDevelop.Ide.FindInFiles -{ - public class SearchCollector - { - - public class FileList - { - public Project Project - { - get; - private set; - } - - public IProjectContent Content - { - get; - private set; - } - - public IEnumerable<FilePath> Files - { - get; - private set; - } - - public FileList (Project project, IProjectContent content, IEnumerable<FilePath> files) - { - Project = project; - Content = content; - Files = files; - } - } - - public static IEnumerable<Project> CollectProjects (Solution solution, IEnumerable<object> entities) - { - return new SearchCollector (solution, null, entities).CollectProjects (); - } - - public static IEnumerable<FileList> CollectFiles (Project project, IEnumerable<object> entities) - { - return new SearchCollector (project.ParentSolution, project, entities).CollectFiles (); - } - - public static IEnumerable<FileList> CollectFiles (Solution solution, IEnumerable<object> entities) - { - return new SearchCollector (solution, null, entities).CollectFiles (); - } - - static IEnumerable<Project> GetAllReferencingProjects (Solution solution, string assemblyName) - { - return solution.GetAllProjects ().Where ( - project => TypeSystemService.GetCompilation (project).Assemblies.Any (a => a.AssemblyName == assemblyName)); - } - - static FileList CollectDeclaringFiles (IEntity entity, IEnumerable<string> fileNames) - { - var project = TypeSystemService.GetProject (entity); - var paths = fileNames.Distinct().Select (p => (FilePath)p); - return new SearchCollector.FileList (project, TypeSystemService.GetProjectContext (project), paths); - } - - public static FileList CollectDeclaringFiles (IEntity entity) - { - if (entity is ITypeDefinition) - return CollectDeclaringFiles (entity, (entity as ITypeDefinition).Parts.Select (p => p.Region.FileName)); - if (entity is IMethod) - return CollectDeclaringFiles (entity, (entity as IMethod).Parts.Select (p => p.Region.FileName)); - return CollectDeclaringFiles (entity, new [] { entity.Region.FileName }); - } - - Project searchProject; - bool searchProjectAdded; // if the searchProject is added, we can stop collecting - Solution solution; - IEnumerable<object> entities; - bool projectOnly; // only collect projects - - IDictionary<Project, ISet<string>> collectedFiles = new Dictionary<Project, ISet<string>> (); - ISet<Project> collectedProjects = new HashSet<Project> (); - - ISet<string> searchedAssemblies = new HashSet<string> (); - ISet<Project> searchedProjects = new HashSet<Project> (); - - /// <param name="searchProject">the project to search. use to null to search the whole solution</param> - SearchCollector (Solution solution, Project searchProject, IEnumerable<object> entities) - { - this.solution = solution; - this.searchProject = searchProject; - this.entities = entities; - } - - IEnumerable<Project> CollectProjects () - { - projectOnly = true; - foreach (var o in entities) { - var entity = o as IEntity; - if (entity != null) { - Collect (TypeSystemService.GetProject (entity), entity); - continue; - } - var par = o as IParameter; - if (par != null) { - Collect (TypeSystemService.GetProject (par.Owner), par.Owner); - continue; - } - } - return collectedProjects; - } - - IEnumerable<FileList> CollectFiles () - { - projectOnly = false; - foreach (var o in entities) { - if (o is INamespace) { - Collect (null, null); - continue; - } - - var par = o as IParameter; - if (par != null) { - if (par.Owner != null) { - Collect (TypeSystemService.GetProject (par.Owner), par.Owner); - } else { - Collect (IdeApp.Workbench.ActiveDocument.Project, null); - } - } else { - var entity = o as IEntity; - if (entity == null) - continue; - Collect (TypeSystemService.GetProject (entity), entity); - } - - if (searchProjectAdded) break; - } - foreach (var project in collectedProjects) - yield return new FileList (project, TypeSystemService.GetProjectContext (project), project.Files.Where (f => f.BuildAction == BuildAction.Compile).Select (f => f.FilePath)); - - foreach (var files in collectedFiles) - yield return new FileList (files.Key, TypeSystemService.GetProjectContext (files.Key), files.Value.Select (f => (FilePath)f)); - } - - void AddProject (Project project) - { - if (project == null) - throw new ArgumentNullException ("project"); - - searchProjectAdded = (project == searchProject); - - // remove duplicate files - if (collectedProjects.Add (project)) - collectedFiles.Remove (project); - } - - void AddFiles (Project project, IEnumerable<string> files) - { - if (project == null) - throw new ArgumentNullException ("project"); - - if (collectedProjects.Contains (project)) - return; - - ISet<string> fileSet; - if (!collectedFiles.TryGetValue (project, out fileSet)) { - fileSet = new HashSet<string> (); - collectedFiles[project] = fileSet; - } - - foreach (var file in files) - fileSet.Add (file); - } - - void Collect (Project sourceProject, IEntity entity, bool searchInProject = false) - { - if (searchedProjects.Contains(sourceProject)) - return; - - if (searchProject != null && sourceProject != searchProject) { - // searching for a entity not defined in the project - AddProject (searchProject); - return; - } - - if (sourceProject == null) { - if (entity == null) { - foreach (var project in solution.GetAllProjects ()) - AddProject (project); - return; - } - // entity is defined in a referenced assembly - var assemblyName = entity.ParentAssembly.AssemblyName; - if (!searchedAssemblies.Add (assemblyName)) - return; - foreach (var project in GetAllReferencingProjects (solution, assemblyName)) - AddProject (project); - - return; - } - - if (entity == null) { - AddProject (sourceProject); - return; - } - - var declaringType = entity.DeclaringTypeDefinition; - // TODO: possible optimization for protected - switch (entity.Accessibility) { - case Accessibility.Public: - case Accessibility.Protected: - case Accessibility.ProtectedOrInternal: - case Accessibility.Internal: - case Accessibility.ProtectedAndInternal: - - if (declaringType != null) - Collect (sourceProject, entity.DeclaringTypeDefinition, searchInProject); - else if (searchProject != null || searchInProject) - AddProject (sourceProject); - else { - foreach (var project in ReferenceFinder.GetAllReferencingProjects (solution, sourceProject)) { - if (entity.Accessibility == Accessibility.Internal || entity.Accessibility == Accessibility.ProtectedAndInternal) { - var wrapper = TypeSystemService.GetProjectContentWrapper (project); - if (wrapper == null) - continue; - if (!entity.ParentAssembly.InternalsVisibleTo (wrapper.Compilation.MainAssembly)) - continue; - } - AddProject (project); - } - } - break; - default: // private - if (projectOnly) - AddProject (sourceProject); - else if (declaringType != null) - AddFiles (sourceProject, declaringType.Parts.Select (p => p.Region.FileName)); - break; - } - } - } -} - +//// +//// SearchCollector.cs +//// +//// Author: +//// Mansheng Yang <lightyang0@gmail.com> +//// +//// Copyright (c) 2012 Mansheng Yang +//// +//// Permission is hereby granted, free of charge, to any person obtaining a copy +//// of this software and associated documentation files (the "Software"), to deal +//// in the Software without restriction, including without limitation the rights +//// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//// copies of the Software, and to permit persons to whom the Software is +//// furnished to do so, subject to the following conditions: +//// +//// The above copyright notice and this permission notice shall be included in +//// all copies or substantial portions of the Software. +//// +//// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +//// THE SOFTWARE. +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using ICSharpCode.NRefactory.TypeSystem; +//using MonoDevelop.Core; +//using MonoDevelop.Projects; +//using MonoDevelop.Ide.TypeSystem; +// +//namespace MonoDevelop.Ide.FindInFiles +//{ +// public class SearchCollector +// { +// +// public class FileList +// { +// public Project Project +// { +// get; +// private set; +// } +// +// public IProjectContent Content +// { +// get; +// private set; +// } +// +// public IEnumerable<FilePath> Files +// { +// get; +// private set; +// } +// +// public FileList (Project project, IProjectContent content, IEnumerable<FilePath> files) +// { +// Project = project; +// Content = content; +// Files = files; +// } +// } +// +// public static IEnumerable<Project> CollectProjects (Solution solution, IEnumerable<object> entities) +// { +// return new SearchCollector (solution, null, entities).CollectProjects (); +// } +// +// public static IEnumerable<FileList> CollectFiles (Project project, IEnumerable<object> entities) +// { +// return new SearchCollector (project.ParentSolution, project, entities).CollectFiles (); +// } +// +// public static IEnumerable<FileList> CollectFiles (Solution solution, IEnumerable<object> entities) +// { +// return new SearchCollector (solution, null, entities).CollectFiles (); +// } +// +// static IEnumerable<Project> GetAllReferencingProjects (Solution solution, string assemblyName) +// { +// return solution.GetAllProjects ().Where ( +// project => TypeSystemService.GetCompilation (project).Assemblies.Any (a => a.AssemblyName == assemblyName)); +// } +// +// static FileList CollectDeclaringFiles (IEntity entity, IEnumerable<string> fileNames) +// { +// var project = TypeSystemService.GetProject (entity); +// var paths = fileNames.Distinct().Select (p => (FilePath)p); +// return new SearchCollector.FileList (project, TypeSystemService.GetProjectContext (project), paths); +// } +// +// public static FileList CollectDeclaringFiles (IEntity entity) +// { +// if (entity is ITypeDefinition) +// return CollectDeclaringFiles (entity, (entity as ITypeDefinition).Parts.Select (p => p.Region.FileName)); +// if (entity is IMethod) +// return CollectDeclaringFiles (entity, (entity as IMethod).Parts.Select (p => p.Region.FileName)); +// return CollectDeclaringFiles (entity, new [] { entity.Region.FileName }); +// } +// +// Project searchProject; +// bool searchProjectAdded; // if the searchProject is added, we can stop collecting +// Solution solution; +// IEnumerable<object> entities; +// bool projectOnly; // only collect projects +// +// IDictionary<Project, ISet<string>> collectedFiles = new Dictionary<Project, ISet<string>> (); +// ISet<Project> collectedProjects = new HashSet<Project> (); +// +// ISet<string> searchedAssemblies = new HashSet<string> (); +// ISet<Project> searchedProjects = new HashSet<Project> (); +// +// /// <param name="searchProject">the project to search. use to null to search the whole solution</param> +// SearchCollector (Solution solution, Project searchProject, IEnumerable<object> entities) +// { +// this.solution = solution; +// this.searchProject = searchProject; +// this.entities = entities; +// } +// +// IEnumerable<Project> CollectProjects () +// { +// projectOnly = true; +// foreach (var o in entities) { +// var entity = o as IEntity; +// if (entity != null) { +// Collect (TypeSystemService.GetProject (entity), entity); +// continue; +// } +// var par = o as IParameter; +// if (par != null) { +// Collect (TypeSystemService.GetProject (par.Owner), par.Owner); +// continue; +// } +// } +// return collectedProjects; +// } +// +// IEnumerable<FileList> CollectFiles () +// { +// projectOnly = false; +// foreach (var o in entities) { +// if (o is INamespace) { +// Collect (null, null); +// continue; +// } +// +// var par = o as IParameter; +// if (par != null) { +// if (par.Owner != null) { +// Collect (TypeSystemService.GetProject (par.Owner), par.Owner); +// } else { +// Collect (IdeApp.Workbench.ActiveDocument.Project, null); +// } +// } else { +// var entity = o as IEntity; +// if (entity == null) +// continue; +// Collect (TypeSystemService.GetProject (entity), entity); +// } +// +// if (searchProjectAdded) break; +// } +// foreach (var project in collectedProjects) +// yield return new FileList (project, TypeSystemService.GetProjectContext (project), project.Files.Where (f => f.BuildAction == BuildAction.Compile).Select (f => f.FilePath)); +// +// foreach (var files in collectedFiles) +// yield return new FileList (files.Key, TypeSystemService.GetProjectContext (files.Key), files.Value.Select (f => (FilePath)f)); +// } +// +// void AddProject (Project project) +// { +// if (project == null) +// throw new ArgumentNullException ("project"); +// +// searchProjectAdded = (project == searchProject); +// +// // remove duplicate files +// if (collectedProjects.Add (project)) +// collectedFiles.Remove (project); +// } +// +// void AddFiles (Project project, IEnumerable<string> files) +// { +// if (project == null) +// throw new ArgumentNullException ("project"); +// +// if (collectedProjects.Contains (project)) +// return; +// +// ISet<string> fileSet; +// if (!collectedFiles.TryGetValue (project, out fileSet)) { +// fileSet = new HashSet<string> (); +// collectedFiles[project] = fileSet; +// } +// +// foreach (var file in files) +// fileSet.Add (file); +// } +// +// void Collect (Project sourceProject, IEntity entity, bool searchInProject = false) +// { +// if (searchedProjects.Contains(sourceProject)) +// return; +// +// if (searchProject != null && sourceProject != searchProject) { +// // searching for a entity not defined in the project +// AddProject (searchProject); +// return; +// } +// +// if (sourceProject == null) { +// if (entity == null) { +// foreach (var project in solution.GetAllProjects ()) +// AddProject (project); +// return; +// } +// // entity is defined in a referenced assembly +// var assemblyName = entity.ParentAssembly.AssemblyName; +// if (!searchedAssemblies.Add (assemblyName)) +// return; +// foreach (var project in GetAllReferencingProjects (solution, assemblyName)) +// AddProject (project); +// +// return; +// } +// +// if (entity == null) { +// AddProject (sourceProject); +// return; +// } +// +// var declaringType = entity.DeclaringTypeDefinition; +// // TODO: possible optimization for protected +// switch (entity.Accessibility) { +// case Accessibility.Public: +// case Accessibility.Protected: +// case Accessibility.ProtectedOrInternal: +// case Accessibility.Internal: +// case Accessibility.ProtectedAndInternal: +// +// if (declaringType != null) +// Collect (sourceProject, entity.DeclaringTypeDefinition, searchInProject); +// else if (searchProject != null || searchInProject) +// AddProject (sourceProject); +// else { +// foreach (var project in ReferenceFinder.GetAllReferencingProjects (solution, sourceProject)) { +// if (entity.Accessibility == Accessibility.Internal || entity.Accessibility == Accessibility.ProtectedAndInternal) { +// var wrapper = TypeSystemService.GetProjectContentWrapper (project); +// if (wrapper == null) +// continue; +// if (!entity.ParentAssembly.InternalsVisibleTo (wrapper.Compilation.MainAssembly)) +// continue; +// } +// AddProject (project); +// } +// } +// break; +// default: // private +// if (projectOnly) +// AddProject (sourceProject); +// else if (declaringType != null) +// AddFiles (sourceProject, declaringType.Parts.Select (p => p.Region.FileName)); +// break; +// } +// } +// } +//} +// diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/SearchProgressMonitor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/SearchProgressMonitor.cs index 69abc8f88f..aeed4d6e8d 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/SearchProgressMonitor.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/SearchProgressMonitor.cs @@ -52,7 +52,11 @@ namespace MonoDevelop.Ide.FindInFiles { outputPad.BasePath = path; } - + + public PathMode PathMode { + set { outputPad.PathMode = value; } + } + public void ReportResult (SearchResult result) { DispatchService.GuiDispatch (delegate { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/SearchResult.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/SearchResult.cs index 0c70cd1b08..651aed9054 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/SearchResult.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/SearchResult.cs @@ -26,19 +26,21 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -using Mono.TextEditor.Highlighting; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Ide.Editor.Highlighting; +using System.Collections.Generic; using MonoDevelop.Projects; using System.Collections.Generic; -using System;
-
+using System; + namespace MonoDevelop.Ide.FindInFiles { public class SearchResult { public virtual FileProvider FileProvider { get; private set; } - public int Offset { get; set; }
- public int Length { get; set; }
+ public int Offset { get; set; } + public int Length { get; set; } public virtual string FileName { get { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/SearchResultPad.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/SearchResultPad.cs index 0a9fe845e0..ad0acbb87e 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/SearchResultPad.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/SearchResultPad.cs @@ -82,6 +82,12 @@ namespace MonoDevelop.Ide.FindInFiles widget.BasePath = value; } } + + internal PathMode PathMode { + set { + widget.PathMode = value; + } + } public SearchResultPad (int instanceNum) { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/SearchResultWidget.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/SearchResultWidget.cs index d485a97a06..3a7b9b932f 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/SearchResultWidget.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/SearchResultWidget.cs @@ -30,9 +30,7 @@ using System.Linq; using Gdk; using Gtk; -using Mono.TextEditor; -using Mono.TextEditor.Highlighting; using System.Collections.Generic; using MonoDevelop.Core; using System.Text; @@ -43,6 +41,10 @@ using MonoDevelop.Ide.Navigation; using MonoDevelop.Ide.Gui.Components; using MonoDevelop.Components; using System.Threading; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Core.Text; +using MonoDevelop.Ide.Editor.Highlighting; + namespace MonoDevelop.Ide.FindInFiles { @@ -57,23 +59,30 @@ namespace MonoDevelop.Ide.FindInFiles const int SearchResultColumn = 0; const int DidReadColumn = 1; - Mono.TextEditor.Highlighting.ColorScheme highlightStyle; + ColorScheme highlightStyle; ScrolledWindow scrolledwindowLogView; PadTreeView treeviewSearchResults; Label labelStatus; TextView textviewLog; - + TreeViewColumn pathColumn; + public string BasePath { get; set; } - + public CancellationTokenSource CancellationTokenSource { get; set; } - + + internal PathMode PathMode { + set { + pathColumn.Visible = (value != PathMode.Hidden); + } + } + public bool AllowReuse { get { return !buttonStop.Sensitive && !buttonPin.Active; @@ -160,8 +169,7 @@ namespace MonoDevelop.Ide.FindInFiles textColumn.Sizing = TreeViewColumnSizing.Fixed; textColumn.FixedWidth = 300; - - TreeViewColumn pathColumn = treeviewSearchResults.AppendColumn (GettextCatalog.GetString ("Path"), + pathColumn = treeviewSearchResults.AppendColumn (GettextCatalog.GetString ("Path"), renderer, ResultPathDataFunc); pathColumn.SortColumnId = 3; pathColumn.Resizable = true; @@ -281,7 +289,10 @@ namespace MonoDevelop.Ide.FindInFiles treeviewSearchResults.ScrollToPoint (0, 0); ResultCount = 0; - documents = new Dictionary<string, TextDocument> (); + foreach (var doc in documents) { + doc.Value.Dispose (); + } + documents = new Dictionary<string, TextEditor> (); store.Clear (); labelStatus.Text = ""; textviewLog.Buffer.Clear (); @@ -295,13 +306,13 @@ namespace MonoDevelop.Ide.FindInFiles static Color AdjustColor (Color baseColor, Color color) { - double b1 = Mono.TextEditor.HslColor.Brightness (color); - double b2 = Mono.TextEditor.HslColor.Brightness (baseColor); + double b1 = HslColor.Brightness (color); + double b2 = HslColor.Brightness (baseColor); double delta = Math.Abs (b1 - b2); if (delta < 0.1) { - Mono.TextEditor.HslColor color1 = color; + HslColor color1 = color; color1.L -= 0.5; - if (Math.Abs (Mono.TextEditor.HslColor.Brightness (color1) - b2) < delta) { + if (Math.Abs (HslColor.Brightness (color1) - b2) < delta) { color1 = color; color1.L += 0.5; } @@ -335,7 +346,7 @@ namespace MonoDevelop.Ide.FindInFiles Color color = Color.Zero; if (Color.Parse(colorStr, ref color)) - colorStr = SyntaxMode.ColorToPangoMarkup(AdjustColor(baseColor, color)); + colorStr = ColorToPangoMarkup(AdjustColor(baseColor, color)); result.Append (colorStr); idx = markup.IndexOf ("foreground=\"", idx, StringComparison.Ordinal); @@ -343,8 +354,11 @@ namespace MonoDevelop.Ide.FindInFiles result.Append (markup.Substring (offset, markup.Length - offset)); return result.ToString (); } - - void DoPopupMenu (EventButton evt) + public static string ColorToPangoMarkup (Gdk.Color color) + { + return string.Format ("#{0:X2}{1:X2}{2:X2}", color.Red >> 8, color.Green >> 8, color.Blue >> 8); + } + void DoPopupMenu (Gdk.EventButton evt) { IdeApp.CommandService.ShowContextMenu (this.treeviewSearchResults, evt, new CommandEntrySet { new CommandEntry (ViewCommands.Open), @@ -493,6 +507,12 @@ namespace MonoDevelop.Ide.FindInFiles pathRenderer.Markup = projectNameMarkup; } + static int TranslateIndexToUTF8 (string text, int index) + { + byte[] bytes = Encoding.UTF8.GetBytes (text); + return Encoding.UTF8.GetString (bytes, 0, index).Length; + } + void ResultTextDataFunc (TreeViewColumn column, CellRenderer cell, TreeModel model, TreeIter iter) { if (TreeIter.Zero.Equals (iter)) @@ -516,31 +536,28 @@ namespace MonoDevelop.Ide.FindInFiles if (searchResult.Markup == null) { if (searchResult.LineNumber <= 0) searchResult.LineNumber = doc.OffsetToLineNumber (searchResult.Offset); - DocumentLine line = doc.GetLine (searchResult.LineNumber); + var line = doc.GetLine (searchResult.LineNumber); if (line == null) { textMarkup = "Invalid line number " + searchResult.LineNumber + " from offset: " + searchResult.Offset; goto end; } int indent = line.GetIndentation (doc).Length; - var data = new Mono.TextEditor.TextEditorData (doc); - data.ColorStyle = highlightStyle; + var data =TextEditorFactory.CreateNewEditor (doc); var lineText = doc.GetTextAt (line.Offset + indent, line.Length - indent); int col = searchResult.Offset - line.Offset - indent; // search result contained part of the indent. if (col + searchResult.Length < lineText.Length) lineText = doc.GetTextAt (line.Offset, line.Length); - var markup = doc.SyntaxMode != null ? - data.GetMarkup (line.Offset + indent, line.Length - indent, true, !isSelected, false) : - GLib.Markup.EscapeText (lineText); - searchResult.Markup = AdjustColors (markup.Replace ("\t", new string (' ', TextEditorOptions.DefaultOptions.TabSize))); + var markup = data.GetPangoMarkup (line.Offset + indent, line.Length - indent); + searchResult.Markup = AdjustColors (markup.Replace ("\t", new string (' ', data.Options.TabSize))); if (col >= 0) { uint start; uint end; try { - start = (uint)TextViewMargin.TranslateIndexToUTF8 (lineText, col); - end = (uint)TextViewMargin.TranslateIndexToUTF8 (lineText, Math.Min (lineText.Length, col + searchResult.Length)); + start = (uint)TranslateIndexToUTF8 (lineText, col); + end = (uint)TranslateIndexToUTF8 (lineText, Math.Min (lineText.Length, col + searchResult.Length)); } catch (Exception e) { LoggingService.LogError ("Exception while translating index to utf8 (column was:" + col + " search result length:" + searchResult.Length + " line text:" + lineText + ")", e); return; @@ -550,17 +567,16 @@ namespace MonoDevelop.Ide.FindInFiles } } - try { textMarkup = searchResult.Markup; if (!isSelected) { var searchColor = searchResult.GetBackgroundMarkerColor (highlightStyle).Color; - double b1 = Mono.TextEditor.HslColor.Brightness (searchColor); - double b2 = Mono.TextEditor.HslColor.Brightness (AdjustColor (Style.Base (StateType.Normal), (Mono.TextEditor.HslColor)highlightStyle.PlainText.Foreground)); + double b1 = HslColor.Brightness (searchColor); + double b2 = HslColor.Brightness (AdjustColor (Style.Base (StateType.Normal), (HslColor)highlightStyle.PlainText.Foreground)); double delta = Math.Abs (b1 - b2); if (delta < 0.1) { - Mono.TextEditor.HslColor color1 = highlightStyle.SearchResult.Color; + var color1 = highlightStyle.SearchResult.Color; if (color1.L + 0.5 > 1.0) { color1.L -= 0.5; } else { @@ -626,18 +642,18 @@ namespace MonoDevelop.Ide.FindInFiles - Dictionary<string, TextDocument> documents = new Dictionary<string, TextDocument> (); + Dictionary<string, TextEditor> documents = new Dictionary<string, TextEditor> (); - TextDocument GetDocument (SearchResult result) + TextEditor GetDocument (SearchResult result) { - TextDocument doc; + TextEditor doc; if (!documents.TryGetValue (result.FileName, out doc)) { var content = result.FileProvider.ReadString (); if (content == null) return null; - doc = TextDocument.CreateImmutableDocument (content); - doc.MimeType = DesktopService.GetMimeTypeForUri (result.FileName); - + + doc = TextEditorFactory.CreateNewEditor (TextEditorFactory.CreateNewReadonlyDocument (new StringTextSource (content), result.FileName, DesktopService.GetMimeTypeForUri (result.FileName))); + documents [result.FileName] = doc; } return doc; @@ -672,7 +688,7 @@ namespace MonoDevelop.Ide.FindInFiles { var result = store.GetValue (iter, SearchResultColumn) as SearchResult; if (result != null) { - DocumentLocation loc = GetLocation (result); + var loc = GetLocation (result); store.SetValue (iter, DidReadColumn, true); IdeApp.Workbench.OpenDocument (result.FileName, loc.Line, loc.Column); } @@ -684,7 +700,7 @@ namespace MonoDevelop.Ide.FindInFiles if (doc == null) return DocumentLocation.Empty; int lineNr = doc.OffsetToLineNumber (searchResult.Offset); - DocumentLine line = doc.GetLine (lineNr); + var line = doc.GetLine (lineNr); if (line == null) return DocumentLocation.Empty; return new DocumentLocation (lineNr, searchResult.Offset - line.Offset + 1); @@ -717,11 +733,11 @@ namespace MonoDevelop.Ide.FindInFiles var result = store.GetValue (iter, SearchResultColumn) as SearchResult; if (result == null) continue; - DocumentLocation loc = GetLocation (result); + var loc = GetLocation (result); var doc = GetDocument (result); if (doc == null) continue; - DocumentLine line = doc.GetLine (loc.Line); + var line = doc.GetLine (loc.Line); sb.AppendFormat ("{0} ({1}, {2}):{3}", result.FileName, loc.Line, loc.Column, doc.GetTextAt (line.Offset, line.Length)); sb.AppendLine (); @@ -789,7 +805,7 @@ namespace MonoDevelop.Ide.FindInFiles var doc = GetDocument (searchResult); if (doc == null) return null; - DocumentLocation location = doc.OffsetToLocation (searchResult.Offset); + var location = doc.OffsetToLocation (searchResult.Offset); return new SearchTextFileNavigationPoint (searchResult.FileName, location.Line, location.Column); } @@ -805,11 +821,11 @@ namespace MonoDevelop.Ide.FindInFiles if (doc == null) return null; - var buf = doc.GetContent<IEditableTextBuffer> (); + var buf = doc.Editor; if (buf != null) { doc.DisableAutoScroll (); buf.RunWhenLoaded (() => { - buf.SetCaretTo (Math.Max (Line, 1), Math.Max (Column, 1)); + buf.SetCaretLocation (Math.Max (Line, 1), Math.Max (Column, 1)); }); } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Components/ExtensibleTreeView.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Components/ExtensibleTreeView.cs index 6d767b6f30..8ac94124be 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Components/ExtensibleTreeView.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Components/ExtensibleTreeView.cs @@ -30,15 +30,8 @@ //#define TREE_VERIFY_INTEGRITY using System; -using System.IO; -using System.ComponentModel; -using System.Drawing; -using System.Diagnostics; using System.Collections; using System.Collections.Generic; -using System.Collections.Specialized; -using System.Xml; -using System.Resources; using System.Text; using Mono.Addins; @@ -48,7 +41,6 @@ using MonoDevelop.Ide.Commands; using MonoDevelop.Components.Commands; using MonoDevelop.Ide.Gui.Pads; using MonoDevelop.Projects.Extensions; -using Mono.TextEditor; using System.Linq; using MonoDevelop.Ide.Tasks; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Components/PadTreeView.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Components/PadTreeView.cs index fed5414ad4..42da610592 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Components/PadTreeView.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Components/PadTreeView.cs @@ -26,7 +26,6 @@ using System; using Gtk; -using Mono.TextEditor; namespace MonoDevelop.Ide.Gui.Components { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/DocumentStateTracker.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/DocumentStateTracker.cs index 45303e9457..b254fac993 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/DocumentStateTracker.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/DocumentStateTracker.cs @@ -30,7 +30,8 @@ using System; using System.Collections.Generic; -using Mono.TextEditor; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Core.Text; namespace MonoDevelop.Ide.Gui.Content { @@ -40,25 +41,25 @@ namespace MonoDevelop.Ide.Gui.Content T currentEngine; Stack<T> cachedEngines = new Stack<T> (); - TextEditorData editor; + TextEditor editor; - public DocumentStateTracker (T engine, TextEditorData editor) + public DocumentStateTracker (T engine, TextEditor editor) { this.currentEngine = engine; this.editor = editor; - editor.Document.TextReplaced += textChanged; + editor.TextChanged += textChanged; } public void Dispose () { - editor.Document.TextReplaced -= textChanged; + editor.TextChanged -= textChanged; } public T Engine { get { return currentEngine; } } - void textChanged (object sender, DocumentChangeEventArgs args) + void textChanged (object sender, TextChangeEventArgs args) { if (args.Offset< currentEngine.Position) ResetEngineToPosition (args.Offset); @@ -85,7 +86,7 @@ namespace MonoDevelop.Ide.Gui.Content } public void UpdateEngine () { - UpdateEngine (editor.Caret.Offset); + UpdateEngine (editor.CaretOffset); } //Makes sure that the smart indent engine's cursor has caught up with the @@ -112,7 +113,7 @@ namespace MonoDevelop.Ide.Gui.Content // get the engine caught up int nextSave = (cachedEngines.Count == 0)? BUFFER_SIZE : cachedEngines.Peek ().Position + BUFFER_SIZE; if (currentEngine.Position + 1 == position) { - char ch = editor.Document.GetCharAt (currentEngine.Position); + char ch = editor.GetCharAt (currentEngine.Position); currentEngine.Push (ch); ConsoleWrite ("pushing character '{0}'", ch); if (currentEngine.Position == nextSave) @@ -124,7 +125,7 @@ namespace MonoDevelop.Ide.Gui.Content int endCut = currentEngine.Position + BUFFER_SIZE; if (endCut > position) endCut = position; - string buffer = editor.Document.GetTextBetween (currentEngine.Position, endCut); + string buffer = editor.GetTextBetween (currentEngine.Position, endCut); ConsoleWrite ("getting buffer between {0} and {1}" /* '{2}'"*/, currentEngine.Position, endCut - 1, buffer); foreach (char ch in buffer) { currentEngine.Push (ch); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/IEditableTextBuffer.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/IEditableTextBuffer.cs deleted file mode 100644 index c606819800..0000000000 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/IEditableTextBuffer.cs +++ /dev/null @@ -1,70 +0,0 @@ -// -// IEditableTextBuffer.cs -// -// Author: -// Mike Krüger <mkrueger@novell.com> -// -// Copyright (C) 2008 Novell, Inc (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using MonoDevelop.Projects.Text; - -namespace MonoDevelop.Ide.Gui.Content -{ - public interface IEditableTextBuffer : ITextBuffer, IEditableTextFile, IUndoHandler - { - int LineCount { get; } - void SetCaretTo (int line, int column); - void SetCaretTo (int line, int column, bool highlightLine); - void SetCaretTo (int line, int column, bool highlightLine, bool centerCaret); - bool HasInputFocus { get; } - - void RunWhenLoaded (System.Action action); - - event EventHandler CaretPositionSet; - event EventHandler<TextChangedEventArgs> TextChanged; - } - - public class TextChangedEventArgs : System.EventArgs - { - int startIndex; - public int StartIndex { - get { - return startIndex; - } - } - - int endIndex; - public int EndIndex { - get { - return endIndex; - } - } - - public TextChangedEventArgs (int startIndex, int endIndex) - { - this.startIndex = startIndex; - this.endIndex = endIndex; - } - } -} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/IEncodedTextContent.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/IEncodedTextContent.cs deleted file mode 100644 index 53f2e681e5..0000000000 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/IEncodedTextContent.cs +++ /dev/null @@ -1,41 +0,0 @@ -// -// IEncodedTextContent.cs -// -// Author: -// Lluis Sanchez Gual -// -// Copyright (C) 2006 Novell, Inc (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Text; - -namespace MonoDevelop.Ide.Gui.Content -{ - public interface IEncodedTextContent - { - void Load (string fileName, Encoding encoding); - void Save (string fileName, Encoding encoding); - - Encoding SourceEncoding { get; } - } -} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/IFoldable.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/IFoldable.cs index 2e163bc941..2775cfd06e 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/IFoldable.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/IFoldable.cs @@ -30,7 +30,7 @@ using System; namespace MonoDevelop.Ide.Gui.Content { - public interface IFoldable + interface IFoldable { void ToggleAllFoldings (); void FoldDefinitions (); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/IOpenNamedElementHandler.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/IOpenNamedElementHandler.cs index 306e4b4fab..e7b6c33768 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/IOpenNamedElementHandler.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/IOpenNamedElementHandler.cs @@ -30,8 +30,9 @@ using System; namespace MonoDevelop.Ide.Gui.Content { - public interface IOpenNamedElementHandler + interface IOpenNamedElementHandler { - void Open (ICSharpCode.NRefactory.TypeSystem.INamedElement element); + void Open (Microsoft.CodeAnalysis.ISymbol element); + void Open (string documentationCommentId); } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/ISmartIndenter.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/ISmartIndenter.cs deleted file mode 100644 index dfeb37a083..0000000000 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/ISmartIndenter.cs +++ /dev/null @@ -1,63 +0,0 @@ -// -// ISmartIndenter.cs -// -// Author: -// Michael Hutchinson <mhutchinson@novell.com> -// -// Copyright (C) 2008 Novell, Inc (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; - -namespace MonoDevelop.Ide.Gui.Content -{ - - public interface ISmartIndenter - { - Indent GetIndent (int line); - Indent GetAutoTriggeredReindent (int insertionOffset, int length); - } - - public struct Indent - { - int depth, alignment; - bool isRelative; - - public Indent (int depth, int alignment) - : this (depth, alignment, false) - { - } - - public Indent (int depth, int alignment, bool isRelative) - { - this.depth = depth; - this.alignment = alignment; - this.isRelative = isRelative; - } - - public int Depth { get { return depth; } } - public int Alignment { get { return alignment; } } - public bool IsRelativeToPreviousLine { get { return isRelative; } } - - public bool IsZero { get { return Depth == 0 && Alignment == 0; } } - } -} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/ITextBuffer.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/ITextBuffer.cs deleted file mode 100644 index 02c2834d25..0000000000 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/ITextBuffer.cs +++ /dev/null @@ -1,46 +0,0 @@ -// -// ITextBuffer.cs -// -// Author: -// Lluis Sanchez Gual -// -// Copyright (C) 2005 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 MonoDevelop.Projects.Text; - -namespace MonoDevelop.Ide.Gui.Content -{ - public interface ITextBuffer: ITextFile - { - //FIXME: this should be a method, it's relatively expensive - string SelectedText { get; set; } - - int CursorPosition { get; set; } - - int SelectionStartPosition { get; } - int SelectionEndPosition { get; } - - void Select (int startPosition, int endPosition); - void ShowPosition (int position); - } -} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/ITextEditorResolver.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/ITextEditorResolver.cs index 5c1006a192..6d6ec4ab16 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/ITextEditorResolver.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/ITextEditorResolver.cs @@ -26,39 +26,26 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -using System; using System.Collections.Generic; using System.Linq; -using Mono.TextEditor; using Mono.Addins; -using ICSharpCode.NRefactory.TypeSystem; -using ICSharpCode.NRefactory.Semantics; -using MonoDevelop.Ide.TypeSystem; +using Microsoft.CodeAnalysis; +using MonoDevelop.Ide.Editor; namespace MonoDevelop.Ide.Gui.Content { public interface ITextEditorResolver { - ResolveResult GetLanguageItem (int offset); - ResolveResult GetLanguageItem (int offset, string expression); + ISymbol GetLanguageItem (int offset); + ISymbol GetLanguageItem (int offset, string expression); } public interface ITextEditorResolverProvider { - ResolveResult GetLanguageItem (MonoDevelop.Ide.Gui.Document document, int offset, out DomRegion expressionRegion); - ResolveResult GetLanguageItem (MonoDevelop.Ide.Gui.Document document, int offset, string identifier); - - string CreateTooltip (MonoDevelop.Ide.Gui.Document document, int offset, ResolveResult result, string errorInformations, Gdk.ModifierType modifierState); - - } - - public interface ITextEditorMemberPositionProvider - { - IUnresolvedTypeDefinition GetTypeAt (int offset); - IUnresolvedMember GetMemberAt (int offset); + ISymbol GetLanguageItem (Document document, int offset, out DocumentRegion expressionRegion); + ISymbol GetLanguageItem (Document document, int offset, string identifier); } - - + public static class TextEditorResolverService { static List<TextEditorResolverProviderCodon> providers = new List<TextEditorResolverProviderCodon> (); @@ -85,16 +72,16 @@ namespace MonoDevelop.Ide.Gui.Content return codon.CreateResolver (); } - public static ResolveResult GetLanguageItem (this MonoDevelop.Ide.Gui.Document document, int offset, out DomRegion expressionRegion) - {
- if (document == null)
+ public static ISymbol GetLanguageItem (this Document document, int offset, out DocumentRegion expressionRegion) + { + if (document == null) throw new System.ArgumentNullException ("document"); - var textEditorResolver = TextEditorResolverService.GetProvider (document.Editor.Document.MimeType); + var textEditorResolver = TextEditorResolverService.GetProvider (document.Editor.MimeType); if (textEditorResolver != null) { return textEditorResolver.GetLanguageItem (document, offset, out expressionRegion); } - expressionRegion = DomRegion.Empty; + expressionRegion = DocumentRegion.Empty; return null; } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/TextEditorExtension.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/TextEditorExtension.cs deleted file mode 100644 index 276c61db99..0000000000 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/TextEditorExtension.cs +++ /dev/null @@ -1,181 +0,0 @@ -// TextEditorExtension.cs -// -// Author: -// Lluis Sanchez Gual -// -// Copyright (c) 2007 Novell, Inc (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -// - - -using System; -using MonoDevelop.Projects; -using MonoDevelop.Ide.CodeCompletion; -using MonoDevelop.Components.Commands; -using MonoDevelop.Ide.Commands;
-using MonoDevelop.Core; -using Mono.TextEditor; -using ICSharpCode.NRefactory.TypeSystem; -using MonoDevelop.Ide.TypeSystem; - -namespace MonoDevelop.Ide.Gui.Content -{ - public class TextEditorExtension : ITextEditorExtension, ICommandRouter - { - internal protected Document document; - - public void Initialize (Document document) - { - if (this.document != null) - throw new InvalidOperationException ("Extension is already initialized."); - this.document = document; - Initialize (); - } - - public ITextEditorExtension Next { - get; - set; - } - - protected Document Document { - get { return document; } - } - - protected TextEditorData Editor { - get { return document != null ? document.Editor : null; } - } - - protected FilePath FileName { - get { - IViewContent view = document.Window.ViewContent; - return view.IsUntitled ? view.UntitledName : view.ContentName; - } - } - - protected IProjectContent GetParserContext () - { - CheckInitialized (); - - IViewContent view = document.Window.ViewContent; - string file = view.IsUntitled ? view.UntitledName : view.ContentName; - Project project = view.Project; - - if (project != null) - return TypeSystemService.GetProjectContext (project); - return TypeSystemService.GetContext (file, Document.Editor.Document.MimeType, Document.Editor.Text); - } - - protected Ambience GetAmbience () - { - CheckInitialized (); - - IViewContent view = document.Window.ViewContent; - string file = view.IsUntitled ? view.UntitledName : view.ContentName; - return AmbienceService.GetAmbienceForFile (file); - } - - public virtual bool ExtendsEditor (Document doc, IEditableTextBuffer editor) - { - return true; - } - - // When a key is pressed, and before the key is processed by the editor, this method will be invoked. - // Return true if the key press should be processed by the editor. - public virtual bool KeyPress (Gdk.Key key, char keyChar, Gdk.ModifierType modifier) - { - CheckInitialized (); - - if (Next == null) - return true; - else - return Next.KeyPress (key, keyChar, modifier); - } - - public virtual void CursorPositionChanged () - { - CheckInitialized (); - - if (Next != null) - Next.CursorPositionChanged (); - } - - public virtual void TextChanged (int startIndex, int endIndex) - { - if (Next != null) - Next.TextChanged (startIndex, endIndex); - } - - public virtual void Initialize () - { - CheckInitialized (); - - TextEditorExtension next = Next as TextEditorExtension; - if (next != null) - next.Initialize (); - } - - public virtual void Dispose () - { - } - - void CheckInitialized () - { - if (document == null) - throw new InvalidOperationException ("Editor extension not yet initialized"); - } - - object ITextEditorExtension.GetExtensionCommandTarget () - { - return this; - } - - object ICommandRouter.GetNextCommandTarget () - { - if (Next != null) - return Next.GetExtensionCommandTarget (); - else - return null; - } - } - - public interface ITextEditorExtension : IDisposable - { - ITextEditorExtension Next { - get; - } - - bool KeyPress (Gdk.Key key, char keyChar, Gdk.ModifierType modifier); - void CursorPositionChanged (); - void TextChanged (int startIndex, int endIndex); - - // Return the object that is going to process commands, or null - // if commands don't need custom processing - object GetExtensionCommandTarget (); - } - - class TextEditorExtensionMarker: TextEditorExtension - { - public override bool ExtendsEditor (Document doc, IEditableTextBuffer editor) - { - return false; - } - } -} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/ClassNodeBuilder.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/ClassNodeBuilder.cs index 033a61ac81..a84c2b17c3 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/ClassNodeBuilder.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/ClassNodeBuilder.cs @@ -61,9 +61,9 @@ namespace MonoDevelop.Ide.Gui.Pads.ClassPad public override void BuildNode (ITreeBuilder treeBuilder, object dataObject, NodeInfo nodeInfo) { - ClassData classData = dataObject as ClassData; - nodeInfo.Label = AmbienceService.DefaultAmbience.GetString (classData.Class.GetDefinition (), OutputFlags.ClassBrowserEntries | OutputFlags.IncludeMarkup); - nodeInfo.Icon = Context.GetIcon (classData.Class.GetStockIcon ()); + // ClassData classData = dataObject as ClassData; + // nodeInfo.Label = Ambience.DefaultAmbience.GetString (classData.Class.GetDefinition (), OutputFlags.ClassBrowserEntries | OutputFlags.IncludeMarkup); + // nodeInfo.Icon = Context.GetIcon (classData.Class.GetStockIcon ()); } /* private string GetNameWithGenericParameters (IType c) @@ -137,8 +137,8 @@ namespace MonoDevelop.Ide.Gui.Pads.ClassPad { public override void ActivateItem () { - ClassData cls = CurrentNode.DataItem as ClassData; - IdeApp.ProjectOperations.JumpToDeclaration (cls.Class, true); +// ClassData cls = CurrentNode.DataItem as ClassData; +// IdeApp.ProjectOperations.JumpToDeclaration (cls.Class, true); } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/EventNodeBuilder.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/EventNodeBuilder.cs index 425527694a..9f5a0a5910 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/EventNodeBuilder.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/EventNodeBuilder.cs @@ -49,9 +49,9 @@ namespace MonoDevelop.Ide.Gui.Pads.ClassPad public override void BuildNode (ITreeBuilder treeBuilder, object dataObject, NodeInfo nodeInfo) { - IEvent data = dataObject as IEvent; - nodeInfo.Label = Ambience.GetString (data, OutputFlags.ClassBrowserEntries | OutputFlags.IncludeMarkup); - nodeInfo.Icon = Context.GetIcon (data.GetStockIcon ()); + // IEvent data = dataObject as IEvent; + // nodeInfo.Label = Ambience.GetString (data, OutputFlags.ClassBrowserEntries | OutputFlags.IncludeMarkup); + // nodeInfo.Icon = Context.GetIcon (data.GetStockIcon ()); } public override int CompareObjects (ITreeNavigator thisNode, ITreeNavigator otherNode) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/FieldNodeBuilder.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/FieldNodeBuilder.cs index 21e4f8d5e5..8573d9ac85 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/FieldNodeBuilder.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/FieldNodeBuilder.cs @@ -49,9 +49,9 @@ namespace MonoDevelop.Ide.Gui.Pads.ClassPad public override void BuildNode (ITreeBuilder treeBuilder, object dataObject, NodeInfo nodeInfo) { - IField data = dataObject as IField; - nodeInfo.Label = Ambience.GetString (data, OutputFlags.ClassBrowserEntries | OutputFlags.IncludeMarkup); - nodeInfo.Icon = Context.GetIcon (data.GetStockIcon ()); + // IField data = dataObject as IField; + // nodeInfo.Label = Ambience.GetString (data, OutputFlags.ClassBrowserEntries | OutputFlags.IncludeMarkup); + // nodeInfo.Icon = Context.GetIcon (data.GetStockIcon ()); } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/MemberNodeBuilder.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/MemberNodeBuilder.cs index bbe1f25eff..35c0151faf 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/MemberNodeBuilder.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/MemberNodeBuilder.cs @@ -43,13 +43,7 @@ namespace MonoDevelop.Ide.Gui.Pads.ClassPad { return ((IMember)dataObject).Name; } - - protected Ambience Ambience { - get { - return AmbienceService.DefaultAmbience; - } - } - + public override Type CommandHandlerType { get { return typeof(MemberNodeCommandHandler); } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/MemberNodeCommandHandler.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/MemberNodeCommandHandler.cs index 241ec93aec..b5a8c88505 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/MemberNodeCommandHandler.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/MemberNodeCommandHandler.cs @@ -40,8 +40,8 @@ namespace MonoDevelop.Ide.Gui.Pads.ClassPad { public override void ActivateItem () { - var member = CurrentNode.DataItem as IEntity; - IdeApp.ProjectOperations.JumpToDeclaration(member); +// var member = CurrentNode.DataItem as IEntity; +// IdeApp.ProjectOperations.JumpToDeclaration(member); } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/MethodNodeBuilder.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/MethodNodeBuilder.cs index 8a25619430..2253d6973e 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/MethodNodeBuilder.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/MethodNodeBuilder.cs @@ -49,9 +49,9 @@ namespace MonoDevelop.Ide.Gui.Pads.ClassPad public override void BuildNode (ITreeBuilder treeBuilder, object dataObject, NodeInfo nodeInfo) { - IMethod data = dataObject as IMethod; - nodeInfo.Label = Ambience.GetString (data, OutputFlags.ClassBrowserEntries | OutputFlags.IncludeMarkup); - nodeInfo.Icon = Context.GetIcon (data.GetStockIcon ()); + // IMethod data = dataObject as IMethod; + // nodeInfo.Label = Ambience.GetString (data, OutputFlags.ClassBrowserEntries | OutputFlags.IncludeMarkup); + // nodeInfo.Icon = Context.GetIcon (data.GetStockIcon ()); } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/ProjectNodeBuilder.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/ProjectNodeBuilder.cs index ad71ba09d4..285cca7e17 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/ProjectNodeBuilder.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/ProjectNodeBuilder.cs @@ -97,23 +97,24 @@ namespace MonoDevelop.Ide.Gui.Pads.ClassPad builder.AddChild (((DotNetProject)project).References); } bool publicOnly = builder.Options ["PublicApiOnly"]; - var dom = TypeSystemService.GetCompilation (project); - bool nestedNamespaces = builder.Options ["NestedNamespaces"]; - HashSet<string> addedNames = new HashSet<string> (); - foreach (var ns in dom.MainAssembly.RootNamespace.ChildNamespaces) { - if (nestedNamespaces) { - if (!addedNames.Contains (ns.Name)) { - builder.AddChild (new ProjectNamespaceData (project, ns)); - addedNames.Add (ns.Name); - } - } else { - FillNamespaces (builder, project, ns); - } - } - foreach (var type in dom.MainAssembly.RootNamespace.Types) { - if (!publicOnly || type.IsPublic) - builder.AddChild (new ClassData (project, type)); - } + // TODO: Roslyn port. +// var dom = TypeSystemService.GetCompilation (project); +// bool nestedNamespaces = builder.Options ["NestedNamespaces"]; +// HashSet<string> addedNames = new HashSet<string> (); +// foreach (var ns in dom.MainAssembly.RootNamespace.ChildNamespaces) { +// if (nestedNamespaces) { +// if (!addedNames.Contains (ns.Name)) { +// builder.AddChild (new ProjectNamespaceData (project, ns)); +// addedNames.Add (ns.Name); +// } +// } else { +// FillNamespaces (builder, project, ns); +// } +// } +// foreach (var type in dom.MainAssembly.RootNamespace.Types) { +// if (!publicOnly || type.IsPublic) +// builder.AddChild (new ClassData (project, type)); +// } } public static void FillNamespaces (ITreeBuilder builder, Project project, INamespace ns) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/PropertyNodeBuilder.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/PropertyNodeBuilder.cs index 2d8c5bb73f..19ebd06aeb 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/PropertyNodeBuilder.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/PropertyNodeBuilder.cs @@ -49,9 +49,9 @@ namespace MonoDevelop.Ide.Gui.Pads.ClassPad public override void BuildNode (ITreeBuilder treeBuilder, object dataObject, NodeInfo nodeInfo) { - IProperty data = dataObject as IProperty; - nodeInfo.Label = Ambience.GetString (data, OutputFlags.ClassBrowserEntries | OutputFlags.IncludeMarkup); - nodeInfo.Icon = Context.GetIcon (data.GetStockIcon ()); + // IProperty data = dataObject as IProperty; + // nodeInfo.Label = Ambience.GetString (data, OutputFlags.ClassBrowserEntries | OutputFlags.IncludeMarkup); + // nodeInfo.Icon = Context.GetIcon (data.GetStockIcon ()); } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/AbstractBaseViewContent.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/AbstractBaseViewContent.cs index 392f2e6744..8c441c2e52 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/AbstractBaseViewContent.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/AbstractBaseViewContent.cs @@ -69,6 +69,14 @@ namespace MonoDevelop.Ide.Gui return null; } + public virtual IEnumerable<T> GetContents<T> () where T : class + { + var t = this as T; + if (t != null) + yield return t; + } + + #endregion #region IDisposable Members diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/AbstractViewContent.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/AbstractViewContent.cs index 00eb11bfa8..620ca00313 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/AbstractViewContent.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/AbstractViewContent.cs @@ -100,8 +100,13 @@ namespace MonoDevelop.Ide.Gui OnBeforeSave (EventArgs.Empty); this.Save (contentName); } - - public virtual void Save (string fileName) + + public void Save (string fileName) + { + Save (new FileSaveInformation (fileName)); + } + + public virtual void Save (FileSaveInformation fileSaveInformation) { throw new NotImplementedException (); } @@ -110,7 +115,12 @@ namespace MonoDevelop.Ide.Gui { } - public abstract void Load (string fileName); + public abstract void Load (FileOpenInformation fileOpenInformation); + + public void Load (string fileName) + { + Load (new FileOpenInformation (fileName, null)); + } public virtual void LoadNew (System.IO.Stream content, string mimeType) { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/CommonTextEditorOptions.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/CommonTextEditorOptions.cs deleted file mode 100644 index e04a6f9d14..0000000000 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/CommonTextEditorOptions.cs +++ /dev/null @@ -1,90 +0,0 @@ -// -// CommonTextEditorOptions.cs -// -// Author: -// Michael Hutchinson <mhutch@xamarin.com> -// -// Copyright (c) 2011 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.Fonts; - -namespace MonoDevelop.Ide.Gui -{ - /// <summary> - /// Text editor options that subscribe to MonoDevelop common settings. - /// </summary> - public class CommonTextEditorOptions : Mono.TextEditor.TextEditorOptions - { - bool disposed = false; - - public CommonTextEditorOptions () - { - PropertyService.PropertyChanged += PropertyServiceChanged; - base.FontName = PropertyService.Get ("FontName", FontService.MonospaceFontName); - base.ColorScheme = IdeApp.Preferences.ColorScheme; - FontService.RegisterFontChangedCallback ("Editor", UpdateFont); - } - - public override void Dispose () - { - if (disposed) - return; - disposed = true; - PropertyService.PropertyChanged -= PropertyServiceChanged; - FontService.RemoveCallback (UpdateFont); - } - - void UpdateFont () - { - base.FontName = FontName; - base.GutterFontName = GutterFontName; - this.OnChanged (EventArgs.Empty); - } - - void PropertyServiceChanged (object sender, PropertyChangedEventArgs e) - { - switch (e.Key) { - case "ColorScheme": { - string val = (string) e.NewValue; - if (string.IsNullOrEmpty (val)) - val = "Default"; - base.ColorScheme = val; - break; - } - } - } - - public override string ColorScheme { - set { throw new InvalidOperationException ("Set via global source editor options"); } - } - - public override string FontName { - get { - return FontService.FilterFontName (FontService.GetUnderlyingFontName ("Editor")); - } - set { - throw new InvalidOperationException ("Set via global source editor options"); - } - } - } -}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/DefaultWorkbench.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/DefaultWorkbench.cs index bc7bf6f75c..f52c5526e9 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/DefaultWorkbench.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/DefaultWorkbench.cs @@ -25,7 +25,6 @@ using System; using System.IO; -using System.Collections.ObjectModel; using System.Collections.Generic; using System.Drawing; using System.Diagnostics; @@ -39,12 +38,10 @@ using MonoDevelop.Ide.Codons; using MonoDevelop.Components.Commands; using MonoDevelop.Components.Docking; -using GLib; using MonoDevelop.Components.DockToolbars; using Gtk; using MonoDevelop.Components; using MonoDevelop.Ide.Extensions; -using Mono.TextEditor; using MonoDevelop.Components.MainToolbar; using MonoDevelop.Components.DockNotebook; @@ -368,7 +365,7 @@ namespace MonoDevelop.Ide.Gui return mimeimage; } - public virtual void ShowView (IViewContent content, bool bringToFront, DockNotebook notebook = null) + public virtual void ShowView (IViewContent content, bool bringToFront, IViewDisplayBinding binding = null, DockNotebook notebook = null) { bool isFile = content.IsFile; if (!isFile) { @@ -404,6 +401,10 @@ namespace MonoDevelop.Ide.Gui sdiWorkspaceWindow.TitleChanged += delegate { SetWorkbenchTitle (); }; sdiWorkspaceWindow.Closed += CloseWindowEvent; sdiWorkspaceWindow.Show (); + if (binding != null) + DisplayBindingService.AttachSubWindows (sdiWorkspaceWindow, binding); + + sdiWorkspaceWindow.CreateCommandHandler (); tab.Content = sdiWorkspaceWindow; if (mimeimage != null) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Document.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Document.cs index eff3218cc9..c96d9fb360 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Document.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Document.cs @@ -47,23 +47,25 @@ using MonoDevelop.Ide.Extensions; using System.Linq; using System.Threading; using MonoDevelop.Ide.TypeSystem; -using ICSharpCode.NRefactory; -using ICSharpCode.NRefactory.TypeSystem; -using ICSharpCode.NRefactory.TypeSystem.Implementation; using System.Text; using System.Collections.ObjectModel; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Options; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Ide.Editor.Highlighting; +using MonoDevelop.Core.Text; namespace MonoDevelop.Ide.Gui { - public class Document : ICSharpCode.NRefactory.AbstractAnnotatable + + public class Document : DocumentContext { internal object MemoryProbe = Counters.DocumentsInMemory.CreateMemoryProbe (); IWorkbenchWindow window; - TextEditorExtension editorExtension; ParsedDocument parsedDocument; - IProjectContent singleFileContext; - Mono.TextEditor.ITextEditorDataProvider provider = null; + FilePath analysisDocumentFileName; + Microsoft.CodeAnalysis.DocumentId analysisDocument; const int ParseDelay = 600; @@ -75,12 +77,20 @@ namespace MonoDevelop.Ide.Gui get; set; } - - public TextEditorExtension EditorExtension { - get { return this.editorExtension; } + + /// <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 override Microsoft.CodeAnalysis.Document AnalysisDocument { + get { + if (analysisDocument == null) + return null; + return TypeSystemService.GetCodeAnalysisDocument (analysisDocument); + } } - public T GetContent<T> () where T : class + public override T GetContent<T> () { if (window == null) return null; @@ -97,39 +107,30 @@ namespace MonoDevelop.Ide.Gui return ret; } - //no, so look through the TexteditorExtensions as well - TextEditorExtension nextExtension = editorExtension; - while (nextExtension != null) { - if (typeof(T).IsAssignableFrom (nextExtension.GetType ())) - return nextExtension as T; - nextExtension = nextExtension.Next as TextEditorExtension; + //If we didn't find in ActiveView or ViewContent... Try in SubViews + foreach (var subView in window.SubViewContents) { + foreach (var cnt in subView.GetContents<T> ()) { + return cnt; + } } + return null; } - public IEnumerable<T> GetContents<T> () where T : class + public override IEnumerable<T> GetContents<T> () { - //check whether the ViewContent can return the type directly - T ret = (T) Window.ActiveViewContent.GetContent (typeof(T)); - if (ret != null) - yield return ret; - - //check the primary viewcontent - //not sure if this is the right thing to do, but things depend on this behaviour - if (Window.ViewContent != Window.ActiveViewContent) { - ret = (T) Window.ViewContent.GetContent (typeof(T)); - if (ret != null) - yield return ret; + foreach (var cnt in window.ViewContent.GetContents<T> ()) { + yield return cnt; } - - //no, so look through the TexteditorExtensions as well - TextEditorExtension nextExtension = editorExtension; - while (nextExtension != null) { - if (typeof(T).IsAssignableFrom (nextExtension.GetType ())) - yield return nextExtension as T; - nextExtension = nextExtension.Next as TextEditorExtension; + + foreach (var subView in window.SubViewContents) { + foreach (var cnt in subView.GetContents<T> ()) { + yield return cnt; + } } } + + static Document () { if (IdeApp.Workbench != null) { @@ -155,6 +156,19 @@ namespace MonoDevelop.Ide.Gui if (window.ViewContent.Project != null) window.ViewContent.Project.Modified += HandleProjectModified; window.ViewsChanged += HandleViewsChanged; + window.ViewContent.ContentNameChanged += delegate { + analysisDocument = null; + }; + MonoDevelopWorkspace.LoadingFinished += TypeSystemService_WorkspaceItemLoaded; + } + + void TypeSystemService_WorkspaceItemLoaded (object sender, EventArgs e) + { + if (adhocProject == null) + analysisDocument = null; + EnsureAnalysisDocumentIsOpen (); + if (analysisDocument != null) + StartReparseThread (); } /* void UpdateRegisteredDom (object sender, ProjectDomEventArgs e) @@ -182,13 +196,12 @@ namespace MonoDevelop.Ide.Gui get { return !Window.ViewContent.IsViewOnly && (Window.ViewContent.ContentName == null || Window.ViewContent.IsDirty); } set { Window.ViewContent.IsDirty = value; } } - - public bool HasProject { - get { return Window != null ? Window.ViewContent.Project != null : false; } - } - - public Project Project { - get { return Window != null ? Window.ViewContent.Project : null; } + + FilePath adHocFile; + Project adhocProject; + + public override Project Project { + get { return (Window != null ? Window.ViewContent.Project : null); } /* set { Window.ViewContent.Project = value; if (value != null) @@ -199,7 +212,7 @@ namespace MonoDevelop.Ide.Gui }*/ } - public bool IsCompileableInProject { + public override bool IsCompileableInProject { get { var project = Project; if (project == null) @@ -220,19 +233,15 @@ namespace MonoDevelop.Ide.Gui } } - public IProjectContent ProjectContent { - get { - return Project != null ? TypeSystemService.GetProjectContext (Project) : GetProjectContext (); - } - } - - public virtual ICompilation Compilation { - get { - return Project != null ? TypeSystemService.GetCompilation (Project) : GetProjectContext ().CreateCompilation (); - } + public Task<Microsoft.CodeAnalysis.Compilation> GetCompilationAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + var project = TypeSystemService.GetCodeAnalysisProject (Project ?? adhocProject); + if (project == null) + return new Task<Microsoft.CodeAnalysis.Compilation> (() => null); + return project.GetCompilationAsync (cancellationToken); } - - public virtual ParsedDocument ParsedDocument { + + public override ParsedDocument ParsedDocument { get { return parsedDocument; } @@ -300,21 +309,16 @@ namespace MonoDevelop.Ide.Gui return new DocumentView (this, content); } - public string Name { + public override string Name { get { IViewContent view = Window.ViewContent; return view.IsUntitled ? view.UntitledName : view.ContentName; } } - public Mono.TextEditor.TextEditorData Editor { + public TextEditor Editor { get { - if (provider == null) { - provider = GetContent <Mono.TextEditor.ITextEditorDataProvider> (); - if (provider == null) - return null; - } - return provider.GetTextEditorData (); + return GetContent <TextEditor> (); } } @@ -378,21 +382,21 @@ namespace MonoDevelop.Ide.Gui // Set the file time of the current document after the file time of the written file, to prevent double file updates. // Note that the parsed document may be overwritten by a background thread to a more recent one. var doc = parsedDocument; - if (doc != null && doc.ParsedFile != null) { + if (doc != null) { string fileName = Window.ViewContent.ContentName; try { // filename could be null if the user cancelled SaveAs and this is a new & unsaved file if (fileName != null) - doc.ParsedFile.LastWriteTime = File.GetLastWriteTimeUtc (fileName); + doc.LastWriteTimeUtc = File.GetLastWriteTimeUtc (fileName); } catch (Exception e) { - doc.ParsedFile.LastWriteTime = DateTime.UtcNow; + doc.LastWriteTimeUtc = DateTime.UtcNow; LoggingService.LogWarning ("Exception while getting the write time from " + fileName, e); } } TypeSystemService.TrackFileChanges = true; } } - + public void SaveAs () { SaveAs (null); @@ -406,11 +410,11 @@ namespace MonoDevelop.Ide.Gui Encoding encoding = null; - IEncodedTextContent tbuffer = GetContent <IEncodedTextContent> (); + var tbuffer = GetContent <ITextSource> (); if (tbuffer != null) { - encoding = tbuffer.SourceEncoding; + encoding = tbuffer.Encoding; if (encoding == null) - encoding = Encoding.Default; + encoding = Encoding.UTF8; } if (filename == null) { @@ -446,16 +450,13 @@ namespace MonoDevelop.Ide.Gui // save backup first if ((bool)PropertyService.Get ("SharpDevelop.CreateBackupCopy", false)) { if (tbuffer != null && encoding != null) - tbuffer.Save (filename + "~", encoding); + TextFileUtility.WriteText (filename + "~", tbuffer.Text, encoding, tbuffer.UseBOM); else - Window.ViewContent.Save (filename + "~"); + Window.ViewContent.Save (new FileSaveInformation (filename + "~", encoding)); } TypeSystemService.RemoveSkippedfile (FileName); // do actual save - if (tbuffer != null && encoding != null) - tbuffer.Save (filename, encoding); - else - Window.ViewContent.Save (filename); + Window.ViewContent.Save (new FileSaveInformation (filename + "~", encoding)); FileService.NotifyFileChanged (filename); DesktopService.RecentFiles.AddFile (filename, (Project)null); @@ -468,12 +469,11 @@ namespace MonoDevelop.Ide.Gui { return ((SdiWorkspaceWindow)Window).CloseWindow (false, true); } - - void OnSaved (EventArgs args) + + protected override void OnSaved (EventArgs e) { IdeApp.Workbench.SaveFileStatus (); - if (Saved != null) - Saved (this, args); + base.OnSaved (e); } public void CancelParseTimeout () @@ -518,8 +518,14 @@ namespace MonoDevelop.Ide.Gui internal void DisposeDocument () { - DetachExtensionChain (); - RemoveAnnotations (typeof(System.Object)); + if (analysisDocument != null) { + TypeSystemService.InformDocumentClose (analysisDocument, FileName); + analysisDocument = null; + } + UnloadAdhocProject (); + if (Editor != null) { + Editor.Dispose (); + } if (window is SdiWorkspaceWindow) ((SdiWorkspaceWindow)window).DetachFromPathedDocument (); window.Closed -= OnClosed; @@ -531,11 +537,12 @@ namespace MonoDevelop.Ide.Gui if (window.ViewContent.Project != null) window.ViewContent.Project.Modified -= HandleProjectModified; window.ViewsChanged += HandleViewsChanged; + TypeSystemService.Workspace.WorkspaceChanged -= HandleWorkspaceChanged; + MonoDevelopWorkspace.LoadingFinished -= TypeSystemService_WorkspaceItemLoaded; + window = null; parsedDocument = null; - singleFileContext = null; - provider = null; views = null; viewsRO = null; } @@ -587,84 +594,44 @@ namespace MonoDevelop.Ide.Gui void InitializeExtensionChain () { - DetachExtensionChain (); - var editor = GetContent<IExtensibleTextEditor> (); - - ExtensionNodeList extensions = window.ExtensionContext.GetExtensionNodes ("/MonoDevelop/Ide/TextEditorExtensions", typeof(TextEditorExtensionNode)); - editorExtension = null; - TextEditorExtension last = null; - var mimetypeChain = DesktopService.GetMimeTypeInheritanceChainForFile (FileName).ToArray (); - foreach (TextEditorExtensionNode extNode in extensions) { - if (!extNode.Supports (FileName, mimetypeChain)) - continue; - TextEditorExtension ext; - try { - ext = (TextEditorExtension)extNode.CreateInstance (); - } catch (Exception e) { - LoggingService.LogError ("Error while creating text editor extension :" + extNode.Id + "(" + extNode.Type +")", e); - continue; - } - if (ext.ExtendsEditor (this, editor)) { - if (last != null) { - ext.Next = last.Next; - last.Next = ext; - last = ext; - } else { - editorExtension = last = ext; - last.Next = editor.AttachExtension (editorExtension); - } - ext.Initialize (this); - } - } + Editor.InitializeExtensionChain (this); + if (window is SdiWorkspaceWindow) ((SdiWorkspaceWindow)window).AttachToPathedDocument (GetContent<MonoDevelop.Ide.Gui.Content.IPathedDocument> ()); - } - void DetachExtensionChain () - { - while (editorExtension != null) { - try { - editorExtension.Dispose (); - } catch (Exception ex) { - LoggingService.LogError ("Exception while disposing extension:" + editorExtension, ex); - } - editorExtension = editorExtension.Next as TextEditorExtension; - } - editorExtension = null; } - void InitializeEditor (IExtensibleTextEditor editor) + void InitializeEditor () { - Editor.Document.TextReplaced += (o, a) => { + Editor.TextChanged += (o, a) => { if (parsedDocument != null) parsedDocument.IsInvalid = true; - if (Editor.Document.IsInAtomicUndo) { + if (Editor.IsInAtomicUndo) { wasEdited = true; } else { StartReparseThread (); } }; - Editor.Document.BeginUndo += delegate { + Editor.BeginAtomicUndoOperation += delegate { wasEdited = false; }; - Editor.Document.EndUndo += delegate { + Editor.EndAtomicUndoOperation += delegate { if (wasEdited) StartReparseThread (); }; - Editor.Document.Undone += (o, a) => StartReparseThread (); - Editor.Document.Redone += (o, a) => StartReparseThread (); +// Editor.Undone += (o, a) => StartReparseThread (); +// Editor.Redone += (o, a) => StartReparseThread (); InitializeExtensionChain (); } internal void OnDocumentAttached () { - IExtensibleTextEditor editor = GetContent<IExtensibleTextEditor> (); - if (editor != null) { - InitializeEditor (editor); + if (Editor != null) { + InitializeEditor (); RunWhenLoaded (delegate { ListenToProjectLoad (Project); }); } @@ -680,24 +647,25 @@ namespace MonoDevelop.Ide.Gui public void RunWhenLoaded (System.Action action) { var e = Editor; - if (e == null || e.Document == null) { + if (e == null) { action (); return; } - e.Document.RunWhenLoaded (action); + e.RunWhenLoaded (action); } - public void AttachToProject (Project project) + public override void AttachToProject (Project project) { SetProject (project); } - TypeSystemService.ProjectContentWrapper currentWrapper; internal void SetProject (Project project) { if (Window == null || Window.ViewContent == null || Window.ViewContent.Project == project) return; - DetachExtensionChain (); + UnloadAdhocProject (); + if (adhocProject == null) + analysisDocument = null; ISupportsProjectReload pr = GetContent<ISupportsProjectReload> (); if (pr != null) { // Unsubscribe project events @@ -709,22 +677,19 @@ namespace MonoDevelop.Ide.Gui if (project != null) project.Modified += HandleProjectModified; InitializeExtensionChain (); - + TypeSystemService.Workspace.WorkspaceChanged += HandleWorkspaceChanged; ListenToProjectLoad (project); } - void ListenToProjectLoad (Project project) + void HandleWorkspaceChanged (object sender, Microsoft.CodeAnalysis.WorkspaceChangeEventArgs e) { - if (currentWrapper != null) { - currentWrapper.Loaded -= HandleInLoadChanged; - currentWrapper = null; - } - if (project != null) { - var wrapper = TypeSystemService.GetProjectContentWrapper (project); - wrapper.Loaded += HandleInLoadChanged; - currentWrapper = wrapper; - currentWrapper.RequestLoad (); + if (e.Kind == Microsoft.CodeAnalysis.WorkspaceChangeKind.DocumentChanged && e.DocumentId == analysisDocument) { + OnDocumentParsed (EventArgs.Empty); } + } + + void ListenToProjectLoad (Project project) + { StartReparseThread (); } @@ -746,18 +711,30 @@ namespace MonoDevelop.Ide.Gui /// <returns> /// A <see cref="ParsedDocument"/> that contains the current dom. /// </returns> - public ParsedDocument UpdateParseDocument () + public override ParsedDocument UpdateParseDocument () { try { + EnsureAnalysisDocumentIsOpen (); string currentParseFile = FileName; var editor = Editor; if (editor == null || string.IsNullOrEmpty (currentParseFile)) return null; TypeSystemService.AddSkippedFile (currentParseFile); - string currentParseText = editor.Text; - this.parsedDocument = TypeSystemService.ParseFile (Project, currentParseFile, editor.Document.MimeType, currentParseText); - if (Project == null && this.parsedDocument != null) { - singleFileContext = GetProjectContext ().AddOrUpdateFiles (parsedDocument.ParsedFile); + var currentParseText = editor.CreateDocumentSnapshot (); + CancelOldParsing(); + var project = Project ?? adhocProject; + if (project != null && TypeSystemService.CanParseProjections (project, Editor.MimeType, FileName)) { + var task = TypeSystemService.ParseProjection (project, currentParseFile, editor.MimeType, currentParseText); + if (task.Result != null) { + var p = task.Result; + this.parsedDocument = p.ParsedDocument; + var projections = p.Projections; + foreach (var p2 in projections) + p2.CreateProjectedEditor (this); + Editor.SetOrUpdateProjections (this, projections, p.DisabledProjectionFeatures); + } + } else { + this.parsedDocument = TypeSystemService.ParseFile (project, currentParseFile, editor.MimeType, currentParseText).Result ?? this.parsedDocument; } } finally { @@ -765,89 +742,136 @@ namespace MonoDevelop.Ide.Gui } return this.parsedDocument; } + + uint parseTimeout = 0; - static readonly Lazy<IUnresolvedAssembly> mscorlib = new Lazy<IUnresolvedAssembly> ( () => new IkvmLoader ().LoadAssemblyFile (typeof (object).Assembly.Location)); - static readonly Lazy<IUnresolvedAssembly> systemCore = new Lazy<IUnresolvedAssembly>( () => new IkvmLoader ().LoadAssemblyFile (typeof (System.Linq.Enumerable).Assembly.Location)); - static readonly Lazy<IUnresolvedAssembly> system = new Lazy<IUnresolvedAssembly>( () => new IkvmLoader ().LoadAssemblyFile (typeof (System.Uri).Assembly.Location)); - - static IUnresolvedAssembly Mscorlib { get { return mscorlib.Value; } } - static IUnresolvedAssembly SystemCore { get { return systemCore.Value; } } - static IUnresolvedAssembly System { get { return system.Value; } } - - public bool IsProjectContextInUpdate { - get { - if (currentWrapper == null) - return false; - return !currentWrapper.IsLoaded; + void EnsureAnalysisDocumentIsOpen () + { + if (analysisDocument != null) + return; + if (Editor == null) { + analysisDocument = null; + return; + } + analysisDocumentFileName = FileName; + if (Project != null) { + RoslynWorkspace = TypeSystemService.GetWorkspace (this.Project.ParentSolution); + analysisDocument = TypeSystemService.GetDocumentId (this.Project, this.FileName); + if (analysisDocument != null) { + TypeSystemService.InformDocumentOpen (analysisDocument, Editor); + } + } else { + lock (adhocProjectLock) { + if (adhocProject != null) { + return; + } + if (Editor != null && Editor.MimeType == "text/x-csharp") { + var newProject = new DotNetAssemblyProject (Microsoft.CodeAnalysis.LanguageNames.CSharp); + this.adhocProject = newProject; + + newProject.Name = "InvisibleProject"; + newProject.References.Add (new ProjectReference (ReferenceType.Package, "mscorlib")); + newProject.References.Add (new ProjectReference (ReferenceType.Package, "System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")); + newProject.References.Add (new ProjectReference (ReferenceType.Package, "System.Core")); + + newProject.FileName = "test.csproj"; + adHocFile = Platform.IsWindows ? "C:\\a.cs" : "/a.cs"; + newProject.Files.Add (new ProjectFile (adHocFile, BuildAction.Compile)); + + var solution = new Solution (); + solution.AddConfiguration ("", true); + solution.DefaultSolutionFolder.AddItem (newProject); + using (var monitor = new MonoDevelop.Core.ProgressMonitoring.NullProgressMonitor ()) + RoslynWorkspace = TypeSystemService.Load (solution, monitor, false); + analysisDocument = TypeSystemService.GetDocumentId (RoslynWorkspace, adhocProject, adHocFile); + TypeSystemService.InformDocumentOpen (RoslynWorkspace, analysisDocument, Editor); + } + } } } + object adhocProjectLock = new object(); - public virtual IProjectContent GetProjectContext () + void UnloadAdhocProject () { - if (Project == null) { - if (singleFileContext == null) { - singleFileContext = new ICSharpCode.NRefactory.CSharp.CSharpProjectContent (); - singleFileContext = singleFileContext.AddAssemblyReferences (new [] { Mscorlib, System, SystemCore }); - } - if (parsedDocument != null) - return singleFileContext.AddOrUpdateFiles (parsedDocument.ParsedFile); - return singleFileContext; + lock (adhocProjectLock) { + if (adhocProject == null) + return; + TypeSystemService.Unload (adhocProject.ParentSolution.ParentWorkspace); + adhocProject = null; } - - return TypeSystemService.GetProjectContext (Project); } - - uint parseTimeout = 0; + object reparseLock = new object(); - internal void StartReparseThread () + CancellationTokenSource parseTokenSource = new CancellationTokenSource(); + int reparseQueue; + void CancelOldParsing() { - lock (reparseLock) { - if (currentWrapper != null) - currentWrapper.EnsureReferencesAreLoaded (); + parseTokenSource.Cancel (); + parseTokenSource = new CancellationTokenSource (); + } + internal void StartReparseThread () + { + string currentParseFile = adhocProject != null ? adHocFile : FileName; + if (string.IsNullOrEmpty (currentParseFile)) + return; + Interlocked.Increment (ref reparseQueue); + Application.Invoke (delegate { + if (Interlocked.Decrement (ref reparseQueue) != 0) { + return; + } // Don't directly parse the document because doing it at every key press is // very inefficient. Do it after a small delay instead, so several changes can // be parsed at the same time. - string currentParseFile = FileName; - if (string.IsNullOrEmpty (currentParseFile)) - return; + EnsureAnalysisDocumentIsOpen (); CancelParseTimeout (); - if (IsProjectContextInUpdate) { - return; - } - parseTimeout = GLib.Timeout.Add (ParseDelay, delegate { - var editor = Editor; - if (editor == null || IsProjectContextInUpdate) { - parseTimeout = 0; - return false; - } - string currentParseText = editor.Text; - string mimeType = editor.Document.MimeType; - ThreadPool.QueueUserWorkItem (delegate { - if (IsProjectContextInUpdate) { - return; - } - TypeSystemService.AddSkippedFile (currentParseFile); - var currentParsedDocument = TypeSystemService.ParseFile (Project, currentParseFile, mimeType, currentParseText); - Application.Invoke (delegate { - // this may be called after the document has closed, in that case the OnDocumentParsed event shouldn't be invoked. - if (isClosed) + var currentParseText = Editor.CreateSnapshot (); + string mimeType = Editor.MimeType; + CancelOldParsing (); + var token = parseTokenSource.Token; + var project = Project ?? adhocProject; + ThreadPool.QueueUserWorkItem (delegate { + TypeSystemService.AddSkippedFile (currentParseFile); + if (project != null && TypeSystemService.CanParseProjections (project, mimeType, currentParseFile)) { + TypeSystemService.ParseProjection (project, currentParseFile, mimeType, currentParseText, token).ContinueWith (task => { + if (token.IsCancellationRequested) return; - this.parsedDocument = currentParsedDocument; - OnDocumentParsed (EventArgs.Empty); - }); - }); + Application.Invoke (delegate { + // this may be called after the document has closed, in that case the OnDocumentParsed event shouldn't be invoked. + var taskResult = task.Result; + if (isClosed || taskResult == null || token.IsCancellationRequested) + return; + this.parsedDocument = taskResult.ParsedDocument; + var projections = taskResult.Projections; + foreach (var p2 in projections) + p2.CreateProjectedEditor (this); + Editor.SetOrUpdateProjections (this, projections, taskResult.DisabledProjectionFeatures); + OnDocumentParsed (EventArgs.Empty); + }); + }, TaskContinuationOptions.OnlyOnRanToCompletion); + } else { + TypeSystemService.ParseFile (project, currentParseFile, mimeType, currentParseText, token).ContinueWith (task => { + if (token.IsCancellationRequested) + return; + Application.Invoke (delegate { + // this may be called after the document has closed, in that case the OnDocumentParsed event shouldn't be invoked. + if (isClosed || task.Result == null || token.IsCancellationRequested) + return; + this.parsedDocument = task.Result; + OnDocumentParsed (EventArgs.Empty); + }); + }, TaskContinuationOptions.OnlyOnRanToCompletion); + } parseTimeout = 0; - return false; }); - } + }); } /// <summary> /// This method kicks off an async document parser and should be used instead of /// <see cref="UpdateParseDocument"/> unless you need the parsed document immediately. /// </summary> - public void ReparseDocument () + public override void ReparseDocument () { StartReparseThread (); } @@ -855,8 +879,8 @@ namespace MonoDevelop.Ide.Gui internal object ExtendedCommandTargetChain { get { // Only go through the text editor chain, if the text editor is selected as subview - if (Window != null && Window.ActiveViewContent.GetContent (typeof(IExtensibleTextEditor)) != null) - return editorExtension; + if (Window != null && Window.ActiveViewContent.GetContent (typeof(TextEditor)) != null) + return Editor.CommandRouter; return null; } } @@ -867,19 +891,10 @@ namespace MonoDevelop.Ide.Gui window.ViewContent.Project = null; } - protected virtual void OnDocumentParsed (EventArgs e) - { - EventHandler handler = this.DocumentParsed; - if (handler != null) - handler (this, e); - } - public event EventHandler Closed; - public event EventHandler Saved; public event EventHandler ViewChanged; - public event EventHandler DocumentParsed; - + public string[] CommentTags { get { if (IsFile) @@ -893,30 +908,16 @@ namespace MonoDevelop.Ide.Gui { //Document doc = IdeApp.Workbench.ActiveDocument; string loadedMimeType = DesktopService.GetMimeTypeForUri (fileName); - - Mono.TextEditor.Highlighting.SyntaxMode mode = null; - foreach (string mt in DesktopService.GetMimeTypeInheritanceChain (loadedMimeType)) { - mode = Mono.TextEditor.Highlighting.SyntaxModeService.GetSyntaxMode (null, mt); - if (mode != null) - break; - } - - if (mode == null) - return null; - - List<string> ctags; - if (mode.Properties.TryGetValue ("LineComment", out ctags) && ctags.Count > 0) { - return new string [] { ctags [0] }; - } - List<string> tags = new List<string> (); - if (mode.Properties.TryGetValue ("BlockCommentStart", out ctags)) - tags.Add (ctags [0]); - if (mode.Properties.TryGetValue ("BlockCommentEnd", out ctags)) - tags.Add (ctags [0]); - if (tags.Count == 2) - return tags.ToArray (); - else - return null; + + var result = TextEditorFactory.GetSyntaxProperties (loadedMimeType, "LineComment"); + if (result != null) + return result; + + var start = TextEditorFactory.GetSyntaxProperties (loadedMimeType, "BlockCommentStart"); + var end = TextEditorFactory.GetSyntaxProperties (loadedMimeType, "BlockCommentEnd"); + if (start != null && end != null) + return new [] { start[0], end[0] }; + return null; } // public MonoDevelop.Projects.CodeGeneration.CodeGenerator CreateCodeGenerator () @@ -932,7 +933,29 @@ namespace MonoDevelop.Ide.Gui public void DisableAutoScroll () { if (IsFile) - Mono.TextEditor.Utils.FileSettingsStore.Remove (FileName); + FileSettingsStore.Remove (FileName); + } + + public override OptionSet GetOptionSet () + { + return TypeSystemService.Workspace.Options; + } + + internal override Task<IReadOnlyList<Editor.Projection.Projection>> GetPartialProjectionsAsync (CancellationToken cancellationToken = default(CancellationToken)) + { + var parser = TypeSystemService.GetParser (Editor.MimeType); + if (parser == null) + return null; + var projectFile = Project.GetProjectFile (Editor.FileName); + if (projectFile == null) + return null; + if (!parser.CanGenerateProjection (Editor.MimeType, projectFile.BuildAction, Project.SupportedLanguages)) + return null; + try { + return parser.GetPartialProjectionsAsync (this, Editor, parsedDocument, cancellationToken); + } catch (NotSupportedException) { + return null; + } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/DocumentSwitcher.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/DocumentSwitcher.cs index 46edebf535..82fa7c413f 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/DocumentSwitcher.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/DocumentSwitcher.cs @@ -34,7 +34,6 @@ using Gdk; using Gtk; using MonoDevelop.Ide.Gui; using MonoDevelop.Core; -using Mono.TextEditor; using MonoDevelop.Components; namespace MonoDevelop.Ide @@ -262,8 +261,8 @@ namespace MonoDevelop.Ide { Gdk.Key key; Gdk.ModifierType mod; - Mono.TextEditor.KeyboardShortcut[] accels; - Mono.TextEditor.GtkWorkarounds.MapKeys (evnt, out key, out mod, out accels); + KeyboardShortcut[] accels; + GtkWorkarounds.MapKeys (evnt, out key, out mod, out accels); switch (accels [0].Key) { case Gdk.Key.Left: diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/DocumentToolbar.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/DocumentToolbar.cs index 1a2536782a..5dfd8bdaf1 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/DocumentToolbar.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/DocumentToolbar.cs @@ -26,8 +26,6 @@ using System; using Gtk; using MonoDevelop.Components; -using Mono.TextEditor; - namespace MonoDevelop.Ide.Gui { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/HiddenTextEditorViewContent.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/HiddenTextEditorViewContent.cs index b070b93358..cbd23e5ea3 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/HiddenTextEditorViewContent.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/HiddenTextEditorViewContent.cs @@ -27,13 +27,20 @@ using System; using MonoDevelop.Ide.Gui.Content; using MonoDevelop.Core; -using Mono.TextEditor; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Core.Text; namespace MonoDevelop.Ide.Gui { - public class HiddenTextEditorViewContent : MonoDevelop.Ide.Gui.AbstractViewContent, IEditableTextBuffer, Mono.TextEditor.ITextEditorDataProvider + public class HiddenTextEditorViewContent : MonoDevelop.Ide.Gui.AbstractViewContent, IServiceProvider { - Mono.TextEditor.TextEditorData data; + readonly TextEditor editor; + + public TextEditor Editor { + get { + return editor; + } + } public override Gtk.Widget Control { get { @@ -43,12 +50,17 @@ namespace MonoDevelop.Ide.Gui public HiddenTextEditorViewContent () { - document = new Mono.TextEditor.TextDocument (); - data = new Mono.TextEditor.TextEditorData (document); + editor = TextEditorFactory.CreateNewEditor (); Name = ""; } - - public override void Load (string fileName) + + public HiddenTextEditorViewContent (TextEditor editor) + { + this.editor = editor; + Name = editor.FileName; + } + + public override void Load (FileOpenInformation fileOpenInformation) { } @@ -63,54 +75,53 @@ namespace MonoDevelop.Ide.Gui public int LineCount { get { - return document.LineCount; + return editor.LineCount; } } - Mono.TextEditor.TextDocument document; public string Text { get { - return document.Text; + return editor.Text; } set { - document.Text = value; + editor.Text = value; } } public int InsertText (int position, string text) { - document.Insert (position, text); + editor.InsertText (position, text); return text.Length; } public void DeleteText (int position, int length) { - document.Replace (position, length, ""); + editor.ReplaceText (position, length, ""); } public int Length { get { - return document.TextLength; + return editor.Length; } } public string GetText (int startPosition, int endPosition) { - return document.GetTextBetween (startPosition, endPosition); + return editor.GetTextBetween (startPosition, endPosition); } public char GetCharAt (int position) { - return document.GetCharAt (position); + return editor.GetCharAt (position); } public int GetPositionFromLineColumn (int line, int column) { - return document.LocationToOffset (line, column); + return editor.LocationToOffset (line, column); } public void GetLineColumnFromPosition (int position, out int line, out int column) { - Mono.TextEditor.DocumentLocation loc = document.OffsetToLocation (position); + var loc = editor.OffsetToLocation (position); line = loc.Line; column = loc.Column; } @@ -119,32 +130,32 @@ namespace MonoDevelop.Ide.Gui public int CursorPosition { get { - return data.Caret.Offset; + return editor.CaretOffset; } set { - data.Caret.Offset = value; + editor.CaretOffset = value; } } public int SelectionStartPosition { get { - if (!data.IsSomethingSelected) - return data.Caret.Offset; - return data.SelectionRange.Offset; + if (!editor.IsSomethingSelected) + return editor.CaretOffset; + return editor.SelectionRange.Offset; } } public int SelectionEndPosition { get { - if (!data.IsSomethingSelected) - return data.Caret.Offset; - return data.SelectionRange.EndOffset; + if (!editor.IsSomethingSelected) + return editor.CaretOffset; + return editor.SelectionRange.EndOffset; } } public void Select (int startPosition, int endPosition) { - data.SelectionRange = new TextSegment (startPosition, endPosition - startPosition); + editor.SelectionRange = new TextSegment (startPosition, endPosition - startPosition); } public void ShowPosition (int position) @@ -195,12 +206,18 @@ namespace MonoDevelop.Ide.Gui { return new DisposeStub (); } - - public Mono.TextEditor.TextEditorData GetTextEditorData () + + #region IServiceProvider implementation + + object IServiceProvider.GetService (Type serviceType) { - return data; + if (serviceType.IsInstanceOfType (editor)) + return editor; + return null; } + + #endregion + public event EventHandler CaretPositionSet; - public event EventHandler<TextChangedEventArgs> TextChanged; } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/IBaseViewContent.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/IBaseViewContent.cs index c40c6f8525..ad246af631 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/IBaseViewContent.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/IBaseViewContent.cs @@ -27,6 +27,7 @@ using System; using Gtk; +using System.Collections.Generic; namespace MonoDevelop.Ide.Gui { @@ -41,7 +42,8 @@ namespace MonoDevelop.Ide.Gui string TabPageLabel { get; } object GetContent (Type type); - + IEnumerable<T> GetContents<T> () where T : class; + bool CanReuseView (string fileName); void RedrawContent (); } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/IViewContent.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/IViewContent.cs index 2fbc0c618e..0c76d3cc02 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/IViewContent.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/IViewContent.cs @@ -30,7 +30,7 @@ using MonoDevelop.Projects; namespace MonoDevelop.Ide.Gui { - public interface IViewContent : IBaseViewContent + public interface IViewContent : IBaseViewContent { Project Project { get; set; } @@ -45,9 +45,11 @@ namespace MonoDevelop.Ide.Gui bool IsDirty { get; set; } bool IsReadOnly { get; } - void Load (string fileName); + void Load (FileOpenInformation fileOpenInformation); + void Load (string fileName); void LoadNew (System.IO.Stream content, string mimeType); - void Save (string fileName); + void Save (FileSaveInformation fileSaveInformation); + void Save (string fileName); void Save (); /// <summary> diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/SdiWorkspaceWindow.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/SdiWorkspaceWindow.cs index c5468e9ee5..05aba59465 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/SdiWorkspaceWindow.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/SdiWorkspaceWindow.cs @@ -121,7 +121,10 @@ namespace MonoDevelop.Ide.Gui Add (box); SetTitleEvent(null, null); + } + internal void CreateCommandHandler () + { commandHandler = new ViewCommandHandlers (this); } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Styles.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Styles.cs index 8705e7258f..23fa36eb7b 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Styles.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Styles.cs @@ -115,7 +115,7 @@ namespace MonoDevelop.Ide.Gui public static int ProgressBarInnerPadding { get { return (int)(4 * PixelScale); } } public static int ProgressBarOuterPadding { get { return (int)(4 * PixelScale); } } - static readonly double PixelScale = Mono.TextEditor.GtkWorkarounds.GetPixelScale (); + static readonly double PixelScale = GtkWorkarounds.GetPixelScale (); // Toolbar diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/ViewCommandHandlers.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/ViewCommandHandlers.cs index 46a5e9712c..da50ac8bc1 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/ViewCommandHandlers.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/ViewCommandHandlers.cs @@ -35,6 +35,7 @@ using MonoDevelop.Core; using MonoDevelop.Ide.Gui.Content; using MonoDevelop.Components.Commands; using MonoDevelop.Ide.Commands; +using MonoDevelop.Ide.Editor; namespace MonoDevelop.Ide.Gui { @@ -240,33 +241,33 @@ namespace MonoDevelop.Ide.Gui [CommandHandler (EditCommands.UppercaseSelection)] public void OnUppercaseSelection () { - IEditableTextBuffer buffer = GetContent <IEditableTextBuffer> (); + var buffer = GetContent <TextEditor> (); if (buffer == null) return; string selectedText = buffer.SelectedText; if (string.IsNullOrEmpty (selectedText)) { - int pos = buffer.CursorPosition; - string ch = buffer.GetText (pos, pos + 1); + int pos = buffer.CaretOffset; + string ch = buffer.GetTextAt (pos, pos + 1); string upper = ch.ToUpper (); if (upper == ch) { - buffer.CursorPosition = pos + 1; + buffer.CaretOffset = pos + 1; return; } using (var undo = buffer.OpenUndoGroup ()) { - buffer.DeleteText (pos, 1); + buffer.RemoveText (pos, 1); buffer.InsertText (pos, upper); - buffer.CursorPosition = pos + 1; + buffer.CaretOffset = pos + 1; } } else { string newText = selectedText.ToUpper (); if (newText == selectedText) return; - int startPos = buffer.SelectionStartPosition; + int startPos = buffer.SelectionRange.Offset; using (var undo = buffer.OpenUndoGroup ()) { - buffer.DeleteText (startPos, selectedText.Length); + buffer.RemoveText (startPos, selectedText.Length); buffer.InsertText (startPos, newText); - buffer.Select (startPos, startPos + newText.Length); + buffer.SetSelection (startPos, startPos + newText.Length); } } } @@ -274,40 +275,40 @@ namespace MonoDevelop.Ide.Gui [CommandUpdateHandler (EditCommands.UppercaseSelection)] protected void OnUppercaseSelection (CommandInfo info) { - IEditableTextBuffer buffer = GetContent <IEditableTextBuffer> (); + var buffer = GetContent <TextEditor> (); info.Enabled = buffer != null; } [CommandHandler (EditCommands.LowercaseSelection)] public void OnLowercaseSelection () { - IEditableTextBuffer buffer = GetContent <IEditableTextBuffer> (); + var buffer = GetContent <TextEditor> (); if (buffer == null) return; string selectedText = buffer.SelectedText; if (string.IsNullOrEmpty (selectedText)) { - int pos = buffer.CursorPosition; - string ch = buffer.GetText (pos, pos + 1); + int pos = buffer.CaretOffset; + string ch = buffer.GetTextAt (pos, pos + 1); string lower = ch.ToLower (); if (lower == ch) { - buffer.CursorPosition = pos + 1; + buffer.CaretOffset = pos + 1; return; }; using (var undo = buffer.OpenUndoGroup ()) { - buffer.DeleteText (pos, 1); + buffer.RemoveText (pos, 1); buffer.InsertText (pos, lower); - buffer.CursorPosition = pos + 1; + buffer.CaretOffset = pos + 1; } } else { string newText = selectedText.ToLower (); if (newText == selectedText) return; - int startPos = buffer.SelectionStartPosition; + int startPos = buffer.SelectionRange.Offset; using (var undo = buffer.OpenUndoGroup ()) { - buffer.DeleteText (startPos, selectedText.Length); + buffer.RemoveText (startPos, selectedText.Length); buffer.InsertText (startPos, newText); - buffer.Select (startPos, startPos + newText.Length); + buffer.SetSelection (startPos, startPos + newText.Length); } } } @@ -315,7 +316,7 @@ namespace MonoDevelop.Ide.Gui [CommandUpdateHandler (EditCommands.LowercaseSelection)] protected void OnLowercaseSelection (CommandInfo info) { - IEditableTextBuffer buffer = GetContent <IEditableTextBuffer> (); + var buffer = GetContent <TextEditor> (); info.Enabled = buffer != null; } @@ -346,69 +347,68 @@ namespace MonoDevelop.Ide.Gui [CommandHandler (TextEditorCommands.LineEnd)] protected void OnLineEnd () { - Mono.TextEditor.CaretMoveActions.LineEnd (doc.Editor); + doc.Editor.EditorActionHost.MoveCaretToLineEnd (); } [CommandHandler (TextEditorCommands.LineStart)] protected void OnLineStart () { - Mono.TextEditor.CaretMoveActions.LineStart (doc.Editor); + doc.Editor.EditorActionHost.MoveCaretToLineStart (); } [CommandHandler (TextEditorCommands.DeleteLeftChar)] protected void OnDeleteLeftChar () { - Mono.TextEditor.CaretMoveActions.Left (doc.Editor); - Mono.TextEditor.DeleteActions.Delete (doc.Editor); + doc.Editor.EditorActionHost.Backspace (); } [CommandHandler (TextEditorCommands.DeleteRightChar)] protected void OnDeleteRightChar () { - Mono.TextEditor.DeleteActions.Delete (doc.Editor); + doc.Editor.EditorActionHost.Delete (); } [CommandHandler (TextEditorCommands.CharLeft)] protected void OnCharLeft () { - Mono.TextEditor.CaretMoveActions.Left (doc.Editor); + doc.Editor.EditorActionHost.MoveCaretLeft (); } [CommandHandler (TextEditorCommands.CharRight)] protected void OnCharRight () { - Mono.TextEditor.CaretMoveActions.Right (doc.Editor); + doc.Editor.EditorActionHost.MoveCaretRight (); } [CommandHandler (TextEditorCommands.LineUp)] protected void OnLineUp () { - Mono.TextEditor.CaretMoveActions.Up (doc.Editor); + doc.Editor.EditorActionHost.MoveCaretUp (); } [CommandHandler (TextEditorCommands.LineDown)] protected void OnLineDown () { - Mono.TextEditor.CaretMoveActions.Down (doc.Editor); + doc.Editor.EditorActionHost.MoveCaretDown (); } [CommandHandler (TextEditorCommands.DocumentStart)] protected void OnDocumentStart () { - Mono.TextEditor.CaretMoveActions.ToDocumentStart (doc.Editor); + doc.Editor.EditorActionHost.MoveCaretToDocumentStart (); } [CommandHandler (TextEditorCommands.DocumentEnd)] protected void OnDocumentEnd () { - Mono.TextEditor.CaretMoveActions.ToDocumentEnd (doc.Editor); + doc.Editor.EditorActionHost.MoveCaretToDocumentEnd (); } [CommandHandler (TextEditorCommands.DeleteLine)] protected void OnDeleteLine () { - var line = doc.Editor.Document.GetLine (doc.Editor.Caret.Line); - doc.Editor.Remove (line.Offset, line.LengthIncludingDelimiter); + var line = doc.Editor.GetLine (doc.Editor.CaretLocation.Line); + doc.Editor.RemoveText (line.Offset, line.LengthIncludingDelimiter); } struct RemoveInfo @@ -435,7 +435,7 @@ namespace MonoDevelop.Ide.Gui return ch == ' ' || ch == '\t' || ch == '\v'; } - public static RemoveInfo GetRemoveInfo (Mono.TextEditor.TextDocument document, ref int pos) + public static RemoveInfo GetRemoveInfo (TextEditor document, ref int pos) { int len = 0; while (pos > 0 && IsWhiteSpace (document.GetCharAt (pos))) { @@ -458,22 +458,22 @@ namespace MonoDevelop.Ide.Gui [CommandHandler (EditCommands.RemoveTrailingWhiteSpaces)] public void OnRemoveTrailingWhiteSpaces () { - Mono.TextEditor.TextEditorData data = doc.Editor; + var data = doc.Editor; if (data == null) return; System.Collections.Generic.List<RemoveInfo> removeList = new System.Collections.Generic.List<RemoveInfo> (); - int pos = data.Document.TextLength - 1; - RemoveInfo removeInfo = RemoveInfo.GetRemoveInfo (data.Document, ref pos); + int pos = data.Length - 1; + RemoveInfo removeInfo = RemoveInfo.GetRemoveInfo (data, ref pos); if (!removeInfo.IsEmpty) removeList.Add (removeInfo); while (pos >= 0) { - char ch = data.Document.GetCharAt (pos); + char ch = data.GetCharAt (pos); if (ch == '\n' || ch == '\r') { - if (RemoveInfo.IsWhiteSpace (data.Document.GetCharAt (pos - 1))) { + if (RemoveInfo.IsWhiteSpace (data.GetCharAt (pos - 1))) { --pos; - removeInfo = RemoveInfo.GetRemoveInfo (data.Document, ref pos); + removeInfo = RemoveInfo.GetRemoveInfo (data, ref pos); if (!removeInfo.IsEmpty) removeList.Add (removeInfo); } @@ -482,8 +482,7 @@ namespace MonoDevelop.Ide.Gui } using (var undo = data.OpenUndoGroup ()) { foreach (var info in removeList) { - data.Document.Remove (info.Position, info.Length); - data.Document.CommitLineUpdate (data.Document.OffsetToLineNumber (info.Position)); + data.RemoveText (info.Position, info.Length); } } } @@ -491,7 +490,7 @@ namespace MonoDevelop.Ide.Gui [CommandUpdateHandler (EditCommands.RemoveTrailingWhiteSpaces)] protected void OnRemoveTrailingWhiteSpaces (CommandInfo info) { - info.Enabled = GetContent <IEditableTextBuffer> () != null; + info.Enabled = GetContent <TextEditor> () != null; } #region Folding diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Workbench.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Workbench.cs index d53c7c1c9c..ff8260efa1 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Workbench.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Workbench.cs @@ -51,6 +51,7 @@ using MonoDevelop.Ide.Navigation; using MonoDevelop.Components.Docking; using MonoDevelop.Components.DockNotebook; using System.Text; +using MonoDevelop.Ide.Editor; namespace MonoDevelop.Ide.Gui { @@ -441,6 +442,25 @@ namespace MonoDevelop.Ide.Gui return OpenDocument (openFileInfo); } + static void ScrollToRequestedCaretLocation (Document doc, FileOpenInformation info) + { + var ipos = doc.Editor; + if ((info.Line >= 1 || info.Offset >= 0) && ipos != null) { + doc.DisableAutoScroll (); + doc.RunWhenLoaded (() => { + var loc = new DocumentLocation (info.Line, info.Column >= 1 ? info.Column : 1); + if (info.Offset >= 0) { + loc = ipos.OffsetToLocation (info.Offset); + } + if (loc.IsEmpty) + return; + ipos.SetCaretLocation (loc, info.Options.HasFlag (OpenDocumentOptions.HighlightCaretLine)); + if (info.Options.HasFlag (OpenDocumentOptions.CenterCaretLine)) + ipos.CenterToCaret (); + }); + } + } + internal Document OpenDocument (FilePath fileName, Project project, int line, int column, OpenDocumentOptions options, Encoding Encoding, IViewDisplayBinding binding, DockNotebook dockNotebook) { var openFileInfo = new FileOpenInformation (fileName, project) { @@ -455,7 +475,6 @@ namespace MonoDevelop.Ide.Gui return OpenDocument (openFileInfo); } - public Document OpenDocument (FileOpenInformation info) { if (string.IsNullOrEmpty (info.FileName)) @@ -481,18 +500,7 @@ namespace MonoDevelop.Ide.Gui doc.SetProject (info.Project); } - IEditableTextBuffer ipos = (IEditableTextBuffer) vcFound.GetContent (typeof(IEditableTextBuffer)); - if (info.Line >= 1 && ipos != null) { - doc.DisableAutoScroll (); - doc.RunWhenLoaded (() => - ipos.SetCaretTo ( - info.Line, - info.Column >= 1 ? info.Column : 1, - info.Options.HasFlag (OpenDocumentOptions.HighlightCaretLine), - info.Options.HasFlag (OpenDocumentOptions.CenterCaretLine) - ) - ); - } + ScrollToRequestedCaretLocation (doc, info); if (info.Options.HasFlag (OpenDocumentOptions.BringToFront)) { doc.Select (); @@ -518,6 +526,9 @@ namespace MonoDevelop.Ide.Gui if (info.NewContent != null) { Counters.OpenDocumentTimer.Trace ("Wrapping document"); Document doc = WrapDocument (info.NewContent.WorkbenchWindow); + + ScrollToRequestedCaretLocation (doc, info); + if (doc != null && info.Options.HasFlag (OpenDocumentOptions.BringToFront)) { doc.RunWhenLoaded (() => { if (doc.Window != null) @@ -577,7 +588,7 @@ namespace MonoDevelop.Ide.Gui if (binding == null) throw new ApplicationException("Can't create display binding for mime type: " + mimeType); - IViewContent newContent = binding.CreateContent (null, mimeType, null); + IViewContent newContent = binding.CreateContent (defaultName, mimeType, null); using (content) { newContent.LoadNew (content, mimeType); } @@ -588,9 +599,8 @@ namespace MonoDevelop.Ide.Gui newContent.UntitledName = defaultName; newContent.IsDirty = true; - workbench.ShowView (newContent, true); - DisplayBindingService.AttachSubWindows (newContent.WorkbenchWindow, binding); - + workbench.ShowView (newContent, true, binding); + var document = WrapDocument (newContent.WorkbenchWindow); document.StartReparseThread (); return document; @@ -979,8 +989,8 @@ namespace MonoDevelop.Ide.Gui var dp = new DocumentUserPrefs (); dp.FileName = FileService.AbsoluteToRelativePath (args.Item.BaseDirectory, document.FileName); if (document.Editor != null) { - dp.Line = document.Editor.Caret.Line; - dp.Column = document.Editor.Caret.Column; + dp.Line = document.Editor.CaretLine; + dp.Column = document.Editor.CaretColumn; } return dp; } @@ -1161,6 +1171,7 @@ namespace MonoDevelop.Ide.Gui ThreadPool.QueueUserWorkItem (delegate { lock (fileStatusLock) { // DateTime t = DateTime.Now; + if (fileStatus == null) return; List<FilePath> modified = new List<FilePath> (fileStatus.Count); @@ -1257,7 +1268,30 @@ namespace MonoDevelop.Ide.Gui } } } - + + public class FileSaveInformation + { + FilePath fileName; + public FilePath FileName { + get { + return fileName; + } + set { + fileName = value.CanonicalPath; + if (fileName.IsNullOrEmpty) + LoggingService.LogError ("FileName == null\n" + Environment.StackTrace); + } + } + + public Encoding Encoding { get; set; } + + public FileSaveInformation (FilePath fileName, Encoding encoding = null) + { + this.FileName = fileName; + this.Encoding = encoding; + } + } + public class FileOpenInformation { FilePath fileName; @@ -1266,19 +1300,29 @@ namespace MonoDevelop.Ide.Gui return fileName; } set { - fileName = FileService.ResolveFullPath (value.CanonicalPath); + fileName = value.CanonicalPath.ResolveLinks (); if (fileName.IsNullOrEmpty) LoggingService.LogError ("FileName == null\n" + Environment.StackTrace); } } public OpenDocumentOptions Options { get; set; } + int offset = -1; + public int Offset { + get { + return offset; + } + set { + offset = value; + } + } public int Line { get; set; } public int Column { get; set; } public IViewDisplayBinding DisplayBinding { get; set; } public IViewContent NewContent { get; set; } public Encoding Encoding { get; set; } public Project Project { get; set; } + internal DockNotebook DockNotebook { get; set; } [Obsolete("Use FileOpenInformation (FilePath filePath, Project project, int line, int column, OpenDocumentOptions options)")] @@ -1291,7 +1335,7 @@ namespace MonoDevelop.Ide.Gui } - public FileOpenInformation (FilePath filePath, Project project) + public FileOpenInformation (FilePath filePath, Project project = null) { this.FileName = filePath; this.Project = project; @@ -1393,12 +1437,8 @@ namespace MonoDevelop.Ide.Gui Counters.OpenDocumentTimer.Trace ("Loading file"); - IEncodedTextContent etc = (IEncodedTextContent) newContent.GetContent (typeof(IEncodedTextContent)); try { - if (fileInfo.Encoding != null && etc != null) - etc.Load (fileName, fileInfo.Encoding); - else - newContent.Load (fileName); + newContent.Load (fileInfo); } catch (InvalidEncodingException iex) { monitor.ReportError (GettextCatalog.GetString ("The file '{0}' could not opened. {1}", fileName, iex.Message), null); return; @@ -1420,14 +1460,14 @@ namespace MonoDevelop.Ide.Gui Counters.OpenDocumentTimer.Trace ("Showing view"); - workbench.ShowView (newContent, fileInfo.Options.HasFlag (OpenDocumentOptions.BringToFront), fileInfo.DockNotebook); + workbench.ShowView (newContent, fileInfo.Options.HasFlag (OpenDocumentOptions.BringToFront), binding, fileInfo.DockNotebook); - DisplayBindingService.AttachSubWindows (newContent.WorkbenchWindow, binding); newContent.WorkbenchWindow.DocumentType = binding.Name; - IEditableTextBuffer ipos = (IEditableTextBuffer) newContent.GetContent (typeof(IEditableTextBuffer)); + + var ipos = (TextEditor) newContent.GetContent (typeof(TextEditor)); if (fileInfo.Line > 0 && ipos != null) { - Mono.TextEditor.Utils.FileSettingsStore.Remove (fileName); + FileSettingsStore.Remove (fileName); ipos.RunWhenLoaded (JumpToLine); } @@ -1436,8 +1476,12 @@ namespace MonoDevelop.Ide.Gui void JumpToLine () { - IEditableTextBuffer ipos = (IEditableTextBuffer) newContent.GetContent (typeof(IEditableTextBuffer)); - ipos.SetCaretTo (Math.Max(1, fileInfo.Line), Math.Max(1, fileInfo.Column), fileInfo.Options.HasFlag (OpenDocumentOptions.HighlightCaretLine)); + var ipos = (TextEditor) newContent.GetContent (typeof(TextEditor)); + var loc = new DocumentLocation (Math.Max(1, fileInfo.Line), Math.Max(1, fileInfo.Column)); + if (fileInfo.Offset >= 0) { + loc = ipos.OffsetToLocation (fileInfo.Offset); + } + ipos.SetCaretLocation (loc, fileInfo.Options.HasFlag (OpenDocumentOptions.HighlightCaretLine)); } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/WorkbenchWindow.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/WorkbenchWindow.cs index 92ed672f84..06098c686c 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/WorkbenchWindow.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/WorkbenchWindow.cs @@ -26,6 +26,7 @@ using System; using System.Collections.Generic; +using MonoDevelop.Components; namespace MonoDevelop.Ide.Gui { @@ -35,7 +36,7 @@ namespace MonoDevelop.Ide.Gui public WorkbenchWindow (): base (Gtk.WindowType.Toplevel) { - Mono.TextEditor.GtkWorkarounds.FixContainerLeak (this); + GtkWorkarounds.FixContainerLeak (this); this.Role = "workbench"; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Navigation/NavigationHistoryService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Navigation/NavigationHistoryService.cs index a1af09c25c..934c975f87 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Navigation/NavigationHistoryService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Navigation/NavigationHistoryService.cs @@ -159,7 +159,7 @@ namespace MonoDevelop.Ide.Navigation return point; } - IEditableTextBuffer editBuf = doc.GetContent<IEditableTextBuffer> (); + var editBuf = doc.Editor; if (editBuf != null) { point = new TextFileNavigationPoint (doc, editBuf); if (point != null) @@ -291,8 +291,8 @@ namespace MonoDevelop.Ide.Navigation currentDoc.Closed += HandleCurrentDocClosed; if (currentDoc.Editor != null) { - currentDoc.Editor.Document.TextReplaced += BufferTextChanged; - currentDoc.Editor.Caret.PositionChanged += BufferCaretPositionChanged; + currentDoc.Editor.TextChanged += BufferTextChanged; + currentDoc.Editor.CaretPositionChanged += BufferCaretPositionChanged; } } @@ -308,8 +308,8 @@ namespace MonoDevelop.Ide.Navigation currentDoc.Closed -= HandleCurrentDocClosed; if (currentDoc.Editor != null) { - currentDoc.Editor.Document.TextReplaced -= BufferTextChanged; - currentDoc.Editor.Caret.PositionChanged -= BufferCaretPositionChanged; + currentDoc.Editor.TextChanged -= BufferTextChanged; + currentDoc.Editor.CaretPositionChanged -= BufferCaretPositionChanged; } currentDoc = null; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Navigation/TextFileNavigationPoint.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Navigation/TextFileNavigationPoint.cs index 82087312c9..d17c03e0c9 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Navigation/TextFileNavigationPoint.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Navigation/TextFileNavigationPoint.cs @@ -31,6 +31,7 @@ using System; using MonoDevelop.Ide.Gui.Content; using MonoDevelop.Core; using MonoDevelop.Ide.Gui; +using MonoDevelop.Ide.Editor; namespace MonoDevelop.Ide.Navigation { @@ -39,10 +40,12 @@ namespace MonoDevelop.Ide.Navigation int line; int column; - public TextFileNavigationPoint (Document doc, IEditableTextBuffer buffer) + public TextFileNavigationPoint (Document doc, TextEditor buffer) : base (doc) { - buffer.GetLineColumnFromPosition (buffer.CursorPosition, out line, out column); + var location = buffer.CaretLocation; + line = location.Line; + column = location.Column; } public TextFileNavigationPoint (FilePath file, int line, int column) @@ -79,11 +82,11 @@ namespace MonoDevelop.Ide.Navigation { Document doc = base.DoShow (); if (doc != null) { - IEditableTextBuffer buf = doc.GetContent<IEditableTextBuffer> (); + var buf = doc.Editor; if (buf != null) { doc.DisableAutoScroll (); buf.RunWhenLoaded (() => { - buf.SetCaretTo (Math.Max (line, 1), Math.Max (column, 1)); + buf.SetCaretLocation (Math.Max (line, 1), Math.Max (column, 1)); }); } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.ProgressMonitoring/MultiTaskDialogProgressMonitor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.ProgressMonitoring/MultiTaskDialogProgressMonitor.cs index ac66b89aef..2cd7be2053 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.ProgressMonitoring/MultiTaskDialogProgressMonitor.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.ProgressMonitoring/MultiTaskDialogProgressMonitor.cs @@ -34,6 +34,7 @@ using MonoDevelop.Core; using MonoDevelop.Core.ProgressMonitoring; using MonoDevelop.Ide.Gui.Dialogs; using MonoDevelop.Ide.Gui; +using MonoDevelop.Components; namespace MonoDevelop.Ide.ProgressMonitoring { @@ -74,7 +75,7 @@ namespace MonoDevelop.Ide.ProgressMonitoring TransientFor = parent, }; MessageService.PlaceDialog (dialog, parent); - Mono.TextEditor.GtkWorkarounds.PresentWindowWithNotification (dialog); + GtkWorkarounds.PresentWindowWithNotification (dialog); dialog.CancellationTokenSource = CancellationTokenSource; DispatchService.RunPendingEvents (); this.showDetails = showDetails; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ApplyPolicyDialog.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ApplyPolicyDialog.cs index e5d8570872..876fd4dc31 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ApplyPolicyDialog.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ApplyPolicyDialog.cs @@ -30,6 +30,7 @@ using MonoDevelop.Ide; using MonoDevelop.Core; using System.Collections.Generic; using System.Text; +using MonoDevelop.Components; namespace MonoDevelop.Ide.Projects { @@ -206,7 +207,7 @@ namespace MonoDevelop.Ide.Projects return true; } - using (var layout = Mono.TextEditor.PangoUtil.CreateLayout (this)) { + using (var layout = PangoUtil.CreateLayout (this)) { layout.SetMarkup ("<i>" + GLib.Markup.EscapeText (message) + "</i>"); int w, h; layout.GetPixelSize (out w, out h); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/AssemblyReferencePanel.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/AssemblyReferencePanel.cs index 4d2ea70c0d..3a19b8397c 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/AssemblyReferencePanel.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/AssemblyReferencePanel.cs @@ -283,7 +283,7 @@ namespace MonoDevelop.Ide.Projects { StringBuilder result = new StringBuilder (); int lastPos = 0; - var color = Mono.TextEditor.HslColor.GenerateHighlightColors (widget.Style.Base (StateType.Normal), + var color = HslColor.GenerateHighlightColors (widget.Style.Base (StateType.Normal), widget.Style.Text (StateType.Normal), 3)[2]; for (int n=0; n < matches.Length; n++) { int pos = matches[n] - startIndex; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/GtkNewProjectDialogBackend.UI.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/GtkNewProjectDialogBackend.UI.cs index c1b7cfd6b7..a6477cfad3 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/GtkNewProjectDialogBackend.UI.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/GtkNewProjectDialogBackend.UI.cs @@ -75,7 +75,6 @@ namespace MonoDevelop.Ide.Projects WidthRequest = 901;
HeightRequest = 632;
- Modal = true;
Name = "wizard_dialog";
Title = GettextCatalog.GetString ("New Project");
WindowPosition = WindowPosition.CenterOnParent;
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/NewProjectController.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/NewProjectController.cs index 155bfca65d..473abc0799 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/NewProjectController.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/NewProjectController.cs @@ -195,7 +195,7 @@ namespace MonoDevelop.Ide.Projects if (BasePath == null)
BasePath = IdeApp.ProjectOperations.ProjectsDefaultPath;
- projectConfiguration.Location = FileService.ResolveFullPath (BasePath);
+ projectConfiguration.Location = new FilePath (BasePath).ResolveLinks ();
}
void SetDefaultGitSettings ()
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/PackageReferencePanel.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/PackageReferencePanel.cs index ad2aa73f1a..81850c4eb2 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/PackageReferencePanel.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/PackageReferencePanel.cs @@ -240,7 +240,7 @@ namespace MonoDevelop.Ide.Projects { StringBuilder result = new StringBuilder (); int lastPos = 0; - var color = Mono.TextEditor.HslColor.GenerateHighlightColors (widget.Style.Base (StateType.Normal), + var color = HslColor.GenerateHighlightColors (widget.Style.Base (StateType.Normal), widget.Style.Text (StateType.Normal), 3)[2]; for (int n=0; n < matches.Length; n++) { int pos = matches[n] - startIndex; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/SelectReferenceDialog.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/SelectReferenceDialog.cs index c8a995266b..6504bf4fc7 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/SelectReferenceDialog.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/SelectReferenceDialog.cs @@ -30,15 +30,12 @@ using System.Linq; using MonoDevelop.Projects; using MonoDevelop.Core; -using MonoDevelop.Core.Assemblies; using Gtk; using System.Collections.Generic; using MonoDevelop.Components; -using MonoDevelop.Ide.Commands; using MonoDevelop.Components.Commands; using System.IO; -using Mono.TextEditor; namespace MonoDevelop.Ide.Projects { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Tasks/CommentTasksChangedEventHandler.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Tasks/CommentTasksChangedEventHandler.cs index 514601b9f1..945cd9a5fb 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Tasks/CommentTasksChangedEventHandler.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Tasks/CommentTasksChangedEventHandler.cs @@ -33,11 +33,11 @@ namespace MonoDevelop.Ide.Tasks { public class CommentTasksChangedEventArgs : EventArgs { - string filename; - IList<Tag> tagComments; - Project project; + readonly string filename; + readonly IReadOnlyList<Tag> tagComments; + readonly Project project; - public CommentTasksChangedEventArgs (string filename, IList<Tag> tagComments, Project project) + public CommentTasksChangedEventArgs (string filename, IReadOnlyList<Tag> tagComments, Project project) { this.filename = filename; this.tagComments = tagComments; @@ -46,7 +46,7 @@ namespace MonoDevelop.Ide.Tasks public string FileName { get { return filename; } } - public IList<Tag> TagComments { get { return tagComments; } } + public IReadOnlyList<Tag> TagComments { get { return tagComments; } } public Project Project { get { return project; } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Tasks/CommentTasksView.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Tasks/CommentTasksView.cs index b01e917033..509f2998b7 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Tasks/CommentTasksView.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Tasks/CommentTasksView.cs @@ -36,10 +36,11 @@ using MonoDevelop.Core; using MonoDevelop.Projects; using MonoDevelop.Ide; using MonoDevelop.Ide.Gui; -using MonoDevelop.Projects.Text; using MonoDevelop.Ide.TypeSystem; -using ICSharpCode.NRefactory.TypeSystem; using System.Linq; +using MonoDevelop.Ide.Editor; +using System.Threading; +using System.Threading.Tasks; namespace MonoDevelop.Ide.Tasks { @@ -83,9 +84,12 @@ namespace MonoDevelop.Ide.Tasks TaskService.CommentTasksChanged += OnCommentTasksChanged; CommentTag.SpecialCommentTagsChanged += OnCommentTagsChanged; - IdeApp.Workspace.WorkspaceItemLoaded += OnWorkspaceItemLoaded; + + MonoDevelopWorkspace.LoadingFinished += OnWorkspaceItemLoaded; IdeApp.Workspace.WorkspaceItemUnloaded += OnWorkspaceItemUnloaded; - + IdeApp.Workbench.DocumentOpened += WorkbenchDocumentOpened; + IdeApp.Workbench.DocumentClosed += WorkbenchDocumentClosed;; + highPrioColor = StringToColor ((string)PropertyService.Get ("Monodevelop.UserTasksHighPrioColor", "")); normalPrioColor = StringToColor ((string)PropertyService.Get ("Monodevelop.UserTasksNormalPrioColor", "")); lowPrioColor = StringToColor ((string)PropertyService.Get ("Monodevelop.UserTasksLowPrioColor", "")); @@ -124,15 +128,8 @@ namespace MonoDevelop.Ide.Tasks col.Resizable = true; LoadColumnsVisibility (); - - comments.BeginTaskUpdates (); - try { - foreach (var item in IdeApp.Workspace.Items) { - LoadWorkspaceItemContents (item); - } - } finally { - comments.EndTaskUpdates (); - } + + OnWorkspaceItemLoaded (null, EventArgs.Empty); comments.TasksAdded += GeneratedTaskAdded; comments.TasksRemoved += GeneratedTaskRemoved; @@ -147,7 +144,7 @@ namespace MonoDevelop.Ide.Tasks view.RowActivated -= OnRowActivated; TaskService.CommentTasksChanged -= OnCommentTasksChanged; CommentTag.SpecialCommentTagsChanged -= OnCommentTagsChanged; - IdeApp.Workspace.WorkspaceItemLoaded -= OnWorkspaceItemLoaded; + MonoDevelopWorkspace.LoadingFinished -= OnWorkspaceItemLoaded; IdeApp.Workspace.WorkspaceItemUnloaded -= OnWorkspaceItemUnloaded; comments.TasksAdded -= GeneratedTaskAdded; comments.TasksRemoved -= GeneratedTaskRemoved; @@ -156,6 +153,38 @@ namespace MonoDevelop.Ide.Tasks }; } + void WorkbenchDocumentClosed (object sender, DocumentEventArgs e) + { + e.Document.DocumentParsed -= HandleDocumentParsed; + } + + void WorkbenchDocumentOpened (object sender, DocumentEventArgs e) + { + e.Document.DocumentParsed += HandleDocumentParsed; + } + + void HandleDocumentParsed (object sender, EventArgs e) + { + var doc = (Document)sender; + var pd = doc.ParsedDocument; + var project = doc.Project; + if (pd == null || project == null) + return; + ProjectCommentTags tags; + if (!projectTags.TryGetValue (project, out tags)) + return; + var token = src.Token; + var file = doc.FileName; + Task.Run (async () => { + try { + tags.UpdateTags (project, file, await pd.GetTagCommentsAsync (token)); + } catch (TaskCanceledException) { + } catch (AggregateException ae) { + ae.Flatten ().Handle (x => x is TaskCanceledException); + } + }); + } + void LoadColumnsVisibility () { string columns = (string)PropertyService.Get ("Monodevelop.CommentTasksColumns", "TRUE;TRUE;TRUE;TRUE"); @@ -181,76 +210,66 @@ namespace MonoDevelop.Ide.Tasks PropertyService.Set ("Monodevelop.CommentTasksColumns", columns); } - void OnWorkspaceItemLoaded (object sender, WorkspaceItemEventArgs e) + void OnWorkspaceItemLoaded (object sender, EventArgs e) { comments.BeginTaskUpdates (); try { - LoadWorkspaceItemContents (e.Item); + foreach (var sln in IdeApp.Workspace.GetAllSolutions ()) + LoadSolutionContents (sln); } finally { comments.EndTaskUpdates (); } } - - void LoadWorkspaceItemContents (WorkspaceItem wob) - { - foreach (var sln in wob.GetAllItems<Solution> ()) - LoadSolutionContents (sln); - } - void UpdateCommentTagsForProject (Solution solution, Project project) + Dictionary<Project, ProjectCommentTags> projectTags = new Dictionary<Project, ProjectCommentTags> (); + CancellationTokenSource src = new CancellationTokenSource (); + void UpdateCommentTagsForProject (Solution solution, Project project, CancellationToken token) { - var ctx = TypeSystemService.GetProjectContentWrapper (project); - if (ctx == null) + if (token.IsCancellationRequested) return; - var tags = ctx.GetExtensionObject<ProjectCommentTags> (); - if (tags == null) { + ProjectCommentTags tags; + if (!projectTags.TryGetValue (project, out tags)) { tags = new ProjectCommentTags (); - ctx.UpdateExtensionObject (tags); - tags.Update (ctx.Project); - } else { - foreach (var kv in tags.Tags) { - UpdateCommentTags (solution, kv.Key, kv.Value); - } + projectTags [project] = tags; } - } - - void HandleSolutionItemAdded (object sender, SolutionItemChangeEventArgs e) - { - var newProject = e.SolutionItem as Project; - if (newProject == null) - return; - UpdateCommentTagsForProject (e.Solution, newProject); + var files = project.Files.ToArray (); + Task.Run (async () => { + try { + await tags.UpdateAsync (project, files, token); + } catch (TaskCanceledException) { + } catch (AggregateException ae) { + ae.Flatten ().Handle (x => x is TaskCanceledException); + } catch (Exception e) { + LoggingService.LogError ("Error while updating comment tags.", e); + } + }); } void LoadSolutionContents (Solution sln) { + src.Cancel (); + src = new CancellationTokenSource (); + var token = src.Token; + loadedSlns.Add (sln); - System.Threading.ThreadPool.QueueUserWorkItem (delegate { - sln.SolutionItemAdded += HandleSolutionItemAdded; + Task.Run (delegate { + sln.SolutionItemAdded += delegate(object sender, SolutionItemChangeEventArgs e) { + var newProject = e.SolutionItem as Project; + if (newProject == null) + return; + UpdateCommentTagsForProject (sln, newProject, token); + }; // Load all tags that are stored in pidb files foreach (Project p in sln.GetAllProjects ()) { - UpdateCommentTagsForProject (sln, p); + UpdateCommentTagsForProject (sln, p, token); } }); } - - static IEnumerable<Tag> GetSpecialComments (IProjectContent ctx, string name) - { - var doc = ctx.GetFile (name) as ParsedDocument; - if (doc == null) - return Enumerable.Empty<Tag> (); - return (IEnumerable<Tag>)doc.TagComments; - } - void OnWorkspaceItemUnloaded (object sender, WorkspaceItemEventArgs e) { - foreach (var sln in e.Item.GetAllItems<Solution>()) { - if (loadedSlns.Remove (sln)) - sln.SolutionItemAdded -= HandleSolutionItemAdded; - } comments.RemoveItemTasks (e.Item, true); } @@ -506,7 +525,7 @@ namespace MonoDevelop.Ide.Tasks { TaskListEntry task = SelectedTask; if (task != null && ! String.IsNullOrEmpty (task.FileName)) { - Document doc = IdeApp.Workbench.OpenDocument (task.FileName, Math.Max (1, task.Line), Math.Max (1, task.Column)); + var doc = IdeApp.Workbench.OpenDocument (task.FileName, Math.Max (1, task.Line), Math.Max (1, task.Column)); if (doc != null && doc.HasProject && doc.Project is DotNetProject) { string[] commentTags = doc.CommentTags; if (commentTags != null && commentTags.Length == 1) { @@ -515,10 +534,11 @@ namespace MonoDevelop.Ide.Tasks string line = doc.Editor.GetLineText (task.Line); int index = line.IndexOf (commentTags[0]); if (index != -1) { - doc.Editor.SetCaretTo (task.Line, task.Column); + doc.Editor.CaretLocation = new DocumentLocation (task.Line, task.Column); + doc.Editor.StartCaretPulseAnimation (); line = line.Substring (0, index); - var ls = doc.Editor.Document.GetLine (task.Line); - doc.Editor.Replace (ls.Offset, ls.Length, line); + var ls = doc.Editor.GetLine (task.Line); + doc.Editor.ReplaceText (ls.Offset, ls.Length, line); comments.Remove (task); } }); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ProjectCommentTags.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Tasks/ProjectCommentTags.cs index c2b33ee0b0..9f9fcb9476 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ProjectCommentTags.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Tasks/ProjectCommentTags.cs @@ -29,11 +29,13 @@ using System.Collections.Generic; using System.Collections.Concurrent; using MonoDevelop.Projects; using MonoDevelop.Ide.Tasks; +using System.Threading; +using MonoDevelop.Ide.TypeSystem; +using System.Threading.Tasks; -namespace MonoDevelop.Ide.TypeSystem +namespace MonoDevelop.Ide.Tasks { - [Serializable] - public class ProjectCommentTags + class ProjectCommentTags { readonly Dictionary<string, List<Tag>> tags = new Dictionary<string, List<Tag>> (); @@ -43,7 +45,7 @@ namespace MonoDevelop.Ide.TypeSystem } } - public void UpdateTags (Project project, string fileName, IList<Tag> tagComments) + public void UpdateTags (Project project, string fileName, IReadOnlyList<Tag> tagComments) { var list = tagComments == null || tagComments.Count == 0 ? null : new List<Tag> (tagComments); lock (tags) { @@ -67,14 +69,15 @@ namespace MonoDevelop.Ide.TypeSystem TaskService.InformCommentTasks (new CommentTasksChangedEventArgs (fileName, null, project)); } - internal void Update (Project project) + internal async Task UpdateAsync (Project project, ProjectFile[] files, CancellationToken token = default (CancellationToken)) { - foreach (var file in project.Files) { + foreach (var file in files) { if (file.BuildAction == BuildAction.None) continue; - TypeSystemService.ParseFile (project, file.FilePath); + var pd = await TypeSystemService.ParseFile (project, file.FilePath, token).ConfigureAwait (false); + if (pd != null) + UpdateTags (project, file.FilePath, await pd.GetTagCommentsAsync (token)); } } } -} - +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Tasks/UserTask.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Tasks/TaskListEntry.cs index 9ddda8db4d..fb8db5ea77 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Tasks/UserTask.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Tasks/TaskListEntry.cs @@ -1,5 +1,5 @@ // -// Task.cs +// TaskListEntry.cs // // Author: // Lluis Sanchez Gual <lluis@novell.com> @@ -31,7 +31,7 @@ using System.CodeDom.Compiler; using MonoDevelop.Projects; using MonoDevelop.Ide.Gui; using MonoDevelop.Ide.Gui.Components; -using MonoDevelop.Ide.Gui.Pads.ProjectPad;
+using MonoDevelop.Ide.Gui.Pads.ProjectPad; using MonoDevelop.Core; using MonoDevelop.Core.Serialization; @@ -64,7 +64,7 @@ namespace MonoDevelop.Ide.Tasks bool completed; object owner; - WorkspaceObject parentObject; + IWorkspaceObject parentObject; internal int SavedLine; public TaskListEntry (FilePath file, string description, int column, int line, TaskSeverity severity) @@ -77,12 +77,12 @@ namespace MonoDevelop.Ide.Tasks { } - public TaskListEntry (FilePath file, string description, int column, int line, TaskSeverity severity, TaskPriority priority, WorkspaceObject parent) + public TaskListEntry (FilePath file, string description, int column, int line, TaskSeverity severity, TaskPriority priority, IWorkspaceObject parent) : this (file, description, column, line, severity, priority, parent, null) { } - public TaskListEntry (FilePath file, string description, int column, int line, TaskSeverity severity, TaskPriority priority, WorkspaceObject parent, object owner) + public TaskListEntry (FilePath file, string description, int column, int line, TaskSeverity severity, TaskPriority priority, IWorkspaceObject parent, object owner) { this.file = file; this.description = description; @@ -106,7 +106,7 @@ namespace MonoDevelop.Ide.Tasks public TaskListEntry (BuildError error, object owner) { - parentObject = error.SourceTarget as WorkspaceObject; + parentObject = error.SourceTarget; file = error.FileName; this.owner = owner; description = error.ErrorText; @@ -179,7 +179,7 @@ namespace MonoDevelop.Ide.Tasks } } - public WorkspaceObject WorkspaceObject { + public IWorkspaceObject WorkspaceObject { get { return parentObject; } @@ -224,17 +224,17 @@ namespace MonoDevelop.Ide.Tasks TaskService.InformJumpToTask (this); } - public bool BelongsToItem (WorkspaceObject item, bool checkHierarchy) + public bool BelongsToItem (IWorkspaceObject item, bool checkHierarchy) { if (!checkHierarchy) return item == parentObject; - WorkspaceObject cit = parentObject; + IWorkspaceObject cit = parentObject; do { if (cit == item) return true; - if (cit is SolutionFolderItem) { - SolutionFolderItem si = (SolutionFolderItem) cit; + if (cit is SolutionItem) { + SolutionItem si = (SolutionItem) cit; if (si.ParentFolder != null) cit = si.ParentFolder; else diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Tasks/TaskService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Tasks/TaskService.cs index a00767d569..408d4189cb 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Tasks/TaskService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Tasks/TaskService.cs @@ -54,8 +54,10 @@ namespace MonoDevelop.Ide.Tasks static TaskService () { - IdeApp.Workspace.WorkspaceItemLoaded += OnWorkspaceItemLoaded; - IdeApp.Workspace.WorkspaceItemUnloaded += OnWorkspaceItemUnloaded; + if (IdeApp.Workspace != null) { + IdeApp.Workspace.WorkspaceItemLoaded += OnWorkspaceItemLoaded; + IdeApp.Workspace.WorkspaceItemUnloaded += OnWorkspaceItemUnloaded; + } errors.ItemName = GettextCatalog.GetString ("Warning/Error"); userTasks.ItemName = GettextCatalog.GetString ("User Task"); } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Tasks/TaskStore.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Tasks/TaskStore.cs index 5bc6c0215d..7db2b7aef8 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Tasks/TaskStore.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Tasks/TaskStore.cs @@ -23,17 +23,17 @@ // 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. -//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:2.0.50727.3074
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-using System;
+//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version:2.0.50727.3074 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; using System.Collections; using System.Collections.Generic; using MonoDevelop.Core; @@ -42,12 +42,12 @@ using MonoDevelop.Ide.Gui; using MonoDevelop.Ide.Gui.Content; using MonoDevelop.Ide.Navigation; using MonoDevelop.Ide.TextEditing; -using MonoDevelop.Ide.Desktop;
-
-namespace MonoDevelop.Ide.Tasks
-{
- public class TaskStore: IEnumerable<TaskListEntry>, ILocationList
- {
+using MonoDevelop.Ide.Desktop; + +namespace MonoDevelop.Ide.Tasks +{ + public class TaskStore: IEnumerable<TaskListEntry>, ILocationList + { int taskUpdateCount; List<TaskListEntry> tasks = new List<TaskListEntry> (); Dictionary<FilePath,TaskListEntry[]> taskIndex = new Dictionary<FilePath, TaskListEntry[]> (); @@ -59,11 +59,13 @@ namespace MonoDevelop.Ide.Tasks List<TaskListEntry> tasksAdded; List<TaskListEntry> tasksRemoved; - public TaskStore ()
- {
- IdeApp.Workspace.FileRenamedInProject += ProjectFileRenamed; - IdeApp.Workspace.FileRemovedFromProject += ProjectFileRemoved; - + public TaskStore () + { + if (IdeApp.Workspace != null) { + IdeApp.Workspace.FileRenamedInProject += ProjectFileRenamed; + IdeApp.Workspace.FileRemovedFromProject += ProjectFileRemoved; + } + TextEditorService.LineCountChangesCommitted += delegate (object sender, TextFileEventArgs args) { foreach (TaskListEntry task in GetFileTasks (args.TextFile.Name.FullPath)) task.SavedLine = -1; @@ -198,7 +200,7 @@ namespace MonoDevelop.Ide.Tasks yield return t; } } -
+ public TaskListEntry[] GetFileTasks (FilePath file) { TaskListEntry[] ta; @@ -523,7 +525,7 @@ namespace MonoDevelop.Ide.Tasks } #endregion - }
+ } public delegate void TaskEventHandler (object sender, TaskEventArgs e); @@ -545,4 +547,4 @@ namespace MonoDevelop.Ide.Tasks get { return tasks; } } } -}
+} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/CodeDomFileDescriptionTemplate.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/CodeDomFileDescriptionTemplate.cs index 3a57928536..964f13b1eb 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/CodeDomFileDescriptionTemplate.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/CodeDomFileDescriptionTemplate.cs @@ -35,8 +35,8 @@ using System.CodeDom.Compiler; using MonoDevelop.Projects; -using MonoDevelop.Ide.Gui.Content; using MonoDevelop.Core; +using MonoDevelop.Ide.Editor; namespace MonoDevelop.Ide.Templates { @@ -89,7 +89,7 @@ namespace MonoDevelop.Ide.Templates static string StripHeaderAndBlankLines (string text, CodeDomProvider provider) { - Mono.TextEditor.TextDocument doc = new Mono.TextEditor.TextDocument (); + var doc = TextEditorFactory.CreateNewDocument (); doc.Text = text; int realStartLine = 0; for (int i = 1; i <= doc.LineCount; i++) { @@ -106,20 +106,20 @@ namespace MonoDevelop.Ide.Templates // We reformat the C# generated output to the user's coding style anyway, but the reformatter preserves blank lines if (provider is Microsoft.CSharp.CSharpCodeProvider) { for (int i = 1; i <= doc.LineCount; i++) { - Mono.TextEditor.DocumentLine line = doc.GetLine (i); + var line = doc.GetLine (i); if (IsBlankLine (doc, line) && line.LengthIncludingDelimiter > 0) { - doc.Remove (line.Offset, line.LengthIncludingDelimiter); + doc.RemoveText (line.Offset, line.LengthIncludingDelimiter); i--; continue; } } } - int offset = doc.GetLine (Math.Max(Mono.TextEditor.DocumentLocation.MinLine, realStartLine)).Offset; - return doc.GetTextAt (offset, doc.TextLength - offset); + int offset = doc.GetLine (realStartLine).Offset; + return doc.GetTextAt (offset, doc.Length - offset); } - static bool IsBlankLine (Mono.TextEditor.TextDocument doc, Mono.TextEditor.DocumentLine line) + static bool IsBlankLine (IReadonlyTextDocument doc, IDocumentLine line) { for (int i = 0; i < line.Length; i++) { if (!Char.IsWhiteSpace (doc.GetCharAt (line.Offset + i))) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/ProjectTemplatingProvider.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/ProjectTemplatingProvider.cs index d23660ee33..b47ae8a685 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/ProjectTemplatingProvider.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/ProjectTemplatingProvider.cs @@ -76,8 +76,8 @@ namespace MonoDevelop.Ide.Templates ProjectCreateInformation CreateProjectCreateInformation (NewProjectConfiguration config, SolutionFolder parentFolder)
{
ProjectCreateInformation cinfo = new ProjectCreateInformation ();
- cinfo.SolutionPath = FileService.ResolveFullPath (config.SolutionLocation);
- cinfo.ProjectBasePath = FileService.ResolveFullPath (config.ProjectLocation);
+ cinfo.SolutionPath = new FilePath (config.SolutionLocation).ResolveLinks ();
+ cinfo.ProjectBasePath = new FilePath (config.ProjectLocation).ResolveLinks ();
cinfo.ProjectName = config.ProjectName;
cinfo.SolutionName = config.SolutionName;
cinfo.ParentFolder = parentFolder;
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/SingleFileDescriptionTemplate.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/SingleFileDescriptionTemplate.cs index 62332a28b5..6c334b9185 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/SingleFileDescriptionTemplate.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/SingleFileDescriptionTemplate.cs @@ -40,6 +40,7 @@ using MonoDevelop.Ide.StandardHeader; using System.Text; using MonoDevelop.Ide.Gui.Content; using MonoDevelop.Ide.CodeFormatting; +using MonoDevelop.Ide.Editor; using MonoDevelop.Projects.SharedAssetsProjects; using MonoDevelop.Core.StringParsing; @@ -317,7 +318,7 @@ namespace MonoDevelop.Ide.Templates ms.Write (data, 0, data.Length); } - Mono.TextEditor.TextDocument doc = new Mono.TextEditor.TextDocument (); + var doc = TextEditorFactory.CreateNewDocument (); doc.Text = content; TextStylePolicy textPolicy = policyParent != null ? policyParent.Policies.Get<TextStylePolicy> ("text/plain") @@ -327,7 +328,7 @@ namespace MonoDevelop.Ide.Templates var tabToSpaces = textPolicy.TabsToSpaces? new string (' ', textPolicy.TabWidth) : null; - foreach (Mono.TextEditor.DocumentLine line in doc.Lines) { + foreach (var line in doc.GetLines ()) { var lineText = doc.GetTextAt (line.Offset, line.Length); if (tabToSpaces != null) lineText = lineText.Replace ("\t", tabToSpaces); @@ -400,8 +401,8 @@ namespace MonoDevelop.Ide.Templates } } - tags ["Namespace"] = ns;
- if (policyParent != null)
+ tags ["Namespace"] = ns; + if (policyParent != null) tags ["SolutionName"] = policyParent.Name; if (project != null) { tags ["ProjectName"] = project.Name; @@ -418,7 +419,7 @@ namespace MonoDevelop.Ide.Templates if (languageExtension.Length > 0) tags ["LanguageExtension"] = languageExtension; - if (fileName != FilePath.Null) {
+ if (fileName != FilePath.Null) { FilePath fileDirectory = Path.GetDirectoryName (fileName); if (project != null && project.BaseDirectory != FilePath.Null && fileDirectory.IsChildPathOf (project.BaseDirectory)) tags ["ProjectRelativeDirectory"] = fileDirectory.ToRelative (project.BaseDirectory); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/FileExtension.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/FileExtension.cs index 03ea1cb117..d0ccd99bc7 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/FileExtension.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/FileExtension.cs @@ -28,7 +28,6 @@ using System; using System.Linq; using MonoDevelop.Core; -using Mono.TextEditor; using System.Collections.Generic; using MonoDevelop.Projects.Text; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/FileExtensionEventArgs.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/FileExtensionEventArgs.cs index 876a9d2581..79d246e707 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/FileExtensionEventArgs.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/FileExtensionEventArgs.cs @@ -27,7 +27,6 @@ using System; using System.Linq; using MonoDevelop.Core; -using Mono.TextEditor; using System.Collections.Generic; using MonoDevelop.Projects.Text; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/FileLineExtension.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/FileLineExtension.cs index ec22e90aff..b5278f365a 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/FileLineExtension.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/FileLineExtension.cs @@ -27,7 +27,6 @@ using System; using System.Linq; using MonoDevelop.Core; -using Mono.TextEditor; using System.Collections.Generic; using MonoDevelop.Projects.Text; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/HorizontalAlignment.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/HorizontalAlignment.cs index 3863a5ea86..f124f748c7 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/HorizontalAlignment.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/HorizontalAlignment.cs @@ -27,7 +27,6 @@ using System; using System.Linq; using MonoDevelop.Core; -using Mono.TextEditor; using System.Collections.Generic; using MonoDevelop.Projects.Text; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/LineCountEventArgs.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/LineCountEventArgs.cs index bf252a697c..95eb469c54 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/LineCountEventArgs.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/LineCountEventArgs.cs @@ -27,7 +27,6 @@ using System; using System.Linq; using MonoDevelop.Core; -using Mono.TextEditor; using System.Collections.Generic; using MonoDevelop.Projects.Text; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/TextEditorService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/TextEditorService.cs index 595db9c80a..74385431b9 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/TextEditorService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/TextEditorService.cs @@ -27,7 +27,6 @@ using System; using System.Linq; using MonoDevelop.Core; -using Mono.TextEditor; using System.Collections.Generic; using MonoDevelop.Projects.Text; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/TextFileEventArgs.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/TextFileEventArgs.cs index d2c3a245c5..53c4ed6e41 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/TextFileEventArgs.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/TextFileEventArgs.cs @@ -27,7 +27,6 @@ using System; using System.Linq; using MonoDevelop.Core; -using Mono.TextEditor; using System.Collections.Generic; using MonoDevelop.Projects.Text; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/TextLineMarkerExtension.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/TextLineMarkerExtension.cs index 6126390e80..67dee58ab2 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/TextLineMarkerExtension.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/TextLineMarkerExtension.cs @@ -27,16 +27,14 @@ using System; using System.Linq; using MonoDevelop.Core; -using Mono.TextEditor; using System.Collections.Generic; using MonoDevelop.Projects.Text; +using MonoDevelop.Ide.Editor; namespace MonoDevelop.Ide.TextEditing { - public abstract class TextLineMarkerExtension: FileLineExtension { - public abstract TextLineMarker CreateMarker (); - } - + public abstract ITextLineMarker CreateMarker (); } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/VerticalAlignment.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/VerticalAlignment.cs index 47f318f92e..d72bfb9e71 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/VerticalAlignment.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TextEditing/VerticalAlignment.cs @@ -27,7 +27,6 @@ using System; using System.Linq; using MonoDevelop.Core; -using Mono.TextEditor; using System.Collections.Generic; using MonoDevelop.Projects.Text; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Ambience.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Ambience.cs index 65621369b2..07849bbe88 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Ambience.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Ambience.cs @@ -30,51 +30,74 @@ using System; using System.Text; using System.Collections.Generic; using MonoDevelop.Core; -using ICSharpCode.NRefactory.TypeSystem; using Mono.Cecil; using System.Linq; using MonoDevelop.Ide.CodeCompletion; +using Microsoft.CodeAnalysis; +using System.Threading; +using System.Xml; +using Mono.Addins; +using MonoDevelop.Ide.Extensions; namespace MonoDevelop.Ide.TypeSystem { - public abstract class Ambience - { - public string Name { - get; - private set; - } - - - public Ambience (string name) - { - this.Name = name; - } - - #region To implement - public abstract string GetIntrinsicTypeName (string reflectionName); - - public abstract string SingleLineComment (string text); - public abstract string GetString (string nameSpace, OutputSettings settings); - - protected abstract string GetTypeReferenceString (IType reference, OutputSettings settings); - protected abstract string GetTypeString (IType type, OutputSettings settings); - protected abstract string GetMethodString (IMethod method, OutputSettings settings); - protected abstract string GetConstructorString (IMethod constructor, OutputSettings settings); - protected abstract string GetDestructorString (IMethod destructor, OutputSettings settings); - protected abstract string GetOperatorString (IMethod op, OutputSettings settings); - - protected abstract string GetFieldString (IField field, OutputSettings settings); - protected abstract string GetEventString (IEvent evt, OutputSettings settings); - protected abstract string GetPropertyString (IProperty property, OutputSettings settings); - protected abstract string GetIndexerString (IProperty property, OutputSettings settings); - protected abstract string GetParameterString (IParameterizedMember member, IParameter parameter, OutputSettings settings); - - #endregion - - public virtual TooltipInformation GetTooltip (IEntity entity) + public static class Ambience + { + public static readonly SymbolDisplayFormat LabelFormat = + new SymbolDisplayFormat( + globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted, + typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, + propertyStyle: SymbolDisplayPropertyStyle.NameOnly, + genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters | SymbolDisplayGenericsOptions.IncludeVariance, + memberOptions: SymbolDisplayMemberOptions.IncludeParameters | SymbolDisplayMemberOptions.IncludeExplicitInterface, + parameterOptions: + SymbolDisplayParameterOptions.IncludeDefaultValue | + SymbolDisplayParameterOptions.IncludeExtensionThis | + SymbolDisplayParameterOptions.IncludeType | + SymbolDisplayParameterOptions.IncludeName | + SymbolDisplayParameterOptions.IncludeParamsRefOut, + miscellaneousOptions: + SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers | + SymbolDisplayMiscellaneousOptions.UseSpecialTypes + ); + /// <summary> + /// Standard format for displaying to the user. + /// </summary> + /// <remarks> + /// No return type. + /// </remarks> + public static readonly SymbolDisplayFormat NameFormat = + new SymbolDisplayFormat( + globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted, + typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, + propertyStyle: SymbolDisplayPropertyStyle.NameOnly, + genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters | SymbolDisplayGenericsOptions.IncludeVariance, + memberOptions: SymbolDisplayMemberOptions.IncludeParameters | SymbolDisplayMemberOptions.IncludeExplicitInterface, + parameterOptions: + SymbolDisplayParameterOptions.IncludeParamsRefOut | + SymbolDisplayParameterOptions.IncludeExtensionThis | + SymbolDisplayParameterOptions.IncludeType | + SymbolDisplayParameterOptions.IncludeName, + miscellaneousOptions: + SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers | + SymbolDisplayMiscellaneousOptions.UseSpecialTypes); + static Ambience () { - return null; + // may not have been initialized in testing environment. + if (AddinManager.IsInitialized) { + AddinManager.AddExtensionNodeHandler ("/MonoDevelop/TypeSystem/AmbienceTooltipProviders", delegate(object sender, ExtensionNodeEventArgs args) { + var node = args.ExtensionNode as MimeTypeExtensionNode; + switch (args.Change) { + case ExtensionChange.Add: + tooltipProviders.Add ((AmbienceTooltipProvider)node.CreateInstance ()); + break; + case ExtensionChange.Remove: + tooltipProviders.Remove ((AmbienceTooltipProvider)node.CreateInstance ()); + break; + } + }); + } } public static string Format (string str) @@ -86,104 +109,490 @@ namespace MonoDevelop.Ide.TypeSystem MarkupUtilities.AppendEscapedString (sb, str); return sb.ToString (); } + + #region Documentation - protected static OutputFlags GetFlags (object settings) + public class DocumentationFormatOptions { - if (settings is OutputFlags) - return (OutputFlags)settings; - return ((OutputSettings)settings).OutputFlags; + public static readonly DocumentationFormatOptions Empty = new DocumentationFormatOptions (); + public string HighlightParameter { + get; + set; + } + + public int MaxLineLength { + get; + set; + } + + public bool BigHeadings { + get; + set; + } + + public bool BoldHeadings { + get; + set; + } + + public bool SmallText { + get; + set; + } + + + public DocumentationFormatOptions () + { + BoldHeadings = true; + } + + public string FormatHeading (string heading) + { + string result = heading; + if (BigHeadings) + result = "<big>" + result + "</big>"; + if (BoldHeadings) + result = "<b>" + result + "</b>"; + return result; + } + + public string FormatBody (string body) + { + var str = body.Trim (); + if (string.IsNullOrEmpty (str)) + return ""; + return SmallText ? "<small>" + str + Environment.NewLine + "</small>" : str + Environment.NewLine; + } + } + + public static string GetSummaryMarkup (ISymbol member, CancellationToken cancellationToken = default(CancellationToken)) + { + if (member == null) + return null; + string documentation = GetDocumentation (member); + if (string.IsNullOrEmpty (documentation)) + return null; + + if (!string.IsNullOrEmpty (documentation)) { + int idx1 = documentation.IndexOf ("<summary>", StringComparison.Ordinal); + int idx2 = documentation.LastIndexOf ("</summary>", StringComparison.Ordinal); + string result; + if (idx1 >= 0 && idx2 > idx1) { + try { + var xmlText = documentation.Substring (idx1, idx2 - idx1 + "</summary>".Length); + return ParseBody (member, + new XmlTextReader (xmlText, XmlNodeType.Element, null), + "summary", + DocumentationFormatOptions.Empty + ); + } catch (Exception e) { + LoggingService.LogWarning ("Malformed documentation xml detected:" + documentation, e); + // may happen on malformed xml. + var len = idx2 - idx1 - "</summary>".Length; + result = len > 0 ? documentation.Substring (idx1 + "<summary>".Length, len) : documentation; + } + } else if (idx1 >= 0) { + result = documentation.Substring (idx1 + "<summary>".Length); + } else if (idx2 >= 0) { + result = documentation.Substring (0, idx2 - 1); + } else { + result = documentation; + } + return GetDocumentationMarkup (member, CleanEmpty (result)); + } + + return GetDocumentationMarkup (member, CleanEmpty (documentation)); } - protected static OutputSettings GetSettings (object settings) + static string CleanEmpty (string doc) + { + return IsEmptyDocumentation (doc)? null : doc; + } + + static bool IsEmptyDocumentation (string documentation) { - if (settings is OutputFlags) - return new OutputSettings ((OutputFlags)settings); - return (OutputSettings)settings; + return string.IsNullOrWhiteSpace (documentation) || documentation.StartsWith ("To be added") || documentation == "we have not entered docs yet"; } - public string GetString (string nameSpace, OutputFlags flags) + public static string GetDocumentation (ISymbol member) { - return GetString (nameSpace, new OutputSettings (flags)); + if (member == null) + return null; + var documentation = member.GetDocumentationCommentXml (); + if (string.IsNullOrEmpty (documentation)) + documentation = MonoDocDocumentationProvider.GetDocumentation (member); + if (documentation != null) + return CleanEmpty (documentation); + return null; } - public string GetString (IEntity entity, OutputSettings settings) - { - if (entity == null) { - string[] trace = Environment.StackTrace.Split (new [] { Environment.NewLine }, StringSplitOptions.None); - return "null entity: " + (trace != null && trace.Length > 2 ? trace [2] : "unknown location"); - } - string result = null; - switch (entity.SymbolKind) { - case SymbolKind.Constructor: - result = GetConstructorString ((IMethod)entity, settings); - break; - case SymbolKind.Destructor: - result = GetDestructorString ((IMethod)entity, settings); - break; - case SymbolKind.Event: - result = GetEventString ((IEvent)entity, settings); - break; - case SymbolKind.Field: - result = GetFieldString ((IField)entity, settings); - break; - case SymbolKind.Indexer: - result = GetPropertyString ((IProperty)entity, settings); - break; - case SymbolKind.Method: - result = GetMethodString ((IMethod)entity, settings); - break; - case SymbolKind.Operator: - result = GetMethodString ((IMethod)entity, settings); - break; - case SymbolKind.Property: - result = GetPropertyString ((IProperty)entity, settings); - break; - case SymbolKind.TypeDefinition: - result = GetTypeString ((ITypeDefinition)entity, settings); - break; - default: - throw new ArgumentOutOfRangeException ("SymbolKind", "Unknown entity type:" + entity.SymbolKind); - } - result = settings.PostProcess (entity, result); - return result; + static string GetCref (ICSharpCode.NRefactory.TypeSystem.ITypeResolveContext ctx, string cref) + { + if (cref == null) + return ""; + + if (cref.Length < 2) + return cref; + try { + var entity = new ICSharpCode.NRefactory.Documentation.DocumentationComment ("", ctx).ResolveCref (cref.Replace("<", "{").Replace(">", "}")); + + if (entity != null) { + var ambience = new ICSharpCode.NRefactory.CSharp.CSharpAmbience (); + ambience.ConversionFlags = ICSharpCode.NRefactory.TypeSystem.ConversionFlags.ShowParameterList | ICSharpCode.NRefactory.TypeSystem.ConversionFlags.ShowParameterNames | ICSharpCode.NRefactory.TypeSystem.ConversionFlags.ShowTypeParameterList; + return ambience.ConvertEntity (entity); + } + } catch (Exception e) { + LoggingService.LogWarning ("Invalid cref:" + cref, e); + } + + if (cref.Substring (1, 1) == ":") + return cref.Substring (2, cref.Length - 2); + + return cref; } - public string GetString (IType type, OutputSettings settings) + static bool IsSpecialChar (int charValue) { - var result = GetTypeString (type, settings); - return settings.PostProcess (type, result); + return + 0x01 <= charValue && charValue <= 0x08 || + 0x0B <= charValue && charValue <= 0x0C || + 0x0E <= charValue && charValue <= 0x1F || + 0x7F <= charValue && charValue <= 0x84 || + 0x86 <= charValue && charValue <= 0x9F; } - -/* public string GetString (ITypeReference reference, OutputSettings settings) + + public static string BreakLines (string text, int maxLineLength) { - var result = GetTypeReferenceString (reference, settings); - return settings.PostProcess (reference, result); - }*/ + if (maxLineLength <= 0) + return text; + StringBuilder result = new StringBuilder (); + int lineLength = 0; + bool inTag = false; + bool inAmp = false; + foreach (char ch in text) { + switch (ch) { + case '<': + inTag = true; + break; + case '>': + inTag = false; + break; + case '&': + inAmp = true; + break; + case ';': + inAmp = false; + break; + case '\n': + lineLength = 0; + break; + case '\r': + lineLength = 0; + break; + } + + result.Append (ch); + if (!inTag && !inAmp) + lineLength++; + if (!Char.IsLetterOrDigit (ch) && lineLength > maxLineLength) { + result.AppendLine (); + lineLength = 0; + } + } + return result.ToString (); + } - public string GetString (IEntity entity, OutputFlags flags) + public static string EscapeText (string text) { - return GetString (entity, new OutputSettings (flags)); + if (text == null) + return null; + StringBuilder result = new StringBuilder (); + foreach (char ch in text) { + switch (ch) { + case '<': + result.Append ("<"); + break; + case '>': + result.Append (">"); + break; + case '&': + result.Append ("&"); + break; + case '\'': + result.Append ("'"); + break; + case '"': + result.Append ("""); + break; + default: + int charValue = (int)ch; + if (IsSpecialChar (charValue)) { + result.AppendFormat ("&#x{0:X};", charValue); + } else { + result.Append (ch); + } + break; + } + } + return result.ToString (); } - public string GetString (IType type, OutputFlags flags) + public static string UnescapeText (string text) { - return GetString (type, new OutputSettings (flags)); + var sb = new StringBuilder (); + for (int i = 0; i < text.Length; i++) { + char ch = text[i]; + if (ch == '&') { + int end = text.IndexOf (';', i); + if (end == -1) + break; + string entity = text.Substring (i + 1, end - i - 1); + switch (entity) { + case "lt": + sb.Append ('<'); + break; + case "gt": + sb.Append ('>'); + break; + case "amp": + sb.Append ('&'); + break; + case "apos": + sb.Append ('\''); + break; + case "quot": + sb.Append ('"'); + break; + } + i = end; + } else { + sb.Append (ch); + } + } + return sb.ToString (); } - public string GetString (ITypeDefinition type, OutputFlags flags) + + public static string GetDocumentationMarkup (ISymbol member, string doc) { - return GetString ((IEntity)type, new OutputSettings (flags)); + return GetDocumentationMarkup (member, doc, DocumentationFormatOptions.Empty); } + static string ParseBody (ISymbol member, XmlTextReader xml, string endTagName, DocumentationFormatOptions options) + { + StringBuilder result = new StringBuilder (); + bool wasWhiteSpace = true; + bool appendSpace = false; + //ITypeResolveContext ctx = member.Compilation.TypeResolveContext; + while (xml.Read ()) { + switch (xml.NodeType) { + case XmlNodeType.EndElement: + if (xml.Name == endTagName) + goto end; + break; + case XmlNodeType.Element: + switch (xml.Name.ToLower ()) { + case "para": + result.AppendLine (ParseBody (member, xml, xml.Name, options)); + wasWhiteSpace = true; + break; + case "see": + if (!wasWhiteSpace) { + result.Append (' '); + wasWhiteSpace = true; + } + result.Append ("<i>"); + string name = (xml ["cref"] + xml ["langword"]).Trim (); + // if (options.Ambience != null) + // name = options.Ambience.GetIntrinsicTypeName (name); + result.Append (EscapeText (name)); + result.Append ("</i>"); + wasWhiteSpace = false; + appendSpace = true; + break; + case "paramref": + if (!wasWhiteSpace) { + result.Append (' '); + wasWhiteSpace = true; + } + result.Append ("<i>"); + result.Append (EscapeText (xml ["name"].Trim ())); + result.Append ("</i>"); + appendSpace = true; + wasWhiteSpace = false; + break; + } + break; + case XmlNodeType.Text: + if (IsEmptyDocumentation (xml.Value)) + break; + foreach (char ch in xml.Value) { + if (!Char.IsWhiteSpace (ch) && appendSpace) { + result.Append (' '); + appendSpace = false; + } + if (Char.IsWhiteSpace (ch) || ch == '\n' || ch == '\r') { + if (!wasWhiteSpace) { + result.Append (' '); + wasWhiteSpace = true; + } + continue; + } + wasWhiteSpace = false; + result.Append (EscapeText (ch.ToString ())); + } + break; + } + } + end: + return result.ToString ().Trim (); + } - public string GetString (IParameterizedMember member, IParameter parameter, OutputFlags flags) + public static string GetDocumentationMarkup (ISymbol member, string doc, DocumentationFormatOptions options) { - return GetParameterString (member, parameter, new OutputSettings (flags)); + if (string.IsNullOrEmpty (doc)) + return null; + System.IO.StringReader reader = new System.IO.StringReader ("<docroot>" + doc + "</docroot>"); + XmlTextReader xml = new XmlTextReader (reader); + StringBuilder ret = new StringBuilder (70); + StringBuilder parameterBuilder = new StringBuilder (); + StringBuilder exceptions = new StringBuilder (); + exceptions.AppendLine (options.FormatHeading (GettextCatalog.GetString ("Exceptions:"))); + // ret.Append ("<small>"); + int paramCount = 0, exceptionCount = 0, summaryEnd = -1; + try { + xml.Read (); + do { + if (xml.NodeType == XmlNodeType.Element) { + switch (xml.Name.ToLower ()) { + case "para": + ret.Append (options.FormatBody (ParseBody (member, xml, xml.Name, options))); + if (summaryEnd < 0) + summaryEnd = ret.Length; + break; + case "summary": + var summary = options.FormatBody (ParseBody (member, xml, xml.Name, options)); + if (!IsEmptyDocumentation (summary)) { + // ret.AppendLine (GetHeading ("Summary:", options)); + ret.Append (summary); + if (summaryEnd < 0) + summaryEnd = ret.Length; + } + break; + case "remarks": + if (string.IsNullOrEmpty (options.HighlightParameter)) { + ret.AppendLine (options.FormatHeading (GettextCatalog.GetString ("Remarks:"))); + ret.Append (options.FormatBody (ParseBody (member, xml, xml.Name, options))); + if (summaryEnd < 0) + summaryEnd = ret.Length; + } else { + options.FormatBody (ParseBody (member, xml, xml.Name, options)); + } + break; + // skip <example>-nodes + case "example": + xml.Skip (); + xml.Skip (); + break; + case "exception": + exceptionCount++; + if (options.SmallText) + exceptions.Append ("<small>"); + exceptions.Append ("<b>"); + exceptions.Append (EscapeText (xml ["cref"])); + exceptions.Append (": "); + exceptions.Append ("</b>"); + if (options.SmallText) + exceptions.Append ("</small>"); + + exceptions.AppendLine (options.FormatBody (ParseBody (member, xml, xml.Name, options))); + break; + case "returns": + if (string.IsNullOrEmpty (options.HighlightParameter)) { + ret.AppendLine (options.FormatHeading (GettextCatalog.GetString ("Returns:"))); + ret.Append (options.FormatBody (ParseBody (member, xml, xml.Name, options))); + } else { + options.FormatBody (ParseBody (member, xml, xml.Name, options)); + } + break; + case "param": + string paramName = xml.GetAttribute ("name") != null ? xml ["name"].Trim () : ""; + + var body = options.FormatBody (ParseBody (member, xml, xml.Name, options)); + if (!IsEmptyDocumentation (body)) { + paramCount++; + parameterBuilder.Append ("<i>"); + if (options.HighlightParameter == paramName) + parameterBuilder.Append ("<b>"); + if (options.SmallText) + parameterBuilder.Append ("<small>"); + parameterBuilder.Append (EscapeText (paramName)); + if (options.SmallText) + parameterBuilder.Append ("</small>"); + if (options.HighlightParameter == paramName) + parameterBuilder.Append ("</b>"); + parameterBuilder.Append (":</i> "); + parameterBuilder.Append (body); + } else { + return null; + } + break; + case "value": + ret.AppendLine (options.FormatHeading (GettextCatalog.GetString ("Value:"))); + ret.AppendLine (options.FormatBody (ParseBody (member, xml, xml.Name, options))); + break; + case "seealso": + if (string.IsNullOrEmpty (options.HighlightParameter)) { + ret.Append (options.FormatHeading (GettextCatalog.GetString ("See also:"))); + ret.Append (" " + EscapeText (xml ["cref"] + xml ["langword"])); + } + break; + } + } + } while (xml.Read ()); + + } catch (Exception ex) { + MonoDevelop.Core.LoggingService.LogError (ex.ToString ()); + return EscapeText (doc); + } + + if (IsEmptyDocumentation (ret.ToString ()) && IsEmptyDocumentation (parameterBuilder.ToString ())) + return EscapeText (doc); + if (string.IsNullOrEmpty (options.HighlightParameter) && exceptionCount > 0) + ret.Append (exceptions.ToString ()); + + string result = ret.ToString (); + if (summaryEnd < 0) + summaryEnd = result.Length; + if (paramCount > 0) { + var paramSb = new StringBuilder (); + if (result.Length > 0) + paramSb.AppendLine ();/* + paramSb.Append ("<small>"); + paramSb.AppendLine (options.FormatHeading (GettextCatalog.GetPluralString ("Parameter:", "Parameters:", paramCount))); + paramSb.Append ("</small>");*/ + paramSb.Append (parameterBuilder.ToString ()); + result = result.Insert (summaryEnd, paramSb.ToString ()); + } + result = result.Trim (); + if (result.EndsWith (Environment.NewLine + "</small>")) + result = result.Substring (0, result.Length - (Environment.NewLine + "</small>").Length) + "</small>"; + return result; } - /* - public string GetString (ITypeReference reference, OutputFlags flags) + #endregion + + #region Tooltips + static List<AmbienceTooltipProvider> tooltipProviders = new List<AmbienceTooltipProvider>(); + + public static TooltipInformation GetTooltip(ISymbol symbol) { - return GetString (reference, new OutputSettings (flags)); - }*/ + foreach (var tp in tooltipProviders) { + var result = tp.GetTooltip (symbol); + if (result != null) + return result; + } + return null; + } + #endregion } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/AmbienceService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/AmbienceService.cs deleted file mode 100644 index 794144d931..0000000000 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/AmbienceService.cs +++ /dev/null @@ -1,559 +0,0 @@ -// -// AmbienceService.cs -// -// Author: -// Mike Krüger <mkrueger@novell.com> -// -// Copyright (C) 2008 Novell, Inc (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Collections.Generic; - -using Mono.Addins; -using System.Xml; -using System.Text; -using ICSharpCode.NRefactory.TypeSystem; -using MonoDevelop.Ide; -using MonoDevelop.Projects; -using MonoDevelop.Core; -using MonoDevelop.Ide.Extensions; -using ICSharpCode.NRefactory.Documentation; - -namespace MonoDevelop.Ide.TypeSystem -{ - public static class AmbienceService - { - static Dictionary <string, Ambience> ambiences = new Dictionary <string, Ambience> (); - - static AmbienceService () - { - // may not have been initialized in testing environment. - if (AddinManager.IsInitialized) { - AddinManager.AddExtensionNodeHandler ("/MonoDevelop/TypeSystem/Ambiences", delegate(object sender, ExtensionNodeEventArgs args) { - var ambience = args.ExtensionNode as MimeTypeExtensionNode; - switch (args.Change) { - case ExtensionChange.Add: - ambiences[ambience.MimeType] = (Ambience) ambience.CreateInstance (); - break; - case ExtensionChange.Remove: - ambiences.Remove (ambience.MimeType); - break; - } - }); - } - } - - public static Ambience GetAmbience (IMember member) - { - return GetAmbienceForFile (member.DeclaringTypeDefinition.Region.FileName) ?? new NetAmbience (); - } - - public static Ambience GetAmbienceForFile (string fileName) - { - if (string.IsNullOrEmpty (fileName)) - return DefaultAmbience; - return GetAmbience (DesktopService.GetMimeTypeForUri (fileName)); - } - - public static Ambience GetAmbience (string mimeType) - { - Ambience result; - if (!string.IsNullOrEmpty (mimeType) && ambiences.TryGetValue (mimeType, out result)) - return result; - return defaultAmbience; - } - - static Ambience defaultAmbience = new NetAmbience (); - - public static Ambience DefaultAmbience { get { return defaultAmbience; } } - #region Documentation - - public class DocumentationFormatOptions - { - public static readonly DocumentationFormatOptions Empty = new DocumentationFormatOptions (); - public string HighlightParameter { - get; - set; - } - - public int MaxLineLength { - get; - set; - } - - public bool BigHeadings { - get; - set; - } - - public bool BoldHeadings { - get; - set; - } - - public bool SmallText { - get; - set; - } - - public Ambience Ambience { - get; - set; - } - - - public DocumentationFormatOptions () - { - BoldHeadings = true; - } - - public string FormatHeading (string heading) - { - string result = heading; - if (BigHeadings) - result = "<big>" + result + "</big>"; - if (BoldHeadings) - result = "<b>" + result + "</b>"; - return result; - } - - public string FormatBody (string body) - { - var str = body.Trim (); - if (string.IsNullOrEmpty (str)) - return ""; - return SmallText ? "<small>" + str + Environment.NewLine + "</small>" : str + Environment.NewLine; - } - } - - public static string GetSummaryMarkup (IEntity member) - { - if (member == null || member.Documentation == null) - return null; - string documentation = member.Documentation.Xml.Text; - if (!string.IsNullOrEmpty (documentation)) { - int idx1 = documentation.IndexOf ("<summary>", StringComparison.Ordinal); - int idx2 = documentation.LastIndexOf ("</summary>", StringComparison.Ordinal); - string result; - if (idx1 >= 0 && idx2 > idx1) { - try { - var xmlText = documentation.Substring (idx1, idx2 - idx1 + "</summary>".Length); - return ParseBody (member, - new XmlTextReader (xmlText, XmlNodeType.Element, null), - "summary", - DocumentationFormatOptions.Empty - ); - } catch (Exception e) { - LoggingService.LogWarning ("Malformed documentation xml detected:" + documentation, e); - // may happen on malformed xml. - var len = idx2 - idx1 - "</summary>".Length; - result = len > 0 ? documentation.Substring (idx1 + "<summary>".Length, len) : documentation; - } - } else if (idx1 >= 0) { - result = documentation.Substring (idx1 + "<summary>".Length); - } else if (idx2 >= 0) { - result = documentation.Substring (0, idx2 - 1); - } else { - result = documentation; - } - return GetDocumentationMarkup (member, CleanEmpty (result)); - } - - return GetDocumentationMarkup (member, CleanEmpty (documentation)); - } - - static string CleanEmpty (string doc) - { - return IsEmptyDocumentation (doc)? null : doc; - } - - static bool IsEmptyDocumentation (string documentation) - { - return string.IsNullOrWhiteSpace (documentation) || documentation.StartsWith ("To be added") || documentation == "we have not entered docs yet"; - } - - public static string GetDocumentation (IEntity member) - { - if (member == null) - return null; - if (member.Documentation != null) - return CleanEmpty (member.Documentation); - return null; - } - - static string GetCref (ITypeResolveContext ctx, string cref) - { - if (cref == null) - return ""; - - if (cref.Length < 2) - return cref; - try { - var entity = new DocumentationComment ("", ctx).ResolveCref (cref.Replace("<", "{").Replace(">", "}")); - - if (entity != null) { - var ambience = new ICSharpCode.NRefactory.CSharp.CSharpAmbience (); - ambience.ConversionFlags = ConversionFlags.ShowParameterList | ConversionFlags.ShowParameterNames | ConversionFlags.ShowTypeParameterList; - return ambience.ConvertSymbol (entity); - } - } catch (Exception e) { - LoggingService.LogWarning ("Invalid cref:" + cref, e); - } - - if (cref.Substring (1, 1) == ":") - return cref.Substring (2, cref.Length - 2); - - return cref; - } - - static bool IsSpecialChar (int charValue) - { - return - 0x01 <= charValue && charValue <= 0x08 || - 0x0B <= charValue && charValue <= 0x0C || - 0x0E <= charValue && charValue <= 0x1F || - 0x7F <= charValue && charValue <= 0x84 || - 0x86 <= charValue && charValue <= 0x9F; - } - - public static string BreakLines (string text, int maxLineLength) - { - if (maxLineLength <= 0) - return text; - StringBuilder result = new StringBuilder (); - int lineLength = 0; - bool inTag = false; - bool inAmp = false; - foreach (char ch in text) { - switch (ch) { - case '<': - inTag = true; - break; - case '>': - inTag = false; - break; - case '&': - inAmp = true; - break; - case ';': - inAmp = false; - break; - case '\n': - lineLength = 0; - break; - case '\r': - lineLength = 0; - break; - } - - result.Append (ch); - if (!inTag && !inAmp) - lineLength++; - if (!Char.IsLetterOrDigit (ch) && lineLength > maxLineLength) { - result.AppendLine (); - lineLength = 0; - } - } - return result.ToString (); - } - - public static string EscapeText (string text) - { - if (text == null) - return null; - StringBuilder result = new StringBuilder (); - foreach (char ch in text) { - switch (ch) { - case '<': - result.Append ("<"); - break; - case '>': - result.Append (">"); - break; - case '&': - result.Append ("&"); - break; - case '\'': - result.Append ("'"); - break; - case '"': - result.Append ("""); - break; - default: - int charValue = (int)ch; - if (IsSpecialChar (charValue)) { - result.AppendFormat ("&#x{0:X};", charValue); - } else { - result.Append (ch); - } - break; - } - } - return result.ToString (); - } - - public static string UnescapeText (string text) - { - var sb = new StringBuilder (); - for (int i = 0; i < text.Length; i++) { - char ch = text[i]; - if (ch == '&') { - int end = text.IndexOf (';', i); - if (end == -1) - break; - string entity = text.Substring (i + 1, end - i - 1); - switch (entity) { - case "lt": - sb.Append ('<'); - break; - case "gt": - sb.Append ('>'); - break; - case "amp": - sb.Append ('&'); - break; - case "apos": - sb.Append ('\''); - break; - case "quot": - sb.Append ('"'); - break; - } - i = end; - } else { - sb.Append (ch); - } - } - return sb.ToString (); - } - - - public static string GetDocumentationMarkup (IEntity member, string doc) - { - return GetDocumentationMarkup (member, doc, DocumentationFormatOptions.Empty); - } - - static string ParseBody (IEntity member, XmlTextReader xml, string endTagName, DocumentationFormatOptions options) - { - StringBuilder result = new StringBuilder (); - bool wasWhiteSpace = true; - bool appendSpace = false; - ITypeResolveContext ctx = member.Compilation.TypeResolveContext; - while (xml.Read ()) { - switch (xml.NodeType) { - case XmlNodeType.EndElement: - if (xml.Name == endTagName) - goto end; - break; - case XmlNodeType.Element: - switch (xml.Name.ToLower ()) { - case "para": - result.AppendLine (ParseBody (member, xml, xml.Name, options)); - wasWhiteSpace = true; - break; - case "see": - if (!wasWhiteSpace) { - result.Append (' '); - wasWhiteSpace = true; - } - result.Append ("<i>"); - string name = (GetCref (ctx, xml ["cref"]) + xml ["langword"]).Trim (); - if (options.Ambience != null) - name = options.Ambience.GetIntrinsicTypeName (name); - result.Append (EscapeText (name)); - result.Append ("</i>"); - wasWhiteSpace = false; - appendSpace = true; - break; - case "paramref": - if (!wasWhiteSpace) { - result.Append (' '); - wasWhiteSpace = true; - } - result.Append ("<i>"); - result.Append (EscapeText (xml ["name"].Trim ())); - result.Append ("</i>"); - appendSpace = true; - wasWhiteSpace = false; - break; - } - break; - case XmlNodeType.Text: - if (IsEmptyDocumentation (xml.Value)) - break; - foreach (char ch in xml.Value) { - if (!Char.IsWhiteSpace (ch) && appendSpace) { - result.Append (' '); - appendSpace = false; - } - if (Char.IsWhiteSpace (ch) || ch == '\n' || ch == '\r') { - if (!wasWhiteSpace) { - result.Append (' '); - wasWhiteSpace = true; - } - continue; - } - wasWhiteSpace = false; - result.Append (EscapeText (ch.ToString ())); - } - break; - } - } - end: - return result.ToString ().Trim (); - } - - public static string GetDocumentationMarkup (IEntity member, string doc, DocumentationFormatOptions options) - { - if (string.IsNullOrEmpty (doc)) - return null; - System.IO.StringReader reader = new System.IO.StringReader ("<docroot>" + doc + "</docroot>"); - XmlTextReader xml = new XmlTextReader (reader); - StringBuilder ret = new StringBuilder (70); - StringBuilder parameterBuilder = new StringBuilder (); - StringBuilder exceptions = new StringBuilder (); - exceptions.AppendLine (options.FormatHeading (GettextCatalog.GetString ("Exceptions:"))); - // ret.Append ("<small>"); - int paramCount = 0, exceptionCount = 0, summaryEnd = -1; - try { - xml.Read (); - do { - if (xml.NodeType == XmlNodeType.Element) { - switch (xml.Name.ToLower ()) { - case "para": - ret.Append (options.FormatBody (ParseBody (member, xml, xml.Name, options))); - if (summaryEnd < 0) - summaryEnd = ret.Length; - break; - case "summary": - var summary = options.FormatBody (ParseBody (member, xml, xml.Name, options)); - if (!IsEmptyDocumentation (summary)) { - // ret.AppendLine (GetHeading ("Summary:", options)); - ret.Append (summary); - if (summaryEnd < 0) - summaryEnd = ret.Length; - } - break; - case "remarks": - if (string.IsNullOrEmpty (options.HighlightParameter)) { - ret.AppendLine (options.FormatHeading (GettextCatalog.GetString ("Remarks:"))); - ret.Append (options.FormatBody (ParseBody (member, xml, xml.Name, options))); - if (summaryEnd < 0) - summaryEnd = ret.Length; - } else { - options.FormatBody (ParseBody (member, xml, xml.Name, options)); - } - break; - // skip <example>-nodes - case "example": - xml.Skip (); - xml.Skip (); - break; - case "exception": - exceptionCount++; - if (options.SmallText) - exceptions.Append ("<small>"); - exceptions.Append ("<b>"); - exceptions.Append (EscapeText (GetCref (member.Compilation.TypeResolveContext, xml ["cref"]))); - exceptions.Append (": "); - exceptions.Append ("</b>"); - if (options.SmallText) - exceptions.Append ("</small>"); - - exceptions.AppendLine (options.FormatBody (ParseBody (member, xml, xml.Name, options))); - break; - case "returns": - if (string.IsNullOrEmpty (options.HighlightParameter)) { - ret.AppendLine (options.FormatHeading (GettextCatalog.GetString ("Returns:"))); - ret.Append (options.FormatBody (ParseBody (member, xml, xml.Name, options))); - } else { - options.FormatBody (ParseBody (member, xml, xml.Name, options)); - } - break; - case "param": - string paramName = xml.GetAttribute ("name") != null ? xml ["name"].Trim () : ""; - - var body = options.FormatBody (ParseBody (member, xml, xml.Name, options)); - if (!IsEmptyDocumentation (body)) { - paramCount++; - parameterBuilder.Append ("<i>"); - if (options.HighlightParameter == paramName) - parameterBuilder.Append ("<b>"); - if (options.SmallText) - parameterBuilder.Append ("<small>"); - parameterBuilder.Append (EscapeText (paramName)); - if (options.SmallText) - parameterBuilder.Append ("</small>"); - if (options.HighlightParameter == paramName) - parameterBuilder.Append ("</b>"); - parameterBuilder.Append (":</i> "); - parameterBuilder.Append (body); - } else { - return null; - } - break; - case "value": - ret.AppendLine (options.FormatHeading (GettextCatalog.GetString ("Value:"))); - ret.AppendLine (options.FormatBody (ParseBody (member, xml, xml.Name, options))); - break; - case "seealso": - if (string.IsNullOrEmpty (options.HighlightParameter)) { - ret.Append (options.FormatHeading (GettextCatalog.GetString ("See also:"))); - ret.Append (" " + EscapeText (GetCref (member.Compilation.TypeResolveContext, xml ["cref"]) + xml ["langword"])); - } - break; - } - } - } while (xml.Read ()); - - } catch (Exception ex) { - MonoDevelop.Core.LoggingService.LogError (ex.ToString ()); - return EscapeText (doc); - } - - if (IsEmptyDocumentation (ret.ToString ()) && IsEmptyDocumentation (parameterBuilder.ToString ())) - return EscapeText (doc); - if (string.IsNullOrEmpty (options.HighlightParameter) && exceptionCount > 0) - ret.Append (exceptions.ToString ()); - - string result = ret.ToString (); - if (summaryEnd < 0) - summaryEnd = result.Length; - if (paramCount > 0) { - var paramSb = new StringBuilder (); - if (result.Length > 0) - paramSb.AppendLine ();/* - paramSb.Append ("<small>"); - paramSb.AppendLine (options.FormatHeading (GettextCatalog.GetPluralString ("Parameter:", "Parameters:", paramCount))); - paramSb.Append ("</small>");*/ - paramSb.Append (parameterBuilder.ToString ()); - result = result.Insert (summaryEnd, paramSb.ToString ()); - } - result = result.Trim (); - if (result.EndsWith (Environment.NewLine + "</small>")) - result = result.Substring (0, result.Length - (Environment.NewLine + "</small>").Length) + "</small>"; - return result; - } - #endregion - } -} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/IExtensibleTextEditor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/AmbienceTooltipProvider.cs index 94df5d0c11..4fd10c0a32 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/IExtensibleTextEditor.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/AmbienceTooltipProvider.cs @@ -1,9 +1,10 @@ -// IExtensibleTextEditor.cs +// +// AmbienceTooltipProvider.cs // // Author: -// Lluis Sanchez Gual <lluis@novell.com> +// Mike Krüger <mkrueger@xamarin.com> // -// Copyright (c) 2007 Novell, Inc (http://www.novell.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 @@ -22,22 +23,18 @@ // 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.CodeCompletion; +using Microsoft.CodeAnalysis; -namespace MonoDevelop.Ide.Gui.Content +namespace MonoDevelop.Ide.TypeSystem { - public interface IExtensibleTextEditor: IEditableTextBuffer + /// <summary> + /// Provides tooltips for a roslyn symbol. + /// Note: Intentionally part of internal extension API. + /// </summary> + abstract class AmbienceTooltipProvider { - ITextEditorExtension Extension { - get; - } - // The provided parameter is the first object of the extension chain - // This method should return the terminator ITextEditorExtension that - // will execute the default behavior (if any) - ITextEditorExtension AttachExtension (ITextEditorExtension extension); + public abstract TooltipInformation GetTooltip (ISymbol symbol); } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/CodeGenerationService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/CodeGenerationService.cs deleted file mode 100644 index 4c857af7d9..0000000000 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/CodeGenerationService.cs +++ /dev/null @@ -1,563 +0,0 @@ -// -// CodeGenerationService.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.IO; -using System.Linq; -using System.Text; -using Mono.TextEditor; -using MonoDevelop.Core; -using System.CodeDom; -using MonoDevelop.Projects; -using System.CodeDom.Compiler; -using ICSharpCode.NRefactory.TypeSystem; -using ICSharpCode.NRefactory; -using MonoDevelop.Ide.TypeSystem; -using MonoDevelop.Ide; -using ICSharpCode.NRefactory.CSharp.TypeSystem; -using System.Threading.Tasks; - -namespace MonoDevelop.Ide.TypeSystem -{ - public static class CodeGenerationService - { - public static IUnresolvedMember AddCodeDomMember (Project project, IUnresolvedTypeDefinition type, CodeTypeMember newMember) - { - bool isOpen; - var data = TextFileProvider.Instance.GetTextEditorData (type.Region.FileName, out isOpen); - var parsedDocument = TypeSystemService.ParseFile (data.Document.FileName, data.Document.MimeType, data.Text); - - var insertionPoints = GetInsertionPoints (data, parsedDocument, type); - - var suitableInsertionPoint = GetSuitableInsertionPoint (insertionPoints, type, newMember); - - var dotNetProject = project as DotNetProject; - if (dotNetProject == null) { - LoggingService.LogError ("Only .NET projects are supported."); - return null; - } - - var generator = dotNetProject.LanguageBinding.GetCodeDomProvider (); - StringWriter sw = new StringWriter (); - var options = new CodeGeneratorOptions (); - options.IndentString = data.GetLineIndent (type.Region.BeginLine) + "\t"; - if (newMember is CodeMemberMethod) - options.BracingStyle = "C"; - generator.GenerateCodeFromMember (newMember, sw, options); - - var code = sw.ToString (); - if (!string.IsNullOrEmpty (code)) - suitableInsertionPoint.Insert (data, code); - if (!isOpen) { - try { - File.WriteAllText (type.Region.FileName, data.Text); - } catch (Exception e) { - LoggingService.LogError (string.Format ("Failed to write file '{0}'.", type.Region.FileName), e); - MessageService.ShowError (GettextCatalog.GetString ("Failed to write file '{0}'.", type.Region.FileName)); - } - } - var newDocument = TypeSystemService.ParseFile (data.FileName, data.MimeType, data.Text); - return newDocument.ParsedFile.GetMember (suitableInsertionPoint.Location.Line, int.MaxValue); - } - - public static void AddNewMember (ITypeDefinition type, IUnresolvedTypeDefinition part, IUnresolvedMember newMember, bool implementExplicit = false) - { - bool isOpen; - var data = TextFileProvider.Instance.GetTextEditorData (part.Region.FileName, out isOpen); - var parsedDocument = TypeSystemService.ParseFile (data.FileName, data.MimeType, data.Text); - - var insertionPoints = GetInsertionPoints (data, parsedDocument, part); - - var suitableInsertionPoint = GetSuitableInsertionPoint (insertionPoints, part, newMember); - - var generator = CreateCodeGenerator (data, type.Compilation); - - generator.IndentLevel = CalculateBodyIndentLevel (parsedDocument.GetInnermostTypeDefinition (type.Region.Begin)); - var generatedCode = generator.CreateMemberImplementation (type, part, newMember, implementExplicit); - suitableInsertionPoint.Insert (data, generatedCode.Code); - if (!isOpen) { - try { - File.WriteAllText (type.Region.FileName, data.Text); - } catch (Exception e) { - LoggingService.LogError (GettextCatalog.GetString ("Failed to write file '{0}'.", type.Region.FileName), e); - MessageService.ShowError (GettextCatalog.GetString ("Failed to write file '{0}'.", type.Region.FileName)); - } - } - } - - public static Task<bool> InsertMemberWithCursor ( - string operation, ITypeDefinition parentType, IUnresolvedTypeDefinition part, - IUnresolvedMember newMember, bool implementExplicit = false) - { - var tcs = new TaskCompletionSource<bool>(); - if (parentType == null) - return tcs.Task; - part = part ?? parentType.Parts.FirstOrDefault (); - if (part == null) - return tcs.Task; - var loadedDocument = IdeApp.Workbench.OpenDocument (part.Region.FileName); - loadedDocument.RunWhenLoaded (delegate { - var editor = loadedDocument.Editor; - var loc = part.Region.Begin; - var parsedDocument = loadedDocument.UpdateParseDocument (); - var declaringType = parsedDocument.GetInnermostTypeDefinition (loc); - var mode = new InsertionCursorEditMode ( - editor.Parent, - CodeGenerationService.GetInsertionPoints (loadedDocument, declaringType)); - if (mode.InsertionPoints.Count == 0) { - MessageService.ShowError ( - GettextCatalog.GetString ("No valid insertion point can be found in type '{0}'.", declaringType.Name) - ); - return; - } - var suitableInsertionPoint = GetSuitableInsertionPoint (mode.InsertionPoints, part, newMember); - if (suitableInsertionPoint != null) - mode.CurIndex = mode.InsertionPoints.IndexOf (suitableInsertionPoint); - else - mode.CurIndex = 0; - - var helpWindow = new Mono.TextEditor.PopupWindow.InsertionCursorLayoutModeHelpWindow () { - TitleText = operation - }; - mode.HelpWindow = helpWindow; - - mode.StartMode (); - mode.Exited += delegate(object s, InsertionCursorEventArgs iCArgs) { - if (!iCArgs.Success) { - tcs.SetResult (false); - return; - } - var generator = CreateCodeGenerator (editor, parentType.Compilation); - generator.IndentLevel = CalculateBodyIndentLevel (declaringType); - var generatedCode = generator.CreateMemberImplementation (parentType, part, newMember, implementExplicit); - mode.InsertionPoints[mode.CurIndex].Insert (editor, generatedCode.Code); - tcs.SetResult (true); - }; - }); - - return tcs.Task; - } - - public static Task<bool> InsertMember ( - ITypeDefinition parentType, IUnresolvedTypeDefinition part, - IUnresolvedMember newMember, bool implementExplicit = false) - { - var tcs = new TaskCompletionSource<bool>(); - if (parentType == null) - return tcs.Task; - part = part ?? parentType.Parts.FirstOrDefault (); - if (part == null) - return tcs.Task; - - var loadedDocument = IdeApp.Workbench.OpenDocument (part.Region.FileName); - loadedDocument.RunWhenLoaded (delegate { - var editor = loadedDocument.Editor; - var loc = part.Region.Begin; - var parsedDocument = loadedDocument.UpdateParseDocument (); - var declaringType = parsedDocument.GetInnermostTypeDefinition (loc); - var insertionPoints = CodeGenerationService.GetInsertionPoints (loadedDocument, declaringType); - if (insertionPoints.Count == 0) { - MessageService.ShowError ( - GettextCatalog.GetString ("No valid insertion point can be found in type '{0}'.", declaringType.Name) - ); - return; - } - var suitableInsertionPoint = GetSuitableInsertionPoint (insertionPoints, part, newMember) ?? insertionPoints.First (); - - var generator = CreateCodeGenerator (editor, parentType.Compilation); - generator.IndentLevel = CalculateBodyIndentLevel (declaringType); - var generatedCode = generator.CreateMemberImplementation (parentType, part, newMember, implementExplicit); - suitableInsertionPoint.Insert (editor, generatedCode.Code); - }); - - return tcs.Task; - } - - public static int CalculateBodyIndentLevel (IUnresolvedTypeDefinition declaringType) - { - if (declaringType == null) - return 0; - int indentLevel = 1; - while (declaringType.DeclaringTypeDefinition != null) { - indentLevel++; - declaringType = declaringType.DeclaringTypeDefinition; - } - var file = declaringType.UnresolvedFile as CSharpUnresolvedFile; - if (file == null) - return indentLevel; - var scope = file.GetUsingScope (declaringType.Region.Begin); - while (scope != null && !string.IsNullOrEmpty (scope.NamespaceName)) { - indentLevel++; - // skip virtual scopes. - while (scope.Parent != null && scope.Parent.Region == scope.Region) - scope = scope.Parent; - scope = scope.Parent; - } - return indentLevel; - } - - public static void AddNewMembers (Project project, ITypeDefinition type, IUnresolvedTypeDefinition part, IEnumerable<IUnresolvedMember> newMembers, string regionName = null, Func<IUnresolvedMember, bool> implementExplicit = null) - { - IUnresolvedMember firstNewMember = newMembers.FirstOrDefault (); - if (firstNewMember == null) - return; - bool isOpen; - var data = TextFileProvider.Instance.GetTextEditorData (part.Region.FileName, out isOpen); - var parsedDocument = TypeSystemService.ParseFile (project, data); - - var insertionPoints = GetInsertionPoints (data, parsedDocument, part); - - - var suitableInsertionPoint = GetSuitableInsertionPoint (insertionPoints, part, firstNewMember); - - var generator = CreateCodeGenerator (data, type.Compilation); - generator.IndentLevel = CalculateBodyIndentLevel (parsedDocument.GetInnermostTypeDefinition (part.Region.Begin)); - StringBuilder sb = new StringBuilder (); - foreach (var newMember in newMembers) { - if (sb.Length > 0) { - sb.AppendLine (); - sb.AppendLine (); - } - sb.Append (generator.CreateMemberImplementation (type, part, newMember, implementExplicit != null ? implementExplicit (newMember) : false).Code); - } - suitableInsertionPoint.Insert (data, string.IsNullOrEmpty (regionName) ? sb.ToString () : generator.WrapInRegions (regionName, sb.ToString ())); - if (!isOpen) { - try { - File.WriteAllText (type.Region.FileName, data.Text); - } catch (Exception e) { - LoggingService.LogError (GettextCatalog.GetString ("Failed to write file '{0}'.", type.Region.FileName), e); - MessageService.ShowError (GettextCatalog.GetString ("Failed to write file '{0}'.", type.Region.FileName)); - } - } - } - - public static CodeGenerator CreateCodeGenerator (this Ide.Gui.Document doc) - { - return CodeGenerator.CreateGenerator (doc); - } - - public static CodeGenerator CreateCodeGenerator (this TextEditorData data, ICompilation compilation) - { - return CodeGenerator.CreateGenerator (data, compilation); - } - - static IUnresolvedTypeDefinition GetMainPart (IType t) - { - return t.GetDefinition ().Parts.First (); - } - - #region Insertion Points - public static List<InsertionPoint> GetInsertionPoints (MonoDevelop.Ide.Gui.Document document, IUnresolvedTypeDefinition type) - { - if (document == null) - throw new ArgumentNullException ("document"); - if (document.ParsedDocument == null) - return new List<InsertionPoint> (); - return GetInsertionPoints (document.Editor, document.ParsedDocument, type); - } - - public static List<InsertionPoint> GetInsertionPoints (TextEditorData data, ParsedDocument parsedDocument, IUnresolvedTypeDefinition type) - { - if (data == null) - throw new ArgumentNullException ("data"); - if (parsedDocument == null) - throw new ArgumentNullException ("parsedDocument"); - if (type == null) - throw new ArgumentNullException ("type"); - - // update type from parsed document, since this is always newer. - //type = parsedDocument.GetInnermostTypeDefinition (type.GetLocation ()) ?? type; - List<InsertionPoint> result = new List<InsertionPoint> (); - int offset = data.LocationToOffset (type.Region.Begin); - if (offset < 0 || type.BodyRegion.IsEmpty) - return result; - while (offset < data.Length && data.GetCharAt (offset) != '{') { - offset++; - } - var realStartLocation = data.OffsetToLocation (offset); - result.Add (GetInsertionPosition (data.Document, realStartLocation.Line, realStartLocation.Column)); - result [0].LineBefore = NewLineInsertion.None; - - foreach (var member in type.Members) { - TextLocation domLocation = member.BodyRegion.End; - if (domLocation.Line <= 0) { - DocumentLine lineSegment = data.GetLine (member.Region.BeginLine); - if (lineSegment == null) - continue; - domLocation = new TextLocation (member.Region.BeginLine, lineSegment.Length + 1); - } - result.Add (GetInsertionPosition (data.Document, domLocation.Line, domLocation.Column)); - } - - foreach (var nestedType in type.NestedTypes) { - TextLocation domLocation = nestedType.BodyRegion.End; - if (domLocation.Line <= 0) { - DocumentLine lineSegment = data.GetLine (nestedType.Region.BeginLine); - if (lineSegment == null) - continue; - domLocation = new TextLocation (nestedType.Region.BeginLine, lineSegment.Length + 1); - } - result.Add (GetInsertionPosition (data.Document, domLocation.Line, domLocation.Column)); - } - - result [result.Count - 1].LineAfter = NewLineInsertion.None; - CheckStartPoint (data.Document, result [0], result.Count == 1); - if (result.Count > 1) { - result.RemoveAt (result.Count - 1); - NewLineInsertion insertLine; - var lineBefore = data.GetLine (type.BodyRegion.EndLine - 1); - if (lineBefore != null && lineBefore.Length == lineBefore.GetIndentation (data.Document).Length) { - insertLine = NewLineInsertion.None; - } else { - insertLine = NewLineInsertion.Eol; - } - // search for line start - int col = type.BodyRegion.EndColumn - 1; - var line = data.GetLine (type.BodyRegion.EndLine); - if (line != null) { - var lineOffset = line.Offset; - col = Math.Min (line.Length, col); - while (lineOffset + col - 2 >= 0 && col > 1 && char.IsWhiteSpace (data.GetCharAt (lineOffset + col - 2))) - col--; - } - result.Add (new InsertionPoint (new DocumentLocation (type.BodyRegion.EndLine, col), insertLine, NewLineInsertion.Eol)); - CheckEndPoint (data.Document, result [result.Count - 1], result.Count == 1); - } - - foreach (var region in parsedDocument.UserRegions.Where (r => type.BodyRegion.IsInside (r.Region.Begin))) { - result.Add (new InsertionPoint (new DocumentLocation (region.Region.BeginLine + 1, 1), NewLineInsertion.Eol, NewLineInsertion.Eol)); - result.Add (new InsertionPoint (new DocumentLocation (region.Region.EndLine, 1), NewLineInsertion.Eol, NewLineInsertion.Eol)); - result.Add (new InsertionPoint (new DocumentLocation (region.Region.EndLine + 1, 1), NewLineInsertion.Eol, NewLineInsertion.Eol)); - } - result.Sort ((left, right) => left.Location.CompareTo (right.Location)); -// foreach (var res in result) -// Console.WriteLine (res); - return result; - } - - static void CheckEndPoint (TextDocument doc, InsertionPoint point, bool isStartPoint) - { - DocumentLine line = doc.GetLine (point.Location.Line); - if (line == null) - return; - - if (doc.GetLineIndent (line).Length + 1 < point.Location.Column) - point.LineBefore = NewLineInsertion.BlankLine; - if (point.Location.Column < line.Length + 1) - point.LineAfter = NewLineInsertion.Eol; - } - - static void CheckStartPoint (TextDocument doc, InsertionPoint point, bool isEndPoint) - { - DocumentLine line = doc.GetLine (point.Location.Line); - if (line == null) - return; - if (doc.GetLineIndent (line).Length + 1 == point.Location.Column) { - int lineNr = point.Location.Line; - while (lineNr > 1 && doc.GetLineIndent (lineNr - 1).Length == doc.GetLine (lineNr - 1).Length) { - lineNr--; - } - line = doc.GetLine (lineNr); - point.Location = new DocumentLocation (lineNr, doc.GetLineIndent (line).Length + 1); - } - - if (doc.GetLineIndent (line).Length + 1 < point.Location.Column) - point.LineBefore = NewLineInsertion.Eol; - if (point.Location.Column < line.Length + 1) - point.LineAfter = isEndPoint ? NewLineInsertion.Eol : NewLineInsertion.BlankLine; - } - - static InsertionPoint GetInsertionPosition (TextDocument doc, int line, int column) - { - int bodyEndOffset = doc.LocationToOffset (line, column) + 1; - DocumentLine curLine = doc.GetLine (line); - if (curLine != null) { - if (bodyEndOffset < curLine.Offset + curLine.Length) { - // case1: positition is somewhere inside the start line - return new InsertionPoint (new DocumentLocation (line, column + 1), NewLineInsertion.Eol, NewLineInsertion.BlankLine); - } - } - - // -> if position is at line end check next line - DocumentLine nextLine = doc.GetLine (line + 1); - if (nextLine == null) // check for 1 line case. - return new InsertionPoint (new DocumentLocation (line, column + 1), NewLineInsertion.BlankLine, NewLineInsertion.BlankLine); - - for (int i = nextLine.Offset; i < nextLine.EndOffset; i++) { - char ch = doc.GetCharAt (i); - if (!char.IsWhiteSpace (ch)) { - // case2: next line contains non ws chars. - return new InsertionPoint (new DocumentLocation (line + 1, 1), NewLineInsertion.Eol, NewLineInsertion.BlankLine); - } - } - - DocumentLine nextLine2 = doc.GetLine (line + 2); - if (nextLine2 != null) { - for (int i = nextLine2.Offset; i < nextLine2.EndOffset; i++) { - char ch = doc.GetCharAt (i); - if (!char.IsWhiteSpace (ch)) { - // case3: one blank line - return new InsertionPoint (new DocumentLocation (line + 1, 1), NewLineInsertion.Eol, NewLineInsertion.Eol); - } - } - } - // case4: more than 1 blank line - return new InsertionPoint (new DocumentLocation (line + 1, 1), NewLineInsertion.Eol, NewLineInsertion.None); - } - - static InsertionPoint GetSuitableInsertionPoint (IEnumerable<InsertionPoint> points, IUnresolvedTypeDefinition cls, IUnresolvedMember member) - { - var mainPart = cls; - switch (member.SymbolKind) { - case SymbolKind.Field: - return GetNewFieldPosition (points, mainPart); - case SymbolKind.Method: - case SymbolKind.Constructor: - case SymbolKind.Destructor: - case SymbolKind.Operator: - return GetNewMethodPosition (points, mainPart); - case SymbolKind.Event: - return GetNewEventPosition (points, mainPart); - case SymbolKind.Property: - return GetNewPropertyPosition (points, mainPart); - } - throw new InvalidOperationException ("Invalid member type: " + member.SymbolKind); - } - - static InsertionPoint GetSuitableInsertionPoint (IEnumerable<InsertionPoint> points, IUnresolvedTypeDefinition cls, CodeTypeMember mem) - { - var mainPart = cls; - if (mem is System.CodeDom.CodeMemberEvent) - return GetNewEventPosition (points, mainPart); - if (mem is System.CodeDom.CodeMemberProperty) - return GetNewPropertyPosition (points, mainPart); - if (mem is System.CodeDom.CodeMemberField) - return GetNewFieldPosition (points, mainPart); - if (mem is System.CodeDom.CodeMemberMethod) - return GetNewMethodPosition (points, mainPart); - return GetNewFieldPosition (points, mainPart); - } - - static InsertionPoint GetNewFieldPosition (IEnumerable<InsertionPoint> points, IUnresolvedTypeDefinition cls) - { - if (!cls.Fields.Any ()) - return points.FirstOrDefault (); - var lastField = cls.Fields.Last (); - return points.FirstOrDefault (p => p.Location.Convert () > lastField.Region.Begin); - } - - static InsertionPoint GetNewMethodPosition (IEnumerable<InsertionPoint> points, IUnresolvedTypeDefinition cls) - { - if (!cls.Methods.Any ()) - return GetNewPropertyPosition (points, cls); - var lastMember = cls.Members.Last (); - return points.FirstOrDefault (p => p.Location.Convert () > lastMember.Region.Begin); - } - - static InsertionPoint GetNewPropertyPosition (IEnumerable<InsertionPoint> points, IUnresolvedTypeDefinition cls) - { - if (!cls.Properties.Any ()) - return GetNewFieldPosition (points, cls); - var lastProperty = cls.Properties.Last (); - return points.FirstOrDefault (p => p.Location.Convert () > lastProperty.Region.Begin); - } - - static InsertionPoint GetNewEventPosition (IEnumerable<InsertionPoint> points, IUnresolvedTypeDefinition cls) - { - if (!cls.Events.Any ()) - return GetNewMethodPosition (points, cls); - var lastEvent = cls.Events.Last (); - return points.FirstOrDefault (p => p.Location.Convert () > lastEvent.Region.Begin); - } - #endregion - - public static void AddAttribute (ITypeDefinition cls, string name, params object[] parameters) - { - bool isOpen; - string fileName = cls.Region.FileName; - var buffer = TextFileProvider.Instance.GetTextEditorData (fileName, out isOpen); - - var attr = new CodeAttributeDeclaration (name); - foreach (var parameter in parameters) { - attr.Arguments.Add (new CodeAttributeArgument (new CodePrimitiveExpression (parameter))); - } - - var type = new CodeTypeDeclaration ("temp"); - type.CustomAttributes.Add (attr); - Project project; - if (!cls.TryGetSourceProject (out project)) { - LoggingService.LogError ("Error can't get source project for:" + cls.FullName); - } - - var provider = ((DotNetProject)project).LanguageBinding.GetCodeDomProvider (); - var sw = new StringWriter (); - provider.GenerateCodeFromType (type, sw, new CodeGeneratorOptions ()); - string code = sw.ToString (); - int start = code.IndexOf ('['); - int end = code.LastIndexOf (']'); - code = code.Substring (start, end - start + 1) + Environment.NewLine; - - int pos = buffer.LocationToOffset (cls.Region.BeginLine, cls.Region.BeginColumn); - - code = buffer.GetLineIndent (cls.Region.BeginLine) + code; - buffer.Insert (pos, code); - if (!isOpen) { - File.WriteAllText (fileName, buffer.Text); - buffer.Dispose (); - } - - } - - public static IUnresolvedTypeDefinition AddType (DotNetProject project, string folder, string namspace, CodeTypeDeclaration type) - { - - var unit = new CodeCompileUnit (); - var ns = new CodeNamespace (namspace); - ns.Types.Add (type); - unit.Namespaces.Add (ns); - - string fileName = project.LanguageBinding.GetFileName (Path.Combine (folder, type.Name)); - using (var sw = new StreamWriter (fileName)) { - var provider = project.LanguageBinding.GetCodeDomProvider (); - var options = new CodeGeneratorOptions (); - options.IndentString = "\t"; - options.BracingStyle = "C"; - - provider.GenerateCodeFromCompileUnit (unit, sw, options); - } - return TypeSystemService.ParseFile (project, fileName).TopLevelTypeDefinitions.FirstOrDefault (); - } - - } - - public static class HelperMethods - { - public static TextLocation Convert (this DocumentLocation location) - { - return new TextLocation (location.Line, location.Column); - } - } -} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/CodeGenerator.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/CodeGenerator.cs index 1b1d6e1310..760496caed 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/CodeGenerator.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/CodeGenerator.cs @@ -29,12 +29,12 @@ using System.Collections.Generic; using System.Linq; using Mono.Addins; using ICSharpCode.NRefactory.TypeSystem; -using Mono.TextEditor; using MonoDevelop.Core.AddIns; using MonoDevelop.Ide.TypeSystem; using ICSharpCode.NRefactory; using MonoDevelop.Projects.Policies; using MonoDevelop.Ide.Extensions; +using MonoDevelop.Ide.Editor; namespace MonoDevelop.Ide.TypeSystem { @@ -67,36 +67,39 @@ namespace MonoDevelop.Ide.TypeSystem set; } - public ICompilation Compilation { - get; - set; - } - - public static CodeGenerator CreateGenerator (Ide.Gui.Document doc) + public static CodeGenerator CreateGenerator (TextEditor editor, DocumentContext documentContext) { MimeTypeExtensionNode node; - if (!generators.TryGetValue (doc.Editor.MimeType, out node)) + if (!generators.TryGetValue (editor.MimeType, out node)) return null; var result = (CodeGenerator)node.CreateInstance (); - result.UseSpaceIndent = doc.Editor.TabsToSpaces; - result.EolMarker = doc.Editor.EolMarker; - result.TabSize = doc.Editor.Options.TabSize; - result.Compilation = doc.Compilation; + + result.UseSpaceIndent = editor.Options.TabsToSpaces; + result.EolMarker = editor.EolMarker; + result.TabSize = editor.Options.TabSize; + return result; } - - public static CodeGenerator CreateGenerator (TextEditorData editor, ICompilation compilation) + + public static CodeGenerator CreateGenerator (Ide.Gui.Document doc) + { + return CreateGenerator (doc.Editor, doc); + } + + public static CodeGenerator CreateGenerator (ITextDocument editor, ICompilation compilation) { MimeTypeExtensionNode node; if (!generators.TryGetValue (editor.MimeType, out node)) return null; var result = (CodeGenerator)node.CreateInstance (); - result.UseSpaceIndent = editor.TabsToSpaces; - result.EolMarker = editor.EolMarker; - result.TabSize = editor.Options.TabSize; - result.Compilation = compilation; + + //result.UseSpaceIndent = editor.Options.TabsToSpaces; + result.EolMarker = editor.GetEolMarker (); + //result.TabSize = editor.Options.TabSize; + //result.Compilation = compilation; + return result; } @@ -154,12 +157,6 @@ namespace MonoDevelop.Ide.TypeSystem generators.Remove (node.MimeType); } - protected void SetIndentTo (IUnresolvedTypeDefinition implementingType) - { - if (IndentLevel < 0) - IndentLevel = AutoIndent ? CodeGenerationService.CalculateBodyIndentLevel (implementingType) : 0; - } - static bool CompareMethods (IMethod interfaceMethod, IMethod typeMethod) { if (typeMethod.IsExplicitInterfaceImplementation) @@ -168,15 +165,26 @@ namespace MonoDevelop.Ide.TypeSystem } public abstract string WrapInRegions (string regionName, string text); - public abstract CodeGeneratorMemberResult CreateMemberImplementation (ITypeDefinition implementingType, IUnresolvedTypeDefinition part, IUnresolvedMember member, bool explicitDeclaration); - public abstract CodeGeneratorMemberResult CreateMemberImplementation (ITypeDefinition implementingType, IUnresolvedTypeDefinition part, IMember member, bool explicitDeclaration); - public abstract string CreateFieldEncapsulation (IUnresolvedTypeDefinition implementingType, IField field, string propertyName, Accessibility modifiers, bool readOnly); + public abstract void AddGlobalNamespaceImport (TextEditor editor, DocumentContext context, string nsName); + public abstract void AddLocalNamespaceImport (TextEditor editor, DocumentContext context, string nsName, TextLocation caretLocation); + + public void AddGlobalNamespaceImport (MonoDevelop.Ide.Gui.Document doc, string nsName) + { + if (doc == null) + throw new ArgumentNullException ("doc"); + AddGlobalNamespaceImport (doc.Editor, doc, nsName); + } + + public void AddLocalNamespaceImport (MonoDevelop.Ide.Gui.Document doc, string nsName, TextLocation caretLocation) + { + if (doc == null) + throw new ArgumentNullException ("doc"); + AddLocalNamespaceImport (doc.Editor, doc, nsName, caretLocation); + } - public abstract void AddGlobalNamespaceImport (MonoDevelop.Ide.Gui.Document doc, string nsName); - public abstract void AddLocalNamespaceImport (MonoDevelop.Ide.Gui.Document doc, string nsName, TextLocation caretLocation); - public abstract string GetShortTypeString (MonoDevelop.Ide.Gui.Document doc, IType type); + //public abstract string GetShortTypeString (TextEditor editor, DocumentContext context, IType type); public abstract void CompleteStatement (MonoDevelop.Ide.Gui.Document doc); } @@ -230,4 +238,4 @@ namespace MonoDevelop.Ide.TypeSystem } } } -}
\ No newline at end of file +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Comment.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Comment.cs index 6f615285ba..01eceed24a 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Comment.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Comment.cs @@ -26,7 +26,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; -using ICSharpCode.NRefactory.TypeSystem; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Core.Text; namespace MonoDevelop.Ide.TypeSystem { @@ -55,7 +56,13 @@ namespace MonoDevelop.Ide.TypeSystem set; } - public DomRegion Region { + public bool HasRegion { + get { + return !Region.IsEmpty; + } + } + + public DocumentRegion Region { get; set; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Error.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Error.cs new file mode 100644 index 0000000000..ca6a00634b --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Error.cs @@ -0,0 +1,216 @@ +// +// Error.cs +// +// Author: +// Mike Krüger <mkrueger@xamarin.com> +// +// Copyright (c) 2015 mkrueger +// +// 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.TypeSystem +{ + /// <summary> + /// Enum that describes the type of an error. + /// </summary> + public enum ErrorType + { + Unknown, + Error, + Warning + } + + /// <summary> + /// Descibes an error during parsing. + /// </summary> + [Serializable] + public class Error + { + readonly ErrorType errorType; + readonly string message; + readonly DocumentRegion region; + + /// <summary> + /// The type of the error. + /// </summary> + public ErrorType ErrorType { get { return errorType; } } + + /// <summary> + /// The error description. + /// </summary> + public string Message { get { return message; } } + + /// <summary> + /// The id of the error. + /// </summary> + public string Id { get; private set; } + + /// <summary> + /// The region of the error. + /// </summary> + public DocumentRegion Region { get { return region; } } + + /// <summary> + /// Gets or sets the tag. + /// </summary> + public object Tag { get; set; } + + /// <summary> + /// Initializes a new instance of the <see cref="ICSharpCode.NRefactory.TypeSystem.Error"/> class. + /// </summary> + /// <param name='errorType'> + /// The error type. + /// </param> + /// <param name='message'> + /// The description of the error. + /// </param> + /// <param name='region'> + /// The region of the error. + /// </param> + public Error (ErrorType errorType, string message, DocumentRegion region) + { + this.errorType = errorType; + this.message = message; + this.region = region; + } + + /// <summary> + /// Initializes a new instance of the <see cref="ICSharpCode.NRefactory.TypeSystem.Error"/> class. + /// </summary> + /// <param name='errorType'> + /// The error type. + /// </param> + /// <param name='message'> + /// The description of the error. + /// </param> + /// <param name='location'> + /// The location of the error. + /// </param> + public Error (ErrorType errorType, string message, DocumentLocation location) + { + this.errorType = errorType; + this.message = message; + this.region = new DocumentRegion (location, location); + } + + /// <summary> + /// Initializes a new instance of the <see cref="ICSharpCode.NRefactory.TypeSystem.Error"/> class. + /// </summary> + /// <param name='errorType'> + /// The error type. + /// </param> + /// <param name='message'> + /// The description of the error. + /// </param> + public Error (ErrorType errorType, string message, int line, int column) : this (errorType, message, new DocumentLocation (line, column)) + { + } + + /// <summary> + /// Initializes a new instance of the <see cref="ICSharpCode.NRefactory.TypeSystem.Error"/> class. + /// </summary> + /// <param name='errorType'> + /// The error type. + /// </param> + /// <param name='message'> + /// The description of the error. + /// </param> + public Error (ErrorType errorType, string message) + { + this.errorType = errorType; + this.message = message; + this.region = DocumentRegion.Empty; + } + + + /// <summary> + /// Initializes a new instance of the <see cref="ICSharpCode.NRefactory.TypeSystem.Error"/> class. + /// </summary> + /// <param name='errorType'> + /// The error type. + /// </param> + /// <param name='message'> + /// The description of the error. + /// </param> + /// <param name='region'> + /// The region of the error. + /// </param> + public Error (ErrorType errorType, string id, string message, DocumentRegion region) + { + this.errorType = errorType; + this.Id = id; + this.message = message; + this.region = region; + } + + /// <summary> + /// Initializes a new instance of the <see cref="ICSharpCode.NRefactory.TypeSystem.Error"/> class. + /// </summary> + /// <param name='errorType'> + /// The error type. + /// </param> + /// <param name='message'> + /// The description of the error. + /// </param> + /// <param name='location'> + /// The location of the error. + /// </param> + public Error (ErrorType errorType, string id, string message, DocumentLocation location) + { + this.errorType = errorType; + this.Id = id; + this.message = message; + this.region = new DocumentRegion (location, location); + } + + /// <summary> + /// Initializes a new instance of the <see cref="ICSharpCode.NRefactory.TypeSystem.Error"/> class. + /// </summary> + /// <param name='errorType'> + /// The error type. + /// </param> + /// <param name='message'> + /// The description of the error. + /// </param> + public Error (ErrorType errorType, string id, string message, int line, int column) : this (errorType, id, message, new DocumentLocation (line, column)) + { + } + + /// <summary> + /// Initializes a new instance of the <see cref="ICSharpCode.NRefactory.TypeSystem.Error"/> class. + /// </summary> + /// <param name='errorType'> + /// The error type. + /// </param> + /// <param name='message'> + /// The description of the error. + /// </param> + public Error (ErrorType errorType, string id, string message) + { + this.errorType = errorType; + this.Id = id; + this.message = message; + this.region = DocumentRegion.Empty; + } + } +} + diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/FoldingRegion.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/FoldingRegion.cs index 46f0ffcfea..91f5b99b93 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/FoldingRegion.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/FoldingRegion.cs @@ -24,7 +24,7 @@ // THE SOFTWARE. // using System; -using ICSharpCode.NRefactory.TypeSystem; +using MonoDevelop.Ide.Editor; namespace MonoDevelop.Ide.TypeSystem { @@ -38,37 +38,37 @@ namespace MonoDevelop.Ide.TypeSystem //NOTE: thsi is only respected if the FoldType is set to "Undefined" public bool IsFoldedByDefault { get; set; } - public DomRegion Region { get; set; } + public DocumentRegion Region { get; set; } public FoldType Type { get; set; } - public FoldingRegion (DomRegion region) : this (null, region) + public FoldingRegion (DocumentRegion region) : this (null, region) { } - public FoldingRegion (string name, DomRegion region) + public FoldingRegion (string name, DocumentRegion region) { this.Name = name ?? defaultName; this.Region = region; } - public FoldingRegion (string name, DomRegion region, bool isFoldedByDefault) : this (name, region) + public FoldingRegion (string name, DocumentRegion region, bool isFoldedByDefault) : this (name, region) { this.IsFoldedByDefault = isFoldedByDefault; } - public FoldingRegion (string name, DomRegion region, FoldType type) : this (name, region) + public FoldingRegion (string name, DocumentRegion region, FoldType type) : this (name, region) { this.Type = type; } - public FoldingRegion (string name, DomRegion region, FoldType type, bool isFoldedByDefault) : this (name, region) + public FoldingRegion (string name, DocumentRegion region, FoldType type, bool isFoldedByDefault) : this (name, region) { this.Type = type; this.IsFoldedByDefault = isFoldedByDefault; } - public FoldingRegion (DomRegion region, FoldType type) : this (null, region, type) + public FoldingRegion (DocumentRegion region, FoldType type) : this (null, region, type) { } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/IFoldingParser.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/IFoldingParser.cs new file mode 100644 index 0000000000..66ea540bed --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/IFoldingParser.cs @@ -0,0 +1,41 @@ +// +// IFoldingParser.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. + + +namespace MonoDevelop.Ide.TypeSystem +{ + /// <summary> + /// The folding parser is used for generating a preliminary parsed document that does not + /// contain a full dom - only some basic lexical constructs like comments or pre processor directives. + /// + /// This is useful for opening a document the first time to have some folding regions as start that are folded by default. + /// Otherwise an irritating screen update will occur. + /// </summary> + public interface IFoldingParser + { + ParsedDocument Parse (string fileName, string content); + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MetadataReferenceCache.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MetadataReferenceCache.cs new file mode 100644 index 0000000000..77d8e818eb --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MetadataReferenceCache.cs @@ -0,0 +1,166 @@ +// +// MetadataReferenceCache.cs +// +// Author: +// David Karlaš <david.karlas@xamarin.com> +// +// Copyright (c) 2015 Xamarin, Inc (http://www.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 Microsoft.CodeAnalysis; +using System.Collections.Generic; +using System.Threading.Tasks; +using System.IO; +using MonoDevelop.Core; +using System.Threading; + +namespace MonoDevelop.Ide.TypeSystem +{ + static class MetadataReferenceCache + { + static Dictionary<string, MetadataReferenceCacheItem> cache = new Dictionary<string, MetadataReferenceCacheItem> (); + + public static MetadataReference LoadReference (ProjectId projectId, string path) + { + lock (cache) { + MetadataReferenceCacheItem result; + if (!cache.TryGetValue (path, out result)) { + result = new MetadataReferenceCacheItem (path); + cache.Add (path, result); + } + result.InUseBy.Add (projectId); + return result.Reference; + } + } + + //TODO: This should be called when reference is actually removed and not on + //project reload because if this is only project that has this reference... Cache will be + //invalidated and when reload comes back in few miliseconds it will need to reload reference again + public static void RemoveReference (ProjectId projectId, string path) + { + lock (cache) { + MetadataReferenceCacheItem result; + if (cache.TryGetValue (path, out result)) { + result.InUseBy.Remove (projectId); + if (result.InUseBy.Count == 0) { + cache.Remove (path); + } + } + } + } + + public static void RemoveReferences (ProjectId id) + { + lock (cache) { + var toRemove = new List<string> (); + foreach (var val in cache) { + val.Value.InUseBy.Remove (id); + if (val.Value.InUseBy.Count == 0) { + toRemove.Add (val.Key); + } + } + toRemove.ForEach ((k) => cache.Remove (k)); + } + } + + static Timer timer; + + static MetadataReferenceCache () + { + timer = new Timer ((o) => CheckForChanges (), null, 10000, 10000); + } + + //TODO: Call this method when focus returns to MD or even better use FileSystemWatcher + public static void CheckForChanges () + { + lock (cache) { + foreach (var value in cache.Values) { + value.CheckForChange (); + } + } + } + + class MetadataReferenceCacheItem + { + public HashSet<ProjectId> InUseBy { get; private set; } + + public MetadataReference Reference { get; private set; } + + readonly string path; + + DateTime timeStamp; + + public MetadataReferenceCacheItem (string path) + { + this.path = path; + CreateNewReference (); + InUseBy = new HashSet<ProjectId> (); + } + + public void CheckForChange () + { + if (timeStamp != File.GetLastWriteTimeUtc (path)) { + if (Reference != null) { + foreach (var solution in IdeApp.Workspace.GetAllSolutions ()) { + var workspace = TypeSystemService.GetWorkspace (solution); + foreach (var projId in InUseBy) { + while (true) { + var project = workspace.CurrentSolution.GetProject (projId); + if (project == null) + break; + if (workspace.TryApplyChanges (project.RemoveMetadataReference (Reference).Solution)) + break; + } + } + } + } + CreateNewReference (); + if (Reference != null) { + foreach (var solution in IdeApp.Workspace.GetAllSolutions ()) { + var workspace = TypeSystemService.GetWorkspace (solution); + foreach (var projId in InUseBy) { + while (true) { + var project = workspace.CurrentSolution.GetProject (projId); + if (project == null) + break; + if (workspace.TryApplyChanges (project.AddMetadataReference (Reference).Solution)) + break; + } + } + } + } + } + } + + readonly static DateTime NonExistentFile = new DateTime (1601, 1, 1); + + void CreateNewReference () + { + timeStamp = File.GetLastWriteTimeUtc (path); + if (timeStamp == NonExistentFile) { + Reference = null; + } else { + Reference = MetadataReference.CreateFromFile (path, MetadataReferenceProperties.Assembly); + } + } + } + } +} + diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopProjectContent.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopProjectContent.cs deleted file mode 100644 index bc77935929..0000000000 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopProjectContent.cs +++ /dev/null @@ -1,109 +0,0 @@ -// -// MonoDevelopProjectContent.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 ICSharpCode.NRefactory.CSharp; -using ICSharpCode.NRefactory.TypeSystem; -using ICSharpCode.NRefactory.TypeSystem.Implementation; -using System.Collections.Generic; -using MonoDevelop.Projects; - -namespace MonoDevelop.Ide.TypeSystem -{ - [Serializable] - class MonoDevelopProjectContent : CSharpProjectContent - { - [NonSerialized] - Project project; - - public Project Project { - get { - return project; - } - internal set { - project = value; - } - } - - public MonoDevelopProjectContent (Project project) - { - this.project = project; - } - - MonoDevelopProjectContent (MonoDevelopProjectContent pc) : base (pc) - { - this.project = pc.project; - } - - public override ICompilation CreateCompilation() - { - var solutionSnapshot = new DefaultSolutionSnapshot(); - ICompilation compilation = new MonoDevelopCompilation(solutionSnapshot, this, AssemblyReferences); - solutionSnapshot.AddCompilation(this, compilation); - return compilation; - } - - protected override CSharpProjectContent Clone() - { - return new MonoDevelopProjectContent(this); - } - - public override ICompilation CreateCompilation(ISolutionSnapshot solutionSnapshot) - { - return new MonoDevelopCompilation(solutionSnapshot, this, AssemblyReferences); - } - } - - class MonoDevelopCompilation : SimpleCompilation - { - readonly MonoDevelopProjectContent content; - - public MonoDevelopCompilation (ISolutionSnapshot solutionSnapshot, MonoDevelopProjectContent content, IEnumerable<IAssemblyReference> assemblyReferences) : base (solutionSnapshot, content, assemblyReferences) - { - this.content = content; - } - - public override INamespace GetNamespaceForExternAlias (string alias) - { - var netProject = content.Project as DotNetProject; - if (netProject == null) - return null; - foreach (var r in netProject.References) { - if (r.Aliases == alias) { - foreach (var refAsm in ReferencedAssemblies) { - if (refAsm.FullAssemblyName == r.StoredReference) { - return refAsm.RootNamespace; - } - - } - } - } - - - return null; - } - } -} - diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopSourceTextContainer.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopSourceTextContainer.cs new file mode 100644 index 0000000000..fa3c9b7192 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopSourceTextContainer.cs @@ -0,0 +1,123 @@ +// +// RoslynTypeSystemService.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 Microsoft.CodeAnalysis; +using System.Linq; +using System.IO; +using MonoDevelop.Core; +using System.Collections.Generic; +using System.Threading; +using System.Reflection; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Core.Text; +using Microsoft.CodeAnalysis.Text; + +namespace MonoDevelop.Ide.TypeSystem +{ + class MonoDevelopSourceText : SourceText + { + readonly ITextSource doc; + + public override System.Text.Encoding Encoding { + get { + return doc.Encoding; + } + } + + public MonoDevelopSourceText (ITextSource doc) + { + if (doc == null) + throw new ArgumentNullException ("doc"); + this.doc = doc; + } + + #region implemented abstract members of SourceText + public override void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count) + { + while (count --> 0) { + destination[destinationIndex++] = doc.GetCharAt (sourceIndex++); + } + } + + public override int Length { + get { + return doc.Length; + } + } + + public override char this [int index] { + get { + return doc.GetCharAt (index); + } + } + #endregion + } + + class MonoDevelopSourceTextContainer : SourceTextContainer + { + readonly ITextDocument document; + public DocumentId Id { + get; + private set; + } + + public MonoDevelopSourceTextContainer (DocumentId documentId, ITextDocument document) : this (document) + { + this.Id = documentId; + } + + public MonoDevelopSourceTextContainer (ITextDocument document) + { + this.document = document; + this.document.TextChanging += HandleTextReplacing; + } + + ~MonoDevelopSourceTextContainer () + { + document.TextChanging -= HandleTextReplacing; + } + + void HandleTextReplacing (object sender, MonoDevelop.Core.Text.TextChangeEventArgs e) + { + var handler = TextChanged; + if (handler != null) { + var oldText = CurrentText; + var newText = oldText.Replace (e.Offset, e.RemovalLength, e.InsertedText.Text); + handler (this, new Microsoft.CodeAnalysis.Text.TextChangeEventArgs (oldText, newText, new TextChangeRange(TextSpan.FromBounds (e.Offset, e.Offset + e.RemovalLength), e.InsertionLength))); + } + + } + #region implemented abstract members of SourceTextContainer + public override SourceText CurrentText { + get { + return new MonoDevelopSourceText (document.CreateSnapshot ()); + } + } + + public override event EventHandler<Microsoft.CodeAnalysis.Text.TextChangeEventArgs> TextChanged; + #endregion + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopTextLoader.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopTextLoader.cs new file mode 100644 index 0000000000..d81a4d16a7 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopTextLoader.cs @@ -0,0 +1,69 @@ +// +// MonoDevelopTextLoader.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 Microsoft.CodeAnalysis; +using System.Threading.Tasks; +using System.Threading; +using Microsoft.CodeAnalysis.Text; +using MonoDevelop.Core.Text; + +namespace MonoDevelop.Ide.TypeSystem +{ + class MonoDevelopTextLoader : TextLoader + { + readonly string fileName; + + public MonoDevelopTextLoader (string fileName) + { + this.fileName = fileName; + } + + #region implemented abstract members of TextLoader + TextAndVersion GetTextAndVersion (Workspace workspace, DocumentId documentId) + { + SourceText text; + if (workspace.IsDocumentOpen (documentId)) { + text = SourceText.From (TextFileProvider.Instance.GetTextEditorData (fileName).Text); + } + else { + text = SourceText.From (MonoDevelop.Core.Text.TextFileUtility.GetText (fileName)); + } + return TextAndVersion.Create (text, VersionStamp.Create ()); + } + + public override Task<TextAndVersion> LoadTextAndVersionAsync (Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + { + return Task.FromResult (GetTextAndVersion (workspace, documentId)); + } + #endregion + + public static TextLoader CreateFromText (string text) + { + if (text == null) + throw new System.ArgumentNullException ("text"); + return TextLoader.From (TextAndVersion.Create (SourceText.From (text), VersionStamp.Create ())); + } + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs new file mode 100644 index 0000000000..e7ce3ef053 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs @@ -0,0 +1,848 @@ +// +// MonoDevelopWorkspace.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 Microsoft.CodeAnalysis; +using System.Linq; +using System.IO; +using MonoDevelop.Core; +using System.Collections.Generic; +using System.Threading; +using Microsoft.CodeAnalysis.Text; +using System.Threading.Tasks; +using MonoDevelop.Ide.Editor; +using Microsoft.CodeAnalysis.Host; +using MonoDevelop.Core.Text; +using System.Collections.Concurrent; +using MonoDevelop.Ide.CodeFormatting; +using MonoDevelop.Core.ProgressMonitoring; +using Gtk; + +namespace MonoDevelop.Ide.TypeSystem +{ + class MonoDevelopWorkspace : Workspace + { + readonly static HostServices services = Microsoft.CodeAnalysis.Host.Mef.MefHostServices.DefaultHost; + CancellationTokenSource src = new CancellationTokenSource (); + MonoDevelop.Projects.Solution currentMonoDevelopSolution; + object addLock = new object(); + bool added; + bool internalChanges; + + public MonoDevelop.Projects.Solution MonoDevelopSolution { + get { + return currentMonoDevelopSolution; + } + } + + public MonoDevelopWorkspace () : base (services, "MonoDevelopWorkspace") + { + if (IdeApp.Workspace != null) { + IdeApp.Workspace.ActiveConfigurationChanged += HandleActiveConfigurationChanged; + } + } + + protected override void Dispose (bool finalize) + { + base.Dispose (finalize); + if (IdeApp.Workspace != null) { + IdeApp.Workspace.ActiveConfigurationChanged -= HandleActiveConfigurationChanged; + } + + } + + internal void InformDocumentTextChange (DocumentId id, SourceText text) + { + base.ApplyDocumentTextChanged (id, text); + } + + void CancelLoad () + { + src.Cancel (); + src = new CancellationTokenSource (); + } + + static StatusBarIcon statusIcon = null; + static int workspacesLoading = 0; + + internal static event EventHandler LoadingFinished; + + static void OnLoadingFinished (EventArgs e) + { + var handler = LoadingFinished; + if (handler != null) + handler (null, e); + } + + internal void HideStatusIcon () + { + Gtk.Application.Invoke (delegate { + workspacesLoading--; + if (workspacesLoading == 0 && statusIcon != null) { + statusIcon.Dispose (); + statusIcon = null; + OnLoadingFinished (EventArgs.Empty); + } + }); + } + + internal void ShowStatusIcon () + { + Gtk.Application.Invoke (delegate { + workspacesLoading++; + if (statusIcon != null) + return; + statusIcon = IdeApp.Workbench?.StatusBar.ShowStatusIcon (ImageService.GetIcon ("md-parser")); + }); + } + + void HandleActiveConfigurationChanged (object sender, EventArgs e) + { + if (currentMonoDevelopSolution == null) + return; + ShowStatusIcon (); + CancelLoad (); + var token = src.Token; + Task.Run (() => { + try { + return CreateSolutionInfo (currentMonoDevelopSolution, token); + } catch (Exception ex) { + LoggingService.LogError ("Error while reloading solution.", ex); + return null; + } + }).ContinueWith ((Task<SolutionInfo> t) => { + try { + if (t.Status == TaskStatus.RanToCompletion) { + if (t.Result == null) + return; + OnSolutionReloaded (t.Result); + } + } finally { + HideStatusIcon (); + } + }); + } + + SolutionInfo CreateSolutionInfo (MonoDevelop.Projects.Solution solution, CancellationToken token) + { + var projects = new ConcurrentBag<ProjectInfo> (); + var mdProjects = solution.GetAllProjects (); + Parallel.ForEach (mdProjects, proj => { + if (token.IsCancellationRequested) + return; + projects.Add (LoadProject (proj, token)); + }); + if (token.IsCancellationRequested) + return null; + var solutionInfo = SolutionInfo.Create (GetSolutionId (solution), VersionStamp.Create (), solution.FileName, projects); + lock (addLock) { + if (!added) { + added = true; + OnSolutionAdded (solutionInfo); + } + } + return solutionInfo; + } + + public void TryLoadSolution (MonoDevelop.Projects.Solution solution/*, IProgressMonitor progressMonitor*/) + { + this.currentMonoDevelopSolution = solution; + CancelLoad (); + CreateSolutionInfo (solution, src.Token); + } + + public void UnloadSolution () + { + OnSolutionRemoved (); + } + + Dictionary<MonoDevelop.Projects.Solution, SolutionId> solutionIdMap = new Dictionary<MonoDevelop.Projects.Solution, SolutionId> (); + + internal SolutionId GetSolutionId (MonoDevelop.Projects.Solution solution) + { + if (solution == null) + throw new ArgumentNullException ("solution"); + lock (solutionIdMap) { + SolutionId result; + if (!solutionIdMap.TryGetValue (solution, out result)) { + result = SolutionId.CreateNewId (solution.Name); + solutionIdMap [solution] = result; + } + return result; + } + } + + ConcurrentDictionary<MonoDevelop.Projects.Project, ProjectId> projectIdMap = new ConcurrentDictionary<MonoDevelop.Projects.Project, ProjectId> (); + ConcurrentDictionary<ProjectId, ProjectData> projectDataMap = new ConcurrentDictionary<ProjectId, ProjectData> (); + + internal MonoDevelop.Projects.Project GetMonoProject (Project project) + { + return GetMonoProject (project.Id); + } + + internal MonoDevelop.Projects.Project GetMonoProject (ProjectId projectId) + { + foreach (var kv in projectIdMap) { + if (kv.Value == projectId) + return kv.Key; + } + return null; + } + + public bool Contains (ProjectId projectId) + { + return projectDataMap.ContainsKey (projectId); + } + + internal ProjectId GetProjectId (MonoDevelop.Projects.Project p) + { + lock (projectIdMap) { + ProjectId result; + if (projectIdMap.TryGetValue (p, out result)) + return result; + return null; + } + } + + internal ProjectId GetOrCreateProjectId (MonoDevelop.Projects.Project p) + { + lock (projectIdMap) { + ProjectId result; + if (!projectIdMap.TryGetValue (p, out result)) { + result = ProjectId.CreateNewId (p.Name); + projectIdMap [p] = result; + } + return result; + } + } + + ProjectData GetProjectData (ProjectId id) + { + lock (projectIdMap) { + ProjectData result; + if (projectDataMap.TryGetValue (id, out result)) { + return result; + } + return null; + } + } + + ProjectData GetOrCreateProjectData (ProjectId id) + { + lock (projectIdMap) { + ProjectData result; + if (!projectDataMap.TryGetValue (id, out result)) { + result = new ProjectData (id); + projectDataMap [id] = result; + } + return result; + } + } + + class ProjectData + { + readonly ProjectId projectId; + readonly Dictionary<string, DocumentId> documentIdMap; + + public ProjectInfo Info { + get; + set; + } + + public ProjectData (ProjectId projectId) + { + this.projectId = projectId; + documentIdMap = new Dictionary<string, DocumentId> (FilePath.PathComparer); + } + + internal DocumentId GetOrCreateDocumentId (string name) + { + lock (documentIdMap) { + DocumentId result; + if (!documentIdMap.TryGetValue (name, out result)) { + result = DocumentId.CreateNewId (projectId, name); + documentIdMap [name] = result; + } + return result; + } + } + + public DocumentId GetDocumentId (string name) + { + DocumentId result; + if (!documentIdMap.TryGetValue (name, out result)) + return null; + return result; + } + + internal void RemoveDocument (string name) + { + documentIdMap.Remove (name); + } + } + + internal DocumentId GetDocumentId (ProjectId projectId, string name) + { + var data = GetProjectData (projectId); + if (data == null) + return null; + return data.GetDocumentId (name); + } + + public override bool CanApplyChange (ApplyChangesKind feature) + { + return true; + } + + ProjectInfo LoadProject (MonoDevelop.Projects.Project p, CancellationToken token) + { + if (!projectIdMap.ContainsKey (p)) { + p.FileAddedToProject += OnFileAdded; + p.FileRemovedFromProject += OnFileRemoved; + p.FileRenamedInProject += OnFileRenamed; + p.Modified += OnProjectModified; + } + + var projectId = GetOrCreateProjectId (p); + var projectData = GetOrCreateProjectData (projectId); + var config = IdeApp.Workspace != null ? p.GetConfiguration (IdeApp.Workspace.ActiveConfiguration) as MonoDevelop.Projects.DotNetProjectConfiguration : null; + MonoDevelop.Projects.DotNetConfigurationParameters cp = null; + if (config != null) + cp = config.CompilationParameters as MonoDevelop.Projects.DotNetConfigurationParameters; + FilePath fileName = IdeApp.Workspace != null ? p.GetOutputFileName (IdeApp.Workspace.ActiveConfiguration) : new FilePath (p.Name + ".dll"); + var info = ProjectInfo.Create ( + projectId, + VersionStamp.Create (), + p.Name, + fileName.FileNameWithoutExtension, + LanguageNames.CSharp, + p.FileName, + fileName, + cp != null ? cp.CreateCompilationOptions () : null, + cp != null ? cp.CreateParseOptions () : null, + CreateDocuments (projectData, p, token), + CreateProjectReferences (p, token), + CreateMetadataReferences (p, projectId, token) + ); + + projectData.Info = info; + return info; + } + + internal static Func<string, TextLoader> CreateTextLoader = fileName => new MonoDevelopTextLoader (fileName); + + static DocumentInfo CreateDocumentInfo (string projectName, ProjectData id, MonoDevelop.Projects.ProjectFile f) + { + var sourceCodeKind = f.FilePath.Extension == ".sketchcs" ? SourceCodeKind.Interactive : SourceCodeKind.Regular; + return DocumentInfo.Create ( + id.GetOrCreateDocumentId (f.Name), + f.FilePath, + new [] { projectName }.Concat (f.ProjectVirtualPath.ParentDirectory.ToString ().Split (Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)), + sourceCodeKind, + CreateTextLoader (f.Name), + f.Name, + false + ); + } + + IEnumerable<DocumentInfo> CreateDocuments (ProjectData id, MonoDevelop.Projects.Project p, CancellationToken token) + { + var duplicates = new HashSet<DocumentId> (); + foreach (var f in p.Files) { + if (token.IsCancellationRequested) + yield break; + if (TypeSystemParserNode.IsCompileBuildAction (f.BuildAction)) { + if (!duplicates.Add (id.GetOrCreateDocumentId (f.Name))) + continue; + yield return CreateDocumentInfo (p.Name, id, f); + continue; + } + var mimeType = DesktopService.GetMimeTypeForUri (f.FilePath); + var node = TypeSystemService.GetTypeSystemParserNode (mimeType, f.BuildAction); + if (node == null || !node.Parser.CanGenerateProjection (mimeType, f.BuildAction, p.SupportedLanguages)) + continue; + if (!duplicates.Add (id.GetOrCreateDocumentId (f.Name))) + continue; + var options = new ParseOptions { + FileName = f.FilePath, + Project = p, + Content = StringTextSource.ReadFrom (f.FilePath), + }; + var projections = node.Parser.GenerateProjections (options); + foreach (var projection in projections.Result) { + yield return DocumentInfo.Create ( + id.GetOrCreateDocumentId (projection.Document.FileName), + projection.Document.FileName, + null, + SourceCodeKind.Regular, + TextLoader.From (TextAndVersion.Create (new MonoDevelopSourceText (projection.Document), VersionStamp.Create (), projection.Document.FileName)), + f.Name, + false + ); + } + } + } + + static IEnumerable<MetadataReference> CreateMetadataReferences (MonoDevelop.Projects.Project p, ProjectId projectId, CancellationToken token) + { + var netProject = p as MonoDevelop.Projects.DotNetProject; + if (netProject == null) + yield break; + var configurationSelector = IdeApp.Workspace?.ActiveConfiguration ?? MonoDevelop.Projects.ConfigurationSelector.Default; + var hashSet = new HashSet<string> (FilePath.PathComparer); + + bool addFacadeAssemblies = false; + + foreach (string file in netProject.GetReferencedAssemblies (configurationSelector, false)) { + if (token.IsCancellationRequested) + yield break; + string fileName; + if (!Path.IsPathRooted (file)) { + fileName = Path.Combine (Path.GetDirectoryName (netProject.FileName), file); + } else { + fileName = Path.GetFullPath (file); + } + if (hashSet.Contains (fileName)) + continue; + hashSet.Add (fileName); + yield return MetadataReferenceCache.LoadReference (projectId, fileName); + addFacadeAssemblies |= MonoDevelop.Core.Assemblies.SystemAssemblyService.ContainsReferenceToSystemRuntime (fileName); + } + + // HACK: Facade assemblies should be added by the project system. Remove that when the project system can do that. + if (addFacadeAssemblies) { + if (netProject != null) { + var runtime = netProject.TargetRuntime ?? MonoDevelop.Core.Runtime.SystemAssemblyService.DefaultRuntime; + var facades = runtime.FindFacadeAssembliesForPCL (netProject.TargetFramework); + foreach (var facade in facades) + yield return MetadataReferenceCache.LoadReference (projectId, facade); + } + } + + foreach (var pr in p.GetReferencedItems (configurationSelector)) { + if (token.IsCancellationRequested) + yield break; + var referencedProject = pr as MonoDevelop.Projects.DotNetProject; + if (referencedProject == null) + continue; + if (TypeSystemService.IsOutputTrackedProject (referencedProject)) { + var fileName = referencedProject.GetOutputFileName (configurationSelector); + yield return MetadataReferenceCache.LoadReference (projectId, fileName); + } + } + } + + IEnumerable<ProjectReference> CreateProjectReferences (MonoDevelop.Projects.Project p, CancellationToken token) + { + foreach (var pr in p.GetReferencedItems (MonoDevelop.Projects.ConfigurationSelector.Default)) { + if (token.IsCancellationRequested) + yield break; + var referencedProject = pr as MonoDevelop.Projects.DotNetProject; + if (referencedProject == null) + continue; + if (TypeSystemService.IsOutputTrackedProject (referencedProject)) + continue; + yield return new ProjectReference (GetOrCreateProjectId (referencedProject)); + } + } + + #region Open documents + public override bool CanOpenDocuments { + get { + return true; + } + } + + public override void OpenDocument (DocumentId documentId, bool activate = true) + { + var document = GetDocument (documentId); + if (document == null) + return; + MonoDevelop.Projects.Project prj = null; + foreach (var curPrj in IdeApp.Workspace.GetAllProjects ()) { + if (GetProjectId (curPrj) == documentId.ProjectId) { + prj = curPrj; + break; + } + } + IdeApp.Workbench.OpenDocument (new MonoDevelop.Ide.Gui.FileOpenInformation ( + DetermineFilePath(document.Id, document.Name, document.FilePath, document.Folders), + prj, + activate + )); + } + + List<MonoDevelopSourceTextContainer> openDocuments = new List<MonoDevelopSourceTextContainer>(); + internal void InformDocumentOpen (DocumentId documentId, ITextDocument editor) + { + var document = this.GetDocument (documentId); + if (document == null) { + return; + } + if (IsDocumentOpen (documentId)) + InformDocumentClose (documentId, document.FilePath); + var monoDevelopSourceTextContainer = new MonoDevelopSourceTextContainer (documentId, editor); + lock (openDocuments) { + openDocuments.Add (monoDevelopSourceTextContainer); + } + OnDocumentOpened (documentId, monoDevelopSourceTextContainer); + } + + Solution newSolution; + public override bool TryApplyChanges (Solution newSolution) + { + this.newSolution = newSolution; + return base.TryApplyChanges (newSolution); + } + + protected override void ApplyProjectChanges (ProjectChanges projectChanges) + { + try { + internalChanges = true; + base.ApplyProjectChanges (projectChanges); + var data = GetMonoProject (projectChanges.NewProject); + if (data != null) { + Application.Invoke (delegate { + data.Save (new NullProgressMonitor ()); + }); + } + } finally { + internalChanges = false; + } + } + + protected override void ApplyAdditionalDocumentAdded (DocumentInfo info, SourceText text) + { + base.ApplyAdditionalDocumentAdded (info, text); + } + + protected override void OnDocumentTextChanged (Document document) + { + base.OnDocumentTextChanged (document); + } + + protected override void OnDocumentClosing (DocumentId documentId) + { + base.OnDocumentClosing (documentId); + lock (openDocuments) { + var openDoc = openDocuments.FirstOrDefault (d => d.Id == documentId); + if (openDoc != null) { +// openDoc.TextChanged -= HandleTextChanged; + openDocuments.Remove (openDoc); + } + } + } + +// internal override bool CanChangeActiveContextDocument { +// get { +// return true; +// } +// } + + public void InformDocumentClose (DocumentId analysisDocument, string filePath) + { + try { + OnDocumentClosed (analysisDocument, new MonoDevelopTextLoader (filePath)); + } catch (Exception e) { + LoggingService.LogError ("Exception while closing document.", e); + } + } + + public override void CloseDocument (DocumentId documentId) + { + } + + protected override void ApplyDocumentTextChanged (DocumentId id, SourceText text) + { + var document = GetDocument (id); + + if (document == null) + return; + bool isOpen; + var data = TextFileProvider.Instance.GetTextEditorData (document.FilePath, out isOpen); + var changes = text.GetTextChanges (document.GetTextAsync ().Result).OrderByDescending (c => c.Span.Start).ToList (); + + int delta = 0; + foreach (var change in changes) { + data.ReplaceText (change.Span.Start, change.Span.Length, change.NewText); + delta += change.Span.Length - change.NewText.Length; + } + + if (!isOpen) { + var formatter = CodeFormatterService.GetFormatter (data.MimeType); + var mp = GetMonoProject (CurrentSolution.GetProject (id.ProjectId)); + string currentText = data.Text; + foreach (var change in changes) { + delta -= change.Span.Length - change.NewText.Length; + var startOffset = change.Span.Start - delta; + var str = formatter.FormatText (mp.Policies, currentText, startOffset, startOffset + change.NewText.Length); + data.ReplaceText (startOffset, change.NewText.Length, str); + } + data.Save (); + FileService.NotifyFileChanged (document.FilePath); + } else { + var formatter = CodeFormatterService.GetFormatter (data.MimeType); + var documentContext = IdeApp.Workbench.Documents.FirstOrDefault (d => FilePath.PathComparer.Compare (d.FileName, document.FilePath) == 0); + if (documentContext != null) { + foreach (var change in changes) { + delta -= change.Span.Length - change.NewText.Length; + var startOffset = change.Span.Start - delta; + formatter.OnTheFlyFormat ((TextEditor)data, documentContext, startOffset, startOffset + change.NewText.Length); + } + } + } + OnDocumentTextChanged (id, new MonoDevelopSourceText(data), PreservationMode.PreserveValue); + } + + protected override void ApplyDocumentAdded (DocumentInfo info, SourceText text) + { + var id = info.Id; + var path = DetermineFilePath (info.Id, info.Name, info.FilePath, info.Folders, true); + + MonoDevelop.Projects.Project mdProject = null; + + if (id.ProjectId != null) { + var project = CurrentSolution.GetProject (id.ProjectId); + mdProject = GetMonoProject (project); + if (mdProject == null) + LoggingService.LogWarning ("Couldn't find project for newly generated file {0} (Project {1}).", path, info.Id.ProjectId); + } + + string formattedText; + var formatter = CodeFormatterService.GetFormatter (DesktopService.GetMimeTypeForUri (path)); + if (formatter != null && mdProject != null) { + formattedText = formatter.FormatText (mdProject.Policies, text.ToString ()); + } else { + formattedText = text.ToString (); + } + + var textSource = new StringTextSource (formattedText, text.Encoding ?? System.Text.Encoding.UTF8); + try { + textSource.WriteTextTo (path); + } catch (Exception e) { + LoggingService.LogError ("Exception while saving file to " + path, e); + } + + if (mdProject != null) { + var file = new MonoDevelop.Projects.ProjectFile (path); + Application.Invoke (delegate { + mdProject.Files.Add (file); + IdeApp.ProjectOperations.Save (mdProject); + }); + } + + OnDocumentAdded (info.WithTextLoader (new MonoDevelopTextLoader (path))); + } + + string DetermineFilePath (DocumentId id, string name, string filePath, IReadOnlyList<string> docFolders, bool createDirectory = false) + { + var path = filePath; + + if (string.IsNullOrEmpty (path)) { + var monoProject = GetMonoProject (id.ProjectId); + + // If the first namespace name matches the name of the project, then we don't want to + // generate a folder for that. The project is implicitly a folder with that name. + IEnumerable<string> folders; + if (docFolders.FirstOrDefault () == monoProject.Name) { + folders = docFolders.Skip (1); + } else { + folders = docFolders; + } + + if (folders.Any ()) { + string baseDirectory = Path.Combine (monoProject.BaseDirectory, Path.Combine (folders.ToArray ())); + try { + if (createDirectory && !Directory.Exists (baseDirectory)) + Directory.CreateDirectory (baseDirectory); + } catch (Exception e) { + LoggingService.LogError ("Error while creating directory for a new file : " + baseDirectory, e); + } + path = Path.Combine (baseDirectory, name); + } + } + return path; + } + #endregion + + public Document GetDocument (DocumentId documentId, CancellationToken cancellationToken = default(CancellationToken)) + { + var project = CurrentSolution.GetProject (documentId.ProjectId); + if (project == null) + return null; + return project.GetDocument (documentId); + } + + public void UpdateFileContent (string fileName, string text) + { + SourceText newText = SourceText.From (text); + foreach (var project in this.projectDataMap.Keys) { + var docId = this.GetDocumentId (project, fileName); + if (docId == null) + continue; + base.OnDocumentTextChanged (docId, newText, PreservationMode.PreserveIdentity); + } + } + + public void AddProject (MonoDevelop.Projects.Project project) + { + var info = LoadProject (project, default(CancellationToken)); + OnProjectAdded (info); + } + + public void RemoveProject (MonoDevelop.Projects.Project project) + { + var id = GetProjectId (project); + if (id != null) { + foreach (var docId in GetOpenDocumentIds (id).ToList ()) { + ClearOpenDocument (docId); + } + OnProjectRemoved (id); + ProjectId val; + projectIdMap.TryRemove (project, out val); + ProjectData val2; + projectDataMap.TryRemove (id, out val2); + MetadataReferenceCache.RemoveReferences (id); + + project.FileAddedToProject -= OnFileAdded; + project.FileRemovedFromProject -= OnFileRemoved; + project.FileRenamedInProject -= OnFileRenamed; + project.Modified -= OnProjectModified; + } + } + + #region Project modification handlers + + void OnFileAdded (object sender, MonoDevelop.Projects.ProjectFileEventArgs args) + { + if (internalChanges) + return; + var project = (MonoDevelop.Projects.Project)sender; + foreach (MonoDevelop.Projects.ProjectFileEventInfo fargs in args) { + if (!TypeSystemParserNode.IsCompileBuildAction (fargs.ProjectFile.BuildAction)) + continue; + var projectId = GetProjectId (project); + var newDocument = CreateDocumentInfo (project.Name, GetProjectData (projectId), fargs.ProjectFile); + OnDocumentAdded (newDocument); + } + } + + void OnFileRemoved (object sender, MonoDevelop.Projects.ProjectFileEventArgs args) + { + if (internalChanges) + return; + var project = (MonoDevelop.Projects.Project)sender; + foreach (MonoDevelop.Projects.ProjectFileEventInfo fargs in args) { + var projectId = GetProjectId (project); + var data = GetProjectData (projectId); + var id = data.GetDocumentId (fargs.ProjectFile.FilePath); + if (id != null) { + ClearDocumentData (id); + OnDocumentRemoved (id); + data.RemoveDocument (fargs.ProjectFile.FilePath); + } + } + } + + void OnFileRenamed (object sender, MonoDevelop.Projects.ProjectFileRenamedEventArgs args) + { + if (internalChanges) + return; + var project = (MonoDevelop.Projects.Project)sender; + foreach (MonoDevelop.Projects.ProjectFileRenamedEventInfo fargs in args) { + if (!TypeSystemParserNode.IsCompileBuildAction (fargs.ProjectFile.BuildAction)) + continue; + + var projectId = GetProjectId (project); + var data = GetProjectData (projectId); + + var id = data.GetDocumentId (fargs.OldName); + if (id != null) { + if (this.IsDocumentOpen (id)) { + this.InformDocumentClose (id, fargs.OldName); + } + OnDocumentRemoved (id); + data.RemoveDocument (fargs.OldName); + } + + var newDocument = CreateDocumentInfo (project.Name, GetProjectData (projectId), fargs.ProjectFile); + OnDocumentAdded (newDocument); + } + } + + void OnProjectModified (object sender, MonoDevelop.Projects.SolutionItemModifiedEventArgs args) + { + if (internalChanges) + return; + if (!args.Any (x => x.Hint == "TargetFramework" || x.Hint == "References")) + return; + var project = (MonoDevelop.Projects.Project)sender; + var projectId = GetProjectId (project); + OnProjectReloaded (LoadProject (project, default(CancellationToken))); + } + + #endregion + + } + +// static class MonoDevelopWorkspaceFeatures +// { +// static FeaturePack pack; +// +// public static FeaturePack Features { +// get { +// if (pack == null) +// Interlocked.CompareExchange (ref pack, ComputePack (), null); +// return pack; +// } +// } +// +// static FeaturePack ComputePack () +// { +// var assemblies = new List<Assembly> (); +// var workspaceCoreAssembly = typeof(Workspace).Assembly; +// assemblies.Add (workspaceCoreAssembly); +// +// LoadAssembly (assemblies, "Microsoft.CodeAnalysis.CSharp.Workspaces"); +// //LoadAssembly (assemblies, "Microsoft.CodeAnalysis.VisualBasic.Workspaces"); +// +// var catalogs = assemblies.Select (a => new System.ComponentModel.Composition.Hosting.AssemblyCatalog (a)); +// +// return new MefExportPack (catalogs); +// } +// +// static void LoadAssembly (List<Assembly> assemblies, string assemblyName) +// { +// try { +// var loadedAssembly = Assembly.Load (assemblyName); +// assemblies.Add (loadedAssembly); +// } catch (Exception e) { +// LoggingService.LogWarning ("Couldn't load assembly:" + assemblyName, e); +// } +// } +// } + +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDocDocumentationProvider.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDocDocumentationProvider.cs index 67e8fd2bf2..9d98c1beb5 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDocDocumentationProvider.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDocDocumentationProvider.cs @@ -26,29 +26,18 @@ using System; using System.Collections.Generic; using System.Xml; -using ICSharpCode.NRefactory.TypeSystem; -using ICSharpCode.NRefactory.TypeSystem.Implementation; using MonoDevelop.Core; -using ICSharpCode.NRefactory.Documentation; using System.Text; +using Microsoft.CodeAnalysis; namespace MonoDevelop.Ide.TypeSystem { - [Serializable] - public class MonoDocDocumentationProvider : IDocumentationProvider + static class MonoDocDocumentationProvider { - [NonSerialized] - bool hadError; - - public MonoDocDocumentationProvider () - { - } + static bool hadError; + static Dictionary<string, string> commentCache = new Dictionary<string, string> (); - #region IDocumentationProvider implementation - [NonSerialized] - readonly Dictionary<string, DocumentationComment> commentCache = new Dictionary<string, DocumentationComment> (); - - public DocumentationComment GetDocumentation (IEntity entity) + public static string GetDocumentation (ISymbol entity) { if (entity == null) throw new System.ArgumentNullException ("entity"); @@ -57,8 +46,10 @@ namespace MonoDevelop.Ide.TypeSystem // shouldn't try it again. A corrupt .zip file could cause long tooltip delays otherwise. if (hadError) return null; - var idString = entity.GetIdString (); - DocumentationComment result; + var idString = entity.GetDocumentationCommentId (); + if (string.IsNullOrEmpty (idString)) + return null; + string result; if (commentCache.TryGetValue (idString, out result)) return result; XmlDocument doc = null; @@ -66,21 +57,20 @@ namespace MonoDevelop.Ide.TypeSystem var helpTree = MonoDevelop.Projects.HelpService.HelpTree; if (helpTree == null) return null; - if (entity.SymbolKind == SymbolKind.TypeDefinition) { + if (entity.Kind == SymbolKind.NamedType) { doc = helpTree.GetHelpXml (idString); } else { - var parentId = entity.DeclaringTypeDefinition.GetIdString (); - + var containingType = entity.ContainingType; + if (containingType == null) + return null; + var parentId = containingType.GetDocumentationCommentId (); doc = helpTree.GetHelpXml (parentId); if (doc == null) return null; XmlNode node = SelectNode (doc, entity); - if (node != null) - return commentCache [idString] = new DocumentationComment (node.OuterXml, new SimpleTypeResolveContext (entity)); + return commentCache [idString] = node.OuterXml; return null; -// var node = doc.SelectSingleNode ("/Type/Members/Member") -// return new DocumentationComment (doc.OuterXml, new SimpleTypeResolveContext (entity)); } } catch (Exception e) { hadError = true; @@ -90,33 +80,34 @@ namespace MonoDevelop.Ide.TypeSystem commentCache [idString] = null; return null; } - return commentCache [idString] = new DocumentationComment (doc.OuterXml, new SimpleTypeResolveContext (entity)); + return commentCache [idString] = doc.OuterXml; } - public XmlNode SelectNode (XmlDocument doc, IEntity entity) + internal static void ClearCommentCache () { - switch (entity.SymbolKind) { - case SymbolKind.None: - case SymbolKind.TypeDefinition: + commentCache = new Dictionary<string, string> (); + } + + static XmlNode SelectNode (XmlDocument doc, ISymbol entity) + { + switch (entity.Kind) { + case SymbolKind.NamedType: case SymbolKind.Field: case SymbolKind.Property: - case SymbolKind.Indexer: case SymbolKind.Event: return doc.SelectSingleNode ("/Type/Members/Member[@MemberName='" + entity.Name + "']"); case SymbolKind.Method: - case SymbolKind.Operator: - case SymbolKind.Destructor: - return SelectOverload (doc.SelectNodes ("/Type/Members/Member[@MemberName='" + entity.Name + "']"), (IParameterizedMember)entity); - case SymbolKind.Constructor: - return SelectOverload (doc.SelectNodes ("/Type/Members/Member[@MemberName='.ctor']"), (IParameterizedMember)entity); - + var method = (IMethodSymbol)entity; + if (method.MethodKind == MethodKind.Constructor) + return SelectOverload (doc.SelectNodes ("/Type/Members/Member[@MemberName='.ctor']"), method); + return SelectOverload (doc.SelectNodes ("/Type/Members/Member[@MemberName='" + entity.Name + "']"), method); default: throw new ArgumentOutOfRangeException (); } - } - public XmlNode SelectOverload (XmlNodeList nodes, IParameterizedMember entity) + + static XmlNode SelectOverload (XmlNodeList nodes, IMethodSymbol entity) { XmlNode node = null; if (nodes.Count == 1) { @@ -125,12 +116,12 @@ namespace MonoDevelop.Ide.TypeSystem var p = entity.Parameters; foreach (XmlNode curNode in nodes) { var paramList = curNode.SelectNodes ("Parameters/*"); - if (p.Count == 0 && paramList.Count == 0) + if (p.Length == 0 && paramList.Count == 0) return curNode; - if (p.Count != paramList.Count) + if (p.Length != paramList.Count) continue; bool matched = true; - for (int i = 0; i < p.Count; i++) { + for (int i = 0; i < p.Length; i++) { var idString = GetTypeString (p [i].Type); if (idString != paramList [i].Attributes ["Type"].Value) { matched = false; @@ -148,69 +139,20 @@ namespace MonoDevelop.Ide.TypeSystem } return null; } - - static string GetTypeString (IType t) + static string GetTypeString (ITypeSymbol t) { - if (t.Kind == TypeKind.Unknown) - return t.Name; - - if (t.Kind == TypeKind.TypeParameter) - return t.FullName; - - var typeWithElementType = t as TypeWithElementType; - if (typeWithElementType != null) { - var sb = new StringBuilder (); - - if (typeWithElementType is PointerType) { - sb.Append ("*"); - } - sb.Append (GetTypeString (typeWithElementType.ElementType)); - - if (typeWithElementType is ArrayType) { - sb.Append ("["); - sb.Append (new string (',', ((ArrayType)t).Dimensions - 1)); - sb.Append ("]"); - } - return sb.ToString (); - } - - ITypeDefinition typeDef = t.GetDefinition (); - if (typeDef == null) - return ""; - - var result = new StringBuilder (); - - result.Append (typeDef.Namespace + "."); - - if (typeDef.DeclaringTypeDefinition != null) { - string typeString = GetTypeString (typeDef.DeclaringTypeDefinition); - result.Append (typeString); - result.Append ("."); - } - - result.Append (typeDef.Name); - - if (typeDef.TypeParameterCount > 0) { - result.Append ("<"); - for (int i = 0; i < typeDef.TypeParameterCount; i++) { - if (i > 0) - result.Append (","); - if (t.TypeArguments.Count > 0) { - result.Append (GetTypeString (t.TypeArguments [i])); - } else { - result.Append (typeDef.TypeParameters [i].FullName); - } - } - result.Append (">"); + switch (t.TypeKind) { + case TypeKind.Array: + var arr = (IArrayTypeSymbol)t; + return GetTypeString (arr.ElementType) + "[" + new string (',', arr.Rank - 1) + "]"; + case TypeKind.Pointer: + var ptr = (IPointerTypeSymbol)t; + return "*" + GetTypeString (ptr.PointedAtType); + default: + var docComment = t.GetDocumentationCommentId (); + return docComment != null && docComment.Length > 2 ? docComment.Substring (2) : t.Name; } - - return result.ToString (); } - - #endregion - - } } - diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/NetAmbience.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/NetAmbience.cs deleted file mode 100755 index 28b35bc9bb..0000000000 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/NetAmbience.cs +++ /dev/null @@ -1,367 +0,0 @@ -// -// NetAmbience.cs -// -// Author: -// Mike Krüger <mkrueger@novell.com> -// -// Copyright (C) 2008 Novell, Inc (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Linq; -using System.Text; -using ICSharpCode.NRefactory.TypeSystem; -using System.Collections.Generic; - -namespace MonoDevelop.Ide.TypeSystem -{ - public class NetAmbience : Ambience - { - public NetAmbience () : base ("NET") - { - classTypes [TypeKind.Class] = "Class"; - classTypes [TypeKind.Enum] = "Enumeration"; - classTypes [TypeKind.Interface] = "Interface"; - classTypes [TypeKind.Struct] = "Structure"; - classTypes [TypeKind.Delegate] = "Delegate"; - } - - public override string SingleLineComment (string text) - { - return "// " + text; - } - - #region Type system output - public override string GetIntrinsicTypeName (string reflectionName) - { - return reflectionName; - } - - protected override string GetTypeReferenceString (IType reference, OutputSettings settings) - { - return reference.ToString (); - } - - protected override string GetTypeString (IType t, OutputSettings settings) - { - ITypeDefinition type = t.GetDefinition (); - var result = new StringBuilder (); - if (settings.IncludeModifiers) - AppendModifiers (result, settings, type); - if (settings.IncludeKeywords) - result.Append (settings.EmitKeyword (GetString (type.Kind))); - - result.Append (settings.EmitName (type, settings.UseFullName ? type.FullName : type.Name)); - - int parameterCount = type.TypeParameters.Count; - - if (settings.IncludeGenerics && parameterCount > 0) { - result.Append (settings.Markup ("<")); - if (!settings.HideGenericParameterNames) { - for (int i = 0; i < parameterCount; i++) { - if (i > 0) - result.Append (settings.Markup (", ")); - result.Append (type.TypeParameters [i].Name); - } - } - result.Append (settings.Markup (">")); - } - - if (settings.IncludeBaseTypes && type.DirectBaseTypes.Any ()) { - result.Append (settings.Markup (" : ")); - bool first = true; - foreach (var baseType in type.DirectBaseTypes) { - if (baseType.Equals (type.Compilation.FindType (KnownTypeCode.Object))) - continue; - if (!first) - result.Append (settings.Markup (", ")); - first = false; - result.Append (GetTypeReferenceString (baseType, settings)); - } - - } - return result.ToString (); - } - - protected override string GetMethodString (IMethod method, OutputSettings settings) - { - var result = new StringBuilder (); - - if (settings.IncludeModifiers) - AppendModifiers (result, settings, method); - - result.Append (settings.EmitKeyword ("Method")); - result.Append (settings.EmitName (method, settings.UseFullName ? method.FullName : method.Name)); - - if (settings.IncludeParameters) { - result.Append (settings.Markup ("(")); - bool first = true; - foreach (var parameter in method.Parameters) { - if (!first) - result.Append (settings.Markup (", ")); - result.Append (GetParameterString (method, parameter, settings)); - first = false; - } - result.Append (settings.Markup (")")); - } - - if (settings.IncludeReturnType) { - result.Append (settings.Markup (" : ")); - result.Append (GetTypeReferenceString (method.ReturnType, settings)); - } - return result.ToString (); - } - - protected override string GetConstructorString (IMethod method, OutputSettings settings) - { - var result = new StringBuilder (); - - if (settings.IncludeModifiers) - AppendModifiers (result, settings, method); - - result.Append (settings.EmitKeyword ("Constructor")); - result.Append (settings.EmitName (method, method.DeclaringType.Name)); - - if (settings.IncludeParameters) { - result.Append (settings.Markup ("(")); - bool first = true; - foreach (var parameter in method.Parameters) { - if (!first) - result.Append (settings.Markup (", ")); - result.Append (GetParameterString (method, parameter, settings)); - first = false; - } - result.Append (settings.Markup (")")); - } - return result.ToString (); - } - - protected override string GetDestructorString (IMethod method, OutputSettings settings) - { - var result = new StringBuilder (); - result.Append (settings.EmitKeyword ("Destructor")); - result.Append (settings.EmitName (method, method.DeclaringType.Name)); - return result.ToString (); - } - - protected override string GetOperatorString (IMethod method, OutputSettings settings) - { - var result = new StringBuilder (); - - if (settings.IncludeModifiers) - AppendModifiers (result, settings, method); - - result.Append (settings.EmitKeyword ("Operator")); - result.Append (settings.EmitName (method, settings.UseFullName ? method.FullName : method.Name)); - - if (settings.IncludeParameters) { - result.Append (settings.Markup ("(")); - bool first = true; - foreach (var parameter in method.Parameters) { - if (!first) - result.Append (settings.Markup (", ")); - result.Append (GetParameterString (method, parameter, settings)); - first = false; - } - result.Append (settings.Markup (")")); - } - - if (settings.IncludeReturnType) { - result.Append (settings.Markup (" : ")); - result.Append (GetTypeReferenceString (method.ReturnType, settings)); - } - return result.ToString (); - } - - protected override string GetFieldString (IField field, OutputSettings settings) - { - var result = new StringBuilder (); - - if (settings.IncludeModifiers) - AppendModifiers (result, settings, field); - - result.Append (settings.EmitKeyword ("Field")); - result.Append (settings.EmitName (field, field.Name)); - - if (settings.IncludeReturnType) { - result.Append (settings.Markup (" : ")); - result.Append (GetTypeReferenceString (field.ReturnType, settings)); - } - return result.ToString (); - } - - protected override string GetEventString (IEvent evt, OutputSettings settings) - { - var result = new StringBuilder (); - - if (settings.IncludeModifiers) - AppendModifiers (result, settings, evt); - - result.Append (settings.EmitKeyword ("Event")); - result.Append (settings.EmitName (evt, evt.Name)); - - if (settings.IncludeReturnType) { - result.Append (settings.Markup (" : ")); - result.Append (GetTypeReferenceString (evt.ReturnType, settings)); - } - return result.ToString (); - } - - protected override string GetPropertyString (IProperty property, OutputSettings settings) - { - var result = new StringBuilder (); - if (settings.IncludeModifiers) - AppendModifiers (result, settings, property); - result.Append (settings.EmitKeyword ("Property")); - result.Append (settings.EmitName (property, property.Name)); - if (settings.IncludeReturnType) { - result.Append (settings.Markup (" : ")); - result.Append (GetTypeReferenceString (property.ReturnType, settings)); - } - return result.ToString (); - } - - protected override string GetIndexerString (IProperty property, OutputSettings settings) - { - var result = new StringBuilder (); - if (settings.IncludeModifiers) - AppendModifiers (result, settings, property); - result.Append (settings.EmitKeyword ("Indexer")); - result.Append (settings.EmitName (property, property.Name)); - - if (settings.IncludeParameters && property.Parameters.Count > 0) { - result.Append (settings.Markup ("(")); - bool first = true; - foreach (var parameter in property.Parameters) { - if (!first) - result.Append (settings.Markup (", ")); - result.Append (GetParameterString (property, parameter, settings)); - first = false; - } - result.Append (settings.Markup (")")); - } - if (settings.IncludeReturnType) { - result.Append (settings.Markup (" : ")); - result.Append (GetTypeReferenceString (property.ReturnType, settings)); - } - return result.ToString (); - } - - protected override string GetParameterString (IParameterizedMember member, IParameter parameter, OutputSettings settings) - { - var result = new StringBuilder (); - if (settings.IncludeParameterName) { - result.Append (Format (parameter.Name)); - if (settings.IncludeReturnType) { - result.Append (settings.Markup (" : ")); - result.Append (GetTypeReferenceString (parameter.Type, settings)); - } - } else { - result.Append (GetTypeReferenceString (parameter.Type, settings)); - } - if (parameter.IsRef || parameter.IsOut) - result.Append (settings.Markup ("&")); - return result.ToString (); - } - #endregion - - void AppendModifiers (StringBuilder result, OutputSettings settings, IEntity entity) - { - if (entity.IsStatic) - result.Append (settings.EmitModifiers ("Static")); - if (entity.IsSealed) - result.Append (settings.EmitModifiers ("Sealed")); - if (entity.IsAbstract) - result.Append (settings.EmitModifiers ("Abstract")); - if (entity.IsShadowing) - result.Append (settings.EmitModifiers ("Shadows")); - if (entity.IsSynthetic) - result.Append (settings.EmitModifiers ("Synthetic")); - - switch (entity.Accessibility) { - case Accessibility.Internal: - result.Append (settings.EmitModifiers ("Internal")); - break; - case Accessibility.ProtectedAndInternal: - result.Append (settings.EmitModifiers ("Protected And Internal")); - break; - case Accessibility.ProtectedOrInternal: - result.Append (settings.EmitModifiers ("Protected Or Internal")); - break; - case Accessibility.Protected: - result.Append (settings.EmitModifiers ("Protected")); - break; - case Accessibility.Private: - result.Append (settings.EmitModifiers ("Private")); - break; - case Accessibility.Public: - result.Append (settings.EmitModifiers ("Public")); - break; - } - } - - public override string GetString (string nameSpace, OutputSettings settings) - { - var result = new StringBuilder (); - result.Append (settings.EmitKeyword ("Namespace")); - result.Append (Format (nameSpace)); - return result.ToString (); - } - - Dictionary<TypeKind, string> classTypes = new Dictionary<TypeKind, string> (); - - string GetString (TypeKind classType) - { - string res; - if (classTypes.TryGetValue (classType, out res)) - return res; - return string.Empty; - } -// public string Visit (IAttribute attribute, OutputSettings settings) -// { -// StringBuilder result = new StringBuilder (); -// result.Append (settings.Markup ("[")); -// result.Append (GetString (attribute.AttributeType, settings)); -// result.Append (settings.Markup ("(")); -// bool first = true; -// if (attribute.PositionalArguments != null) { -// foreach (object o in attribute.PositionalArguments) { -// if (!first) -// result.Append (settings.Markup (", ")); -// first = false; -// if (o is string) { -// result.Append (settings.Markup ("\"")); -// result.Append (o); -// result.Append (settings.Markup ("\"")); -// } else if (o is char) { -// result.Append (settings.Markup ("\"")); -// result.Append (o); -// result.Append (settings.Markup ("\"")); -// } else -// result.Append (o); -// } -// } -// result.Append (settings.Markup (")]")); -// return result.ToString (); -// } - } -} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/OutputFlags.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/OutputFlags.cs deleted file mode 100644 index 6ef2920d6d..0000000000 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/OutputFlags.cs +++ /dev/null @@ -1,65 +0,0 @@ -// -// OutputFlags.cs -// -// Author: -// Mike Krüger <mkrueger@novell.com> -// -// Copyright (C) 2008 Novell, Inc (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; - -namespace MonoDevelop.Ide.TypeSystem -{ - [Flags] - public enum OutputFlags { - None = 0, - - // Flags - UseFullName = 0x0001, - IncludeReturnType = 0x0002, - IncludeParameters = 0x0004, - IncludeParameterName = 0x0008, - IncludeMarkup = 0x0010, - IncludeKeywords = 0x0020, - IncludeModifiers = 0x0040, - IncludeBaseTypes = 0x0080, - IncludeGenerics = 0x0100, - UseIntrinsicTypeNames = 0x0200, - HighlightName = 0x0400, - HideExtensionsParameter = 0x0800, - HideGenericParameterNames= 0x1000, - HideArrayBrackets = 0x2000, - UseNETTypeNames = 0x4000, // print 'System.Int32' intead of 'int' - UseFullInnerTypeName = 0x8000, - ReformatDelegates = 0x10000, - GeneralizeGenerics = 0x20000, - StaticUsage = 0x40000, // to distinguish static usage for extension methods. - IncludeConstraints = 0x80000, - ReturnTypesLast = 0x100000, - CompletionListFomat = 0x200000, // print "Name : type" instead of "type : Name" - IncludeAccessor = 0x400000, // print {get;set;} after property name. - - ClassBrowserEntries = IncludeReturnType | IncludeParameters | IncludeGenerics, - AssemblyBrowserDescription = IncludeGenerics | IncludeBaseTypes | IncludeReturnType | IncludeParameters | IncludeParameterName | IncludeMarkup | IncludeKeywords | IncludeModifiers - } -} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/OutputSettings.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/OutputSettings.cs deleted file mode 100644 index 0071f2b4ac..0000000000 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/OutputSettings.cs +++ /dev/null @@ -1,268 +0,0 @@ -// OutputSettings.cs -// -// Author: -// Mike Krüger <mkrueger@novell.com> -// -// Copyright (c) 2008 Novell, Inc (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; -using MonoDevelop.Projects.Policies; -using System.Collections.Generic; -using ICSharpCode.NRefactory.TypeSystem; - -namespace MonoDevelop.Ide.TypeSystem -{ - public class OutputSettings - { - public OutputFlags OutputFlags { - get; - set; - } - - public PolicyContainer PolicyParent { - get; - set; - } - - public OutputSettings (OutputFlags outputFlags) - { - this.OutputFlags = outputFlags; - } - - public string Markup (string text) - { - if (MarkupCallback != null) - return MarkupCallback (text); - return IncludeMarkup ? PangoFormat (text) : text; - } - - public string EmitName (object domVisitable, string text) - { - if (EmitNameCallback != null) - return EmitNameCallback (domVisitable, text); - return text; - } - - public string EmitModifiers (string text) - { - if (!IncludeModifiers) - return string.Empty; - if (EmitModifiersCallback != null) - return EmitModifiersCallback (text) + " "; - if (IncludeMarkup) - return "<b>" + PangoFormat (text) + "</b> "; - return text + " "; - } - - public string EmitKeyword (string text) - { - if (EmitKeywordCallback != null) - return EmitKeywordCallback (text) + " "; - if (!IncludeKeywords) - return ""; - if (IncludeMarkup) - return "<b>" + PangoFormat (text) + "</b> "; - return text + " "; - } - - public string Highlight (string text) - { - if (HighlightCallback != null) - return HighlightCallback (text); - if (IncludeMarkup) - return "<b>" + PangoFormat (text) + "</b>"; - return text; - } - - public string PostProcess (object domVisitable, string outString) - { - if (PostProcessCallback != null) - return PostProcessCallback (domVisitable, outString); - return outString; - } - - static string PangoFormat (string input) - { - StringBuilder result = new StringBuilder (); - foreach (char ch in input) { - switch (ch) { - case '<': - result.Append ("<"); - break; - case '>': - result.Append (">"); - break; - case '&': - result.Append ("&"); - break; - default: - result.Append (ch); - break; - } - } - return result.ToString (); - } - - public bool IncludeMarkup { - get { - return (OutputFlags & OutputFlags.IncludeMarkup) == OutputFlags.IncludeMarkup; - } - } - - public bool IncludeKeywords { - get { - return (OutputFlags & OutputFlags.IncludeKeywords) == OutputFlags.IncludeKeywords; - } - } - - public bool IncludeModifiers { - get { - return (OutputFlags & OutputFlags.IncludeModifiers) == OutputFlags.IncludeModifiers; - } - } - - public bool UseFullName { - get { - return (OutputFlags & OutputFlags.UseFullName) == OutputFlags.UseFullName; - } - } - - public bool UseFullInnerTypeName { - get { - return (OutputFlags & OutputFlags.UseFullInnerTypeName) == OutputFlags.UseFullInnerTypeName; - } - } - - public bool IncludeParameters { - get { - return (OutputFlags & OutputFlags.IncludeParameters) == OutputFlags.IncludeParameters; - } - } - - public bool IncludeReturnType { - get { - return (OutputFlags & OutputFlags.IncludeReturnType) == OutputFlags.IncludeReturnType; - } - } - - public bool IncludeParameterName { - get { - return (OutputFlags & OutputFlags.IncludeParameterName) == OutputFlags.IncludeParameterName; - } - } - - public bool IncludeBaseTypes { - get { - return (OutputFlags & OutputFlags.IncludeBaseTypes) == OutputFlags.IncludeBaseTypes; - } - } - - public bool IncludeGenerics { - get { - return (OutputFlags & OutputFlags.IncludeGenerics) == OutputFlags.IncludeGenerics; - } - } - - public bool HideArrayBrackets { - get { - return (OutputFlags & OutputFlags.HideArrayBrackets) == OutputFlags.HideArrayBrackets; - } - } - - public bool HighlightName { - get { - return (OutputFlags & OutputFlags.HighlightName) == OutputFlags.HighlightName; - } - } - - public bool HideExtensionsParameter { - get { - return (OutputFlags & OutputFlags.HideExtensionsParameter) == OutputFlags.HideExtensionsParameter; - } - } - - public bool HideGenericParameterNames { - get { - return (OutputFlags & OutputFlags.HideGenericParameterNames) != 0; - } - } - - public bool GeneralizeGenerics { - get { - return (OutputFlags & OutputFlags.GeneralizeGenerics) != 0; - } - } - - public bool UseNETTypeNames { - get { - return (OutputFlags & OutputFlags.UseNETTypeNames) != 0; - } - } - - public bool ReformatDelegates { - get { - return (OutputFlags & OutputFlags.ReformatDelegates) != 0; - } - } - - public bool StaticUsage { - get { - return (OutputFlags & OutputFlags.StaticUsage) != 0; - } - } - - public bool IncludeConstraints { - get { - return (OutputFlags & OutputFlags.IncludeConstraints) != 0; - } - } - - public bool CompletionListFomat { - get { - return (OutputFlags & OutputFlags.CompletionListFomat) != 0; - } - } - - public bool ReturnTypesLast { - get { - return (OutputFlags & OutputFlags.ReturnTypesLast) != 0; - } - } - - public bool IncludeAccessor { - get { - return (OutputFlags & OutputFlags.IncludeAccessor) == OutputFlags.IncludeAccessor; - } - } - - public MarkupText EmitModifiersCallback; - public MarkupText EmitKeywordCallback; - public MarkupText MarkupCallback; - public MarkupText HighlightCallback; - public Func<object, string, string> EmitNameCallback; - - public delegate string MarkupText (string text); - - public Func<object, string, string> PostProcessCallback; - } -} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ParsedDocument.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ParsedDocument.cs index 0fdb4a13cd..dddd7641f3 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ParsedDocument.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ParsedDocument.cs @@ -29,10 +29,8 @@ using System.Collections.Generic; using System.Text; using System.Linq; using System.Threading; -using ICSharpCode.NRefactory; -using ICSharpCode.NRefactory.Semantics; -using Mono.TextEditor; - +using MonoDevelop.Ide.Editor; +using System.Threading.Tasks; namespace MonoDevelop.Ide.TypeSystem { @@ -43,7 +41,6 @@ namespace MonoDevelop.Ide.TypeSystem NonSerializable = 1 } - public abstract class ParsedDocument { DateTime lastWriteTimeUtc = DateTime.UtcNow; @@ -51,63 +48,8 @@ namespace MonoDevelop.Ide.TypeSystem get { return lastWriteTimeUtc; } set { lastWriteTimeUtc = value; } } - - [NonSerialized] - List<Comment> comments = new List<Comment> (); - - public virtual IUnresolvedFile ParsedFile { - get { return null; } - set { throw new InvalidOperationException (); } - } - public IList<Comment> Comments { - get { - return comments; - } - } - /// <summary> - /// Gets or sets a value indicating whether this instance is invalid and needs to be reparsed. - /// </summary> - public bool IsInvalid { - get; - set; - } - - List<Tag> tagComments = new List<Tag> (); - public IList<Tag> TagComments { - get { - return tagComments; - } - } - - IEnumerable<FoldingRegion> foldings = null; - public virtual IEnumerable<FoldingRegion> Foldings { - get { - return foldings ?? Enumerable.Empty<FoldingRegion> (); - } - } - - public IEnumerable<FoldingRegion> UserRegions { - get { - return Foldings.Where (f => f.Type == FoldType.UserRegion); - } - } - - List<PreProcessorDefine> defines = new List<PreProcessorDefine> (); - public IList<PreProcessorDefine> Defines { - get { - return defines; - } - } - - List<ConditionalRegion> conditionalRegions = new List<ConditionalRegion> (); - public IList<ConditionalRegion> ConditionalRegions { - get { - return conditionalRegions; - } - } - [NonSerialized] ParsedDocumentFlags flags; public ParsedDocumentFlags Flags { @@ -128,16 +70,30 @@ namespace MonoDevelop.Ide.TypeSystem fileName = value; } } - - public virtual IList<Error> Errors { - get { - return new Error[0]; - } + + /// <summary> + /// Gets or sets a value indicating whether this instance is invalid and needs to be reparsed. + /// </summary> + public bool IsInvalid { + get; + set; } - + + public abstract Task<IReadOnlyList<Comment>> GetCommentsAsync (CancellationToken cancellationToken = default(CancellationToken)); + public abstract Task<IReadOnlyList<Tag>> GetTagCommentsAsync (CancellationToken cancellationToken = default(CancellationToken)); + public abstract Task<IReadOnlyList<FoldingRegion>> GetFoldingsAsync (CancellationToken cancellationToken = default(CancellationToken)); + + public async Task<IEnumerable<FoldingRegion>> GetUserRegionsAsync (CancellationToken cancellationToken = default(CancellationToken)) + { + var foldings = await GetFoldingsAsync (cancellationToken).ConfigureAwait (false); + return foldings.Where (f => f.Type == FoldType.UserRegion); + } + + public abstract Task<IReadOnlyList<Error>> GetErrorsAsync (CancellationToken cancellationToken = default(CancellationToken)); + public bool HasErrors { get { - return Errors.Any (e => e.ErrorType == ErrorType.Error); + return GetErrorsAsync ().Result.Any (e => e.ErrorType == ErrorType.Error); } } @@ -162,236 +118,84 @@ namespace MonoDevelop.Ide.TypeSystem { this.fileName = fileName; } - - - public void Add (Comment comment) - { - comments.Add (comment); - } - - public void Add (Tag tagComment) - { - tagComments.Add (tagComment); - } - - public void Add (PreProcessorDefine define) - { - defines.Add (define); - } - - public void Add (ConditionalRegion region) + } + + public class DefaultParsedDocument : ParsedDocument + { + public DefaultParsedDocument (string fileName) : base (fileName) { - conditionalRegions.Add (region); + Flags |= ParsedDocumentFlags.NonSerializable; } - List<FoldingRegion> EnsureFoldingList () - { - if (this.foldings == null || !(foldings is List<FoldingRegion>)) - this.foldings = new List<FoldingRegion> (); - return (List<FoldingRegion>)foldings; - } - - public void Add (FoldingRegion region) + List<Comment> comments = new List<Comment> (); + + public void Add (Comment comment) { - EnsureFoldingList ().Add (region); + comments.Add (comment); } - - public void Add (IEnumerable<Comment> comments) + + public void AddRange (IEnumerable<Comment> comments) { this.comments.AddRange (comments); } - - public void Add (IEnumerable<Tag> tagComments) - { - this.tagComments.AddRange (tagComments); - } - - public void Add (IEnumerable<PreProcessorDefine> defines) - { - this.defines.AddRange (defines); - } - - public void Add (IEnumerable<FoldingRegion> folds) - { - if (foldings == null) { - this.foldings = folds; - return; - } - if (foldings != null && !(foldings is List<FoldingRegion>)) - EnsureFoldingList ().AddRange (foldings); - EnsureFoldingList ().AddRange (folds); - } - - public void Add (IEnumerable<ConditionalRegion> conditionalRegions) - { - this.conditionalRegions.AddRange (conditionalRegions); - } - - #region IUnresolvedFile delegation - public virtual IUnresolvedTypeDefinition GetTopLevelTypeDefinition (TextLocation location) + + public override Task<IReadOnlyList<Comment>> GetCommentsAsync (CancellationToken cancellationToken = default(CancellationToken)) { - return null; + return Task.FromResult<IReadOnlyList<Comment>> (comments); } - - public virtual IUnresolvedTypeDefinition GetInnermostTypeDefinition (TextLocation location) + + List<Tag> tagComments = new List<Tag> (); + + public void Add (Tag tagComment) { - return null; + tagComments.Add (tagComment); } - public virtual IUnresolvedMember GetMember (TextLocation location) + public void AddRange (IEnumerable<Tag> tagComments) { - return null; + this.tagComments.AddRange (tagComments); } - public virtual IList<IUnresolvedTypeDefinition> TopLevelTypeDefinitions { - get { - return new List<IUnresolvedTypeDefinition> (); - } + public override Task<IReadOnlyList<Tag>> GetTagCommentsAsync (CancellationToken cancellationToken = default(CancellationToken)) + { + return Task.FromResult<IReadOnlyList<Tag>> (tagComments); } - #endregion + List<FoldingRegion> foldingRegions = new List<FoldingRegion> (); - public Func<MonoDevelop.Ide.Gui.Document, CancellationToken, IRefactoringContext> CreateRefactoringContext; - public Func<TextEditorData, object, CancellationToken, IRefactoringContext> CreateRefactoringContextWithEditor; - } - - public class DefaultParsedDocument : ParsedDocument, IUnresolvedFile - { - - public override IUnresolvedFile ParsedFile { - get { return this; } - } - - List<Error> errors = new List<Error> (); - - public override IList<Error> Errors { - get { - return errors; - } - } - - public DefaultParsedDocument (string fileName) : base (fileName) + public void Add (FoldingRegion foldingRegion) { - Flags |= ParsedDocumentFlags.NonSerializable; - } - - #region IUnresolvedFile implementation - public override IUnresolvedTypeDefinition GetTopLevelTypeDefinition(TextLocation location) - { - return TopLevelTypeDefinitions.FirstOrDefault (t => t.Region.IsInside (location)); + foldingRegions.Add (foldingRegion); } - - public override IUnresolvedTypeDefinition GetInnermostTypeDefinition(TextLocation location) + + public void AddRange (IEnumerable<FoldingRegion> foldingRegions) { - IUnresolvedTypeDefinition parent = null; - var type = GetTopLevelTypeDefinition(location); - while (type != null) { - parent = type; - type = parent.NestedTypes.FirstOrDefault (t => t.Region.IsInside (location)); - } - return parent; + this.foldingRegions.AddRange (foldingRegions); } - - public override IUnresolvedMember GetMember(TextLocation location) + + public override Task<IReadOnlyList<FoldingRegion>> GetFoldingsAsync (CancellationToken cancellationToken = default(CancellationToken)) { - var type = GetInnermostTypeDefinition(location); - if (type == null) - return null; - return type.Members.FirstOrDefault (e => e.Region.IsInside(location)); - } - - List<IUnresolvedTypeDefinition> types = new List<IUnresolvedTypeDefinition> (); - public override IList<IUnresolvedTypeDefinition> TopLevelTypeDefinitions { - get { - return types; - } - } - - List<IUnresolvedAttribute> attributes = new List<IUnresolvedAttribute> (); - public IList<IUnresolvedAttribute> AssemblyAttributes { - get { - return attributes; - } + return Task.FromResult<IReadOnlyList<FoldingRegion>> (foldingRegions); } - public IList<IUnresolvedAttribute> ModuleAttributes { - get { - return new List<IUnresolvedAttribute> (); - } - } - #endregion - + List<Error> errors = new List<Error> (); + public void Add (Error error) { errors.Add (error); } - - public void Add (IEnumerable<Error> errors) - { - this.errors.AddRange (errors); - } - - #region IUnresolvedFile implementation - DateTime? IUnresolvedFile.LastWriteTime { - get { - return LastWriteTimeUtc; - } - set { - LastWriteTimeUtc = value.HasValue ? value.Value : DateTime.UtcNow; - } - } - #endregion - } - - [Serializable] - public class ParsedDocumentDecorator : ParsedDocument - { - IUnresolvedFile parsedFile; - - public override IUnresolvedFile ParsedFile { - get { return parsedFile; } - set { parsedFile = value; FileName = parsedFile.FileName; } - } - - public override IList<Error> Errors { - get { - return parsedFile.Errors; - } - } - - public ParsedDocumentDecorator (IUnresolvedFile parsedFile) : base (parsedFile.FileName) - { - this.parsedFile = parsedFile; - } - - public ParsedDocumentDecorator () : base ("") - { - } - - #region IUnresolvedFile implementation - public override IUnresolvedTypeDefinition GetTopLevelTypeDefinition (TextLocation location) - { - return parsedFile.GetTopLevelTypeDefinition (location); - } - public override IUnresolvedTypeDefinition GetInnermostTypeDefinition (TextLocation location) + public void AddRange (IEnumerable<Error> errors) { - return parsedFile.GetInnermostTypeDefinition (location); + this.errors.AddRange (errors); } - public override IUnresolvedMember GetMember (TextLocation location) + public override Task<IReadOnlyList<Error>> GetErrorsAsync (CancellationToken cancellationToken = default(CancellationToken)) { - return parsedFile.GetMember (location); - } - - public override IList<IUnresolvedTypeDefinition> TopLevelTypeDefinitions { - get { - return parsedFile.TopLevelTypeDefinitions; - } + return Task.FromResult<IReadOnlyList<Error>> (errors); } - #endregion } - + public static class FoldingUtilities { static bool IncompleteOrSingleLine (DomRegion region) @@ -399,7 +203,7 @@ namespace MonoDevelop.Ide.TypeSystem return region.BeginLine <= 0 || region.EndLine <= region.BeginLine; } - public static IEnumerable<FoldingRegion> ToFolds (this IList<Comment> comments) + public static IEnumerable<FoldingRegion> ToFolds (this IReadOnlyList<Comment> comments) { for (int i = 0; i < comments.Count; i++) { Comment comment = comments [i]; @@ -449,7 +253,7 @@ namespace MonoDevelop.Ide.TypeSystem curLine = curComment.Region.BeginLine; } - if (j - i > 1) { + if (j - i > 1 || (comment.IsDocumentation && comment.Region.BeginLine < comment.Region.EndLine)) { string txt; if (comment.IsDocumentation) { txt = "/// ..."; @@ -483,7 +287,7 @@ namespace MonoDevelop.Ide.TypeSystem } yield return new FoldingRegion (txt, - new DomRegion (comment.Region.Begin, end), + new DocumentRegion (comment.Region.Begin, end), FoldType.Comment); i = j - 1; } @@ -550,6 +354,23 @@ namespace MonoDevelop.Ide.TypeSystem } return false; } + + static bool IsInsideMember (this DocumentRegion region, IUnresolvedTypeDefinition cl) + { + if (region.IsEmpty || cl == null || !cl.BodyRegion.IsInside (region.Begin.Line, region.Begin.Column)) + return false; + foreach (var member in cl.Members) { + if (member.BodyRegion.IsEmpty) + continue; + if (member.BodyRegion.IsInside (region.Begin.Line, region.Begin.Column) && member.BodyRegion.IsInside (region.End.Line, region.End.Column)) + return true; + } + foreach (var inner in cl.NestedTypes) { + if (region.IsInsideMember (inner)) + return true; + } + return false; + } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/StockIcons.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/StockIcons.cs index bd7eea162b..d52111e65f 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/StockIcons.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/StockIcons.cs @@ -25,8 +25,8 @@ // THE SOFTWARE. using System; using MonoDevelop.Core; -using ICSharpCode.NRefactory.TypeSystem; using Mono.Cecil; +using ICSharpCode.NRefactory6.CSharp; namespace MonoDevelop.Ide.TypeSystem { @@ -122,149 +122,175 @@ namespace MonoDevelop.Ide.TypeSystem Stock.Event, Stock.PrivateEvent, Stock.Event, Stock.ProtectedEvent, Stock.InternalEvent, Stock.ProtectedOrInternalEvent, Stock.InternalAndProtectedEvent }; - public static string GetStockIcon (this INamedElement element) - { - if (element is IType) - return ((IType)element).GetStockIcon (); - if (element is ITypeParameter) - return ((ITypeParameter)element).GetStockIcon (); - if (element is IUnresolvedEntity) - return ((IUnresolvedEntity)element).GetStockIcon (); - return ((IEntity)element).GetStockIcon (); - } - - public static string GetStockIcon (this ITypeDefinition entity) - { - return GetStockIcon ((IType)entity); - } - - public static string GetStockIcon (this IType entity) +// public static IconId GetStockIcon (this INamedElement element) +// { +// if (element is IType) +// return ((IType)element).GetStockIcon (); +// if (element is ITypeParameter) +// return ((ITypeParameter)element).GetStockIcon (); +// if (element is IUnresolvedEntity) +// return ((IUnresolvedEntity)element).GetStockIcon (); +// return ((IEntity)element).GetStockIcon (); +// } +// +// public static IconId GetStockIcon (this ITypeDefinition entity) +// { +// return GetStockIcon ((IType)entity); +// } +// +// public static IconId GetStockIcon (this IType entity) +// { +// var def = entity.GetDefinition (); +// if (def == null) +// return Class; +// switch (def.Kind) { +// case TypeKind.Class: +// return typeIconTable [0, (int)def.Accessibility]; +// case TypeKind.Enum: +// return typeIconTable [1, (int)def.Accessibility]; +// case TypeKind.Interface: +// return typeIconTable [2, (int)def.Accessibility]; +// case TypeKind.Struct: +// return typeIconTable [3, (int)def.Accessibility]; +// case TypeKind.Delegate: +// return typeIconTable [4, (int)def.Accessibility]; +// default: +// return typeIconTable [0, (int)def.Accessibility]; +// } +// } + + static int GetIndex (Microsoft.CodeAnalysis.Accessibility accessibility) { - var def = entity.GetDefinition (); - if (def == null) - return Class; - switch (def.Kind) { - case TypeKind.Class: - return typeIconTable [0, (int)def.Accessibility]; - case TypeKind.Enum: - return typeIconTable [1, (int)def.Accessibility]; - case TypeKind.Interface: - return typeIconTable [2, (int)def.Accessibility]; - case TypeKind.Struct: - return typeIconTable [3, (int)def.Accessibility]; - case TypeKind.Delegate: - return typeIconTable [4, (int)def.Accessibility]; + switch (accessibility) { + case Microsoft.CodeAnalysis.Accessibility.NotApplicable: + return 0; + case Microsoft.CodeAnalysis.Accessibility.Private: + return 1; + case Microsoft.CodeAnalysis.Accessibility.ProtectedAndInternal: + return 5; + case Microsoft.CodeAnalysis.Accessibility.Protected: + return 3; + case Microsoft.CodeAnalysis.Accessibility.Internal: + return 4; + case Microsoft.CodeAnalysis.Accessibility.ProtectedOrInternal: + return 5; + case Microsoft.CodeAnalysis.Accessibility.Public: + return 2; default: - return typeIconTable [0, (int)def.Accessibility]; + throw new ArgumentOutOfRangeException (); } } - - public static string GetStockIcon (this IUnresolvedTypeDefinition def) + + public static IconId GetStockIcon (this Microsoft.CodeAnalysis.ISymbol symbol) { - switch (def.Kind) { - case TypeKind.Class: - return typeIconTable [0, (int)def.Accessibility]; - case TypeKind.Enum: - return typeIconTable [1, (int)def.Accessibility]; - case TypeKind.Interface: - return typeIconTable [2, (int)def.Accessibility]; - case TypeKind.Struct: - return typeIconTable [3, (int)def.Accessibility]; - case TypeKind.Delegate: - return typeIconTable [4, (int)def.Accessibility]; + switch (symbol.Kind) { + case Microsoft.CodeAnalysis.SymbolKind.Alias: + case Microsoft.CodeAnalysis.SymbolKind.ArrayType: + case Microsoft.CodeAnalysis.SymbolKind.Assembly: + case Microsoft.CodeAnalysis.SymbolKind.DynamicType: + case Microsoft.CodeAnalysis.SymbolKind.ErrorType: + case Microsoft.CodeAnalysis.SymbolKind.Label: + case Microsoft.CodeAnalysis.SymbolKind.Local: + case Microsoft.CodeAnalysis.SymbolKind.NetModule: + case Microsoft.CodeAnalysis.SymbolKind.PointerType: + return Field; + case Microsoft.CodeAnalysis.SymbolKind.NamedType: + var namedTypeSymbol = (Microsoft.CodeAnalysis.INamedTypeSymbol)symbol; + return typeIconTable [GetTypeIndex(namedTypeSymbol.TypeKind ), GetIndex (namedTypeSymbol.DeclaredAccessibility)]; + case Microsoft.CodeAnalysis.SymbolKind.Event: + var evtSymbol = (Microsoft.CodeAnalysis.IEventSymbol)symbol; + return eventIconTable [GetIndex (evtSymbol.DeclaredAccessibility)]; + case Microsoft.CodeAnalysis.SymbolKind.Field: + var fieldSymbol = (Microsoft.CodeAnalysis.IFieldSymbol)symbol; + return fieldIconTable [GetIndex (fieldSymbol.DeclaredAccessibility)]; + case Microsoft.CodeAnalysis.SymbolKind.Method: + var methodSymbol = (Microsoft.CodeAnalysis.IMethodSymbol)symbol; + return methodIconTable [GetIndex (methodSymbol.DeclaredAccessibility)]; + case Microsoft.CodeAnalysis.SymbolKind.Namespace: + return Namespace; + case Microsoft.CodeAnalysis.SymbolKind.Parameter: + return Field; + case Microsoft.CodeAnalysis.SymbolKind.Property: + var propertySymbol = (Microsoft.CodeAnalysis.IPropertySymbol)symbol; + return propertyIconTable [GetIndex (propertySymbol.DeclaredAccessibility)]; + case Microsoft.CodeAnalysis.SymbolKind.RangeVariable: + return Field; + case Microsoft.CodeAnalysis.SymbolKind.TypeParameter: + return Stock.typeIconTable [0, 0]; + case Microsoft.CodeAnalysis.SymbolKind.Preprocessing: + return Field; default: - return typeIconTable [0, (int)def.Accessibility]; + throw new ArgumentOutOfRangeException (); } } - - public static string GetStockIcon (this IField field) - { - return GetStockIcon ((IEntity)field); - } - - public static string GetStockIcon (this IVariable variable) - { - return Field; - } - - public static string GetStockIcon (this IParameter parameter) - { - return Field; - } - - public static string GetStockIcon (this IUnresolvedTypeParameter parameter) - { - return Field; - } - - public static string GetStockIcon (this IEntity entity, bool showAccessibility = true) + + static int GetTypeIndex (Microsoft.CodeAnalysis.TypeKind typeKind) { - switch (entity.SymbolKind) { - case SymbolKind.TypeDefinition: - return GetStockIcon ((IType)entity); - case SymbolKind.Field: - if (showAccessibility) - return fieldIconTable [(int)entity.Accessibility]; - else - return fieldIconTable [0]; - case SymbolKind.Method: - case SymbolKind.Constructor: - case SymbolKind.Destructor: - case SymbolKind.Operator: - if (showAccessibility) { - if (((IMethod)entity).IsExtensionMethod) - return extensionMethodIconTable [(int)entity.Accessibility]; - return methodIconTable [(int)entity.Accessibility]; - } else { - if (((IMethod)entity).IsExtensionMethod) - return extensionMethodIconTable [0]; - return methodIconTable [0]; - } - case SymbolKind.Property: - case SymbolKind.Indexer: - if (showAccessibility) - return propertyIconTable [(int)entity.Accessibility]; - else - return propertyIconTable [0]; - case SymbolKind.Event: - if (showAccessibility) - return eventIconTable [(int)entity.Accessibility]; - else - return eventIconTable [0]; + switch (typeKind) { + case Microsoft.CodeAnalysis.TypeKind.Unknown: + case Microsoft.CodeAnalysis.TypeKind.Array: + return 0; + case Microsoft.CodeAnalysis.TypeKind.Class: + return 0; + case Microsoft.CodeAnalysis.TypeKind.Delegate: + return 4; + case Microsoft.CodeAnalysis.TypeKind.Dynamic: + return 0; + case Microsoft.CodeAnalysis.TypeKind.Enum: + return 1; + case Microsoft.CodeAnalysis.TypeKind.Error: + return 0; + case Microsoft.CodeAnalysis.TypeKind.Interface: + return 2; + case Microsoft.CodeAnalysis.TypeKind.Module: + return 0; + case Microsoft.CodeAnalysis.TypeKind.Pointer: + return 0; + case Microsoft.CodeAnalysis.TypeKind.Struct: + return 3; + case Microsoft.CodeAnalysis.TypeKind.TypeParameter: + return 0; + case Microsoft.CodeAnalysis.TypeKind.Submission: + return 0; + default: + throw new ArgumentOutOfRangeException (); } - return ""; } - public static string GetStockIcon (this IUnresolvedEntity entity, bool showAccessibility = true) + + internal static IconId GetStockIconForSymbolInfo (this DeclaredSymbolInfo symbol) { - switch (entity.SymbolKind) { - case SymbolKind.TypeDefinition: - return GetStockIcon ((IUnresolvedTypeDefinition)entity); - case SymbolKind.Field: - if (showAccessibility) - return fieldIconTable [(int)entity.Accessibility]; - else - return fieldIconTable [0]; - case SymbolKind.Method: - case SymbolKind.Constructor: - case SymbolKind.Destructor: - case SymbolKind.Operator: - if (showAccessibility) - return methodIconTable [(int)entity.Accessibility]; - else - return methodIconTable [0]; - case SymbolKind.Property: - case SymbolKind.Indexer: - if (showAccessibility) - return propertyIconTable [(int)entity.Accessibility]; - else - return propertyIconTable [0]; - case SymbolKind.Event: - if (showAccessibility) - return eventIconTable [(int)entity.Accessibility]; - else - return eventIconTable [0]; + switch (symbol.Kind) { + case DeclaredSymbolInfoKind.Class: + return Stock.Class; + case DeclaredSymbolInfoKind.Constant: + return Stock.Field; + case DeclaredSymbolInfoKind.Constructor: + return Stock.Method; + case DeclaredSymbolInfoKind.Delegate: + return Stock.Delegate; + case DeclaredSymbolInfoKind.Enum: + return Stock.Enum; + case DeclaredSymbolInfoKind.EnumMember: + return Stock.Field; + case DeclaredSymbolInfoKind.Event: + return Stock.Event; + case DeclaredSymbolInfoKind.Field: + return Stock.Field; + case DeclaredSymbolInfoKind.Indexer: + return Stock.Method; + case DeclaredSymbolInfoKind.Interface: + return Stock.Interface; + case DeclaredSymbolInfoKind.Method: + return Stock.Method; + case DeclaredSymbolInfoKind.Module: + return Stock.Method; + case DeclaredSymbolInfoKind.Property: + return Stock.Property; + case DeclaredSymbolInfoKind.Struct: + return Stock.Struct; + default: + throw new ArgumentOutOfRangeException (); } - return ""; } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Tag.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Tag.cs index f45231144b..9f6924a2d0 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Tag.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Tag.cs @@ -51,7 +51,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; -using ICSharpCode.NRefactory.TypeSystem; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Core.Text; namespace MonoDevelop.Ide.TypeSystem { @@ -66,16 +67,16 @@ namespace MonoDevelop.Ide.TypeSystem } } - public Tag (string key, DomRegion region) + public Tag (string key, DocumentRegion region) { this.key = key; base.Region = region; } - - public Tag (string key, string comment, DomRegion region) : base (comment) + + public Tag (string key, string comment, DocumentRegion region) : base (comment) { this.key = key; base.Region = region; } } -} +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemParser.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemParser.cs index 7470e8c358..62ca819303 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemParser.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemParser.cs @@ -27,9 +27,53 @@ using System; using System.IO; using ICSharpCode.NRefactory.TypeSystem; using MonoDevelop.Projects; +using MonoDevelop.Core.Text; +using Microsoft.CodeAnalysis; +using System.Threading.Tasks; +using System.Threading; +using MonoDevelop.Ide.Editor.Projection; +using MonoDevelop.Ide.Editor; +using System.Collections.Generic; namespace MonoDevelop.Ide.TypeSystem { + public sealed class ParseOptions + { + public string FileName { get; set; } + + public ITextSource Content { get; set; } + + public MonoDevelop.Projects.Project Project { get; set; } + + public Document RoslynDocument { get; set; } + } + + [Flags] + public enum DisabledProjectionFeatures { + None = 0, + Completion = 1 << 0, + SemanticHighlighting = 1 << 1, + Tooltips = 1 << 2, + + All = Completion | SemanticHighlighting | Tooltips + } + + public class ParsedDocumentProjection + { + public ParsedDocument ParsedDocument { get; private set; } + + public IReadOnlyList<Projection> Projections { get; private set;} + + public DisabledProjectionFeatures DisabledProjectionFeatures { get; private set;} + + public ParsedDocumentProjection (ParsedDocument parsedDocument, IReadOnlyList<Projection> projections, DisabledProjectionFeatures disabledProjectionFeatures = DisabledProjectionFeatures.None) + { + this.ParsedDocument = parsedDocument; + this.Projections = projections; + this.DisabledProjectionFeatures = disabledProjectionFeatures; + } + } + /// <summary> /// A type system parser provides a ParsedDocument (which just adds some more information to a IUnresolvedFile) for /// a given file. This is required for adding information to the type system service to make the file contents available @@ -52,24 +96,51 @@ namespace MonoDevelop.Ide.TypeSystem /// <param name='project'> /// The project the file belongs to. /// </param> - public abstract ParsedDocument Parse (bool storeAst, string fileName, TextReader content, Project project = null); + public abstract Task<ParsedDocument> Parse (ParseOptions options, CancellationToken cancellationToken = default(CancellationToken)); /// <summary> - /// Parse the specified file. The file content should be read by the type system parser. + /// If true projections are possible. A projection transforms a source to a target language and maps certain parts of the file to parts in the projected file. + /// That's used for embedded languages for example. /// </summary> - /// <param name='storeAst'> - /// If set to <c>true</c> the ast should be stored in the parsed document. - /// </param> - /// <param name='fileName'> - /// The name of the file. - /// </param> - /// <param name='project'> - /// The project the file belongs to. - /// </param> - public virtual ParsedDocument Parse (bool storeAst, string fileName, Project project = null) + /// <returns><c>true</c> if this instance can generate projection the specified mimeType buildAction supportedLanguages; + /// otherwise, <c>false</c>.</returns> + /// <param name="mimeType">MIME type.</param> + /// <param name="buildAction">Build action.</param> + /// <param name="supportedLanguages">Supported languages.</param> + public virtual bool CanGenerateProjection (string mimeType, string buildAction, string[] supportedLanguages) + { + return false; + } + + /// <summary> + /// Generates the plain projection. This is used for type system services. + /// </summary> + /// <returns>The projection.</returns> + /// <param name="options">Options.</param> + /// <param name="cancellationToken">Cancellation token.</param> + public virtual Task<IReadOnlyList<Projection>> GenerateProjections (ParseOptions options, CancellationToken cancellationToken = default(CancellationToken)) + { + throw new NotSupportedException (); + } + + /// <summary> + /// Generates the parsed document projection. That contains the parsed document and the projection. This is used inside the IDE for the editor. + /// That's usually more efficient than calling Parse/GenerateProjection separately. + /// </summary> + /// <returns>The parsed document projection.</returns> + /// <param name="options">Options.</param> + /// <param name="cancellationToken">Cancellation token.</param> + public virtual Task<ParsedDocumentProjection> GenerateParsedDocumentProjection (ParseOptions options, CancellationToken cancellationToken = default(CancellationToken)) + { + throw new NotSupportedException (); + } + + /// <summary> + /// Gets an up to date partial projection used by code completion. + /// </summary> + public virtual Task<IReadOnlyList<Projection>> GetPartialProjectionsAsync (DocumentContext ctx, TextEditor editor, ParsedDocument currentParsedDocument, CancellationToken cancellationToken = default(CancellationToken)) { - using (var stream = Mono.TextEditor.Utils.TextFileUtility.OpenStream (fileName)) - return Parse (storeAst, fileName, stream, project); + throw new NotSupportedException (); } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemParserNode.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemParserNode.cs index 1cb27e418a..1a58e50598 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemParserNode.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemParserNode.cs @@ -113,6 +113,4 @@ namespace MonoDevelop.Ide.TypeSystem } } } - } - diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService.cs index e18c2c603c..8a5bf71552 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService.cs @@ -28,138 +28,24 @@ using System.Collections.Generic; using System.Linq; using System.IO; using MonoDevelop.Projects; -using ICSharpCode.NRefactory.TypeSystem.Implementation; -using ICSharpCode.NRefactory.TypeSystem; using Mono.Addins; using MonoDevelop.Core; using MonoDevelop.Ide; -using Mono.TextEditor; using System.Threading; -using MonoDevelop.Core.ProgressMonitoring; using System.Xml; using ICSharpCode.NRefactory.Utils; -using ICSharpCode.NRefactory; using System.Threading.Tasks; -using ICSharpCode.NRefactory.Documentation; -using ICSharpCode.NRefactory.CSharp; using MonoDevelop.Ide.Extensions; using MonoDevelop.Core.Assemblies; using System.Text; -using ICSharpCode.NRefactory.Completion; -using System.Diagnostics; -using MonoDevelop.Projects.SharedAssetsProjects; -using Mono.CSharp.Nullable; -using MonoDevelop.Ide.Templates; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Core.Text; +using Microsoft.CodeAnalysis.Text; +using Mono.Posix; namespace MonoDevelop.Ide.TypeSystem { - public static class TypeSystemServiceExt - { - /// <summary> - /// Tries to the get source project for a given type definition. This operation may fall if it was called on an outdated - /// compilation unit or the correspondening project was unloaded. - /// </summary> - /// <returns><c>true</c>, if get source project was found, <c>false</c> otherwise.</returns> - /// <param name="type">The type definition.</param> - /// <param name="project">The project or null if it wasn't found.</param> - public static bool TryGetSourceProject (this ITypeDefinition type, out Project project) - { - var location = type.Compilation.MainAssembly.UnresolvedAssembly.Location; - if (string.IsNullOrEmpty (location)) { - project = null; - return false; - } - project = TypeSystemService.GetProject (location); - return project != null; - } - - /// <summary> - /// Tries to the get source project for a given type. This operation may fall if it was called on an outdated - /// compilation unit or the correspondening project was unloaded. - /// </summary> - /// <returns><c>true</c>, if get source project was found, <c>false</c> otherwise.</returns> - /// <param name="type">The type.</param> - /// <param name="project">The project or null if it wasn't found.</param> - public static bool TryGetSourceProject (this IType type, out Project project) - { - var def = type.GetDefinition (); - if (def == null) { - project = null; - return false; - } - return def.TryGetSourceProject (out project); - } - - - internal static Project GetProjectWhereTypeIsDefined (this ITypeDefinition type) - { - var location = type.ParentAssembly.UnresolvedAssembly.Location; - if (string.IsNullOrEmpty (location)) - return null; - return TypeSystemService.GetProject (location); - } - - internal static Project GetProjectWhereTypeIsDefined (this IType type) - { - Project project; - TryGetSourceProject (type, out project); - return project; - } - - public static TextLocation GetLocation (this IType type) - { - return type.GetDefinition ().Region.Begin; - } - - public static bool IsBaseType (this IType type, IType potentialBase) - { - return type.GetAllBaseTypes ().Any (t => t.Equals (potentialBase)); - } - - public static bool IsObsolete (this IEntity member) - { - return member != null && member.Attributes.Any (a => a.AttributeType.FullName == "System.ObsoleteAttribute"); - } - - public static bool IsObsolete (this IEntity member, out string reason) - { - if (member == null) { - reason = null; - return false; - } - var attr = member.Attributes.FirstOrDefault (a => a.AttributeType.FullName == "System.ObsoleteAttribute"); - if (attr == null) { - reason = null; - return false; - } - reason = attr.PositionalArguments.Count > 0 ? attr.PositionalArguments [0].ConstantValue.ToString () : null; - return true; - } - - public static IType Resolve (this IUnresolvedTypeDefinition def, Project project) - { - if (project == null) - throw new ArgumentNullException ("project"); - var compilation = TypeSystemService.GetCompilation (project); - var ctx = new SimpleTypeResolveContext (compilation.MainAssembly); - var resolvedType = def.Resolve (ctx); - return resolvedType; - } - } - - /// <summary> - /// The folding parser is used for generating a preliminary parsed document that does not - /// contain a full dom - only some basic lexical constructs like comments or pre processor directives. - /// - /// This is useful for opening a document the first time to have some folding regions as start that are folded by default. - /// Otherwise an irritating screen update will occur. - /// </summary> - public interface IFoldingParser - { - ParsedDocument Parse (string fileName, string content); - } - - public static class TypeSystemService + public static partial class TypeSystemService { const string CurrentVersion = "1.1.8"; static readonly List<TypeSystemParserNode> parsers; @@ -171,6 +57,11 @@ namespace MonoDevelop.Ide.TypeSystem } } + public static bool TrackFileChanges { + get; + set; + } + public static void RemoveSkippedfile (FilePath fileName) { filesSkippedInParseThread = filesSkippedInParseThread.Where (f => f != fileName).ToArray (); @@ -196,7 +87,33 @@ namespace MonoDevelop.Ide.TypeSystem break; } }); + try { + emptyWorkspace = new MonoDevelopWorkspace (); + } catch (Exception e) { + LoggingService.LogFatalError ("Can't create roslyn workspace", e); + } + FileService.FileChanged += delegate(object sender, FileEventArgs e) { + // if (!TrackFileChanges) + // return; + foreach (var file in e) { + // Open documents are handled by the Document class itself. + if (IdeApp.Workbench != null && IdeApp.Workbench.GetDocument (file.FileName) != null) + continue; + try { + var text = MonoDevelop.Core.Text.StringTextSource.ReadFrom (file.FileName).Text; + foreach (var w in Workspaces) + w.UpdateFileContent (file.FileName, text); + } catch (FileNotFoundException) {} + } + if (IdeApp.Workbench != null) + foreach (var w in IdeApp.Workbench.Documents) + w.StartReparseThread (); + }; + + IntitializeTrackedProjectHandling (); + } +/* AddinManager.AddExtensionNodeHandler ("/MonoDevelop/TypeSystem/OutputTracking", delegate (object sender, ExtensionNodeEventArgs args) { var node = (TypeSystemOutputTrackingNode)args.ExtensionNode; switch (args.Change) { @@ -209,63 +126,11 @@ namespace MonoDevelop.Ide.TypeSystem } }); - FileService.FileChanged += delegate(object sender, FileEventArgs e) { - if (!TrackFileChanges) - return; - foreach (var file in e) { - // Open documents are handled by the Document class itself. - if (IdeApp.Workbench != null && IdeApp.Workbench.GetDocument (file.FileName) != null) - continue; - // - lock (projectWrapperUpdateLock) { - foreach (var wrapper in projectContents.Values) { - var projectFile = wrapper.Project.Files.GetFile (file.FileName); - if (projectFile != null) - QueueParseJob (wrapper, new [] { projectFile }); - } - UnresolvedAssemblyProxy ctx; - if (cachedAssemblyContents.TryGetValue (file.FileName, out ctx)) - CheckModifiedFile (ctx); - } - } - - foreach (var content in projectContents.Values.ToArray ()) { - var files = new List<ProjectFile> (); - foreach (var file in e) { - var f = content.Project.GetProjectFile (file.FileName); - if (f == null || f.BuildAction == BuildAction.None) - continue; - files.Add (f); - } - if (files.Count > 0) - QueueParseJob (content, files); - } - - }; - if (IdeApp.ProjectOperations != null) { - IdeApp.ProjectOperations.EndBuild += HandleEndBuild; - } - if (IdeApp.Workspace != null) { - IdeApp.Workspace.ActiveConfigurationChanged += HandleActiveConfigurationChanged; - } - } - - static void HandleActiveConfigurationChanged (object sender, EventArgs e) - { - foreach (var pr in projectContents.ToArray ()) { - var project = pr.Key as DotNetProject; - if (project != null) - CheckProjectOutput (project, true); - - pr.Value.referencesConnected = false; - } - } - static readonly List<TypeSystemOutputTrackingNode> outputTrackedProjects = new List<TypeSystemOutputTrackingNode> (); static bool IsOutputTracked (DotNetProject project) { - foreach (var projectType in project.GetTypeTags ()) { + foreach (var projectType in project.GetProjectTypes ()) { if (outputTrackedProjects.Any (otp => otp.ProjectType != null && string.Equals (otp.ProjectType, projectType, StringComparison.OrdinalIgnoreCase))) { return true; } @@ -273,35 +138,7 @@ namespace MonoDevelop.Ide.TypeSystem return outputTrackedProjects.Any (otp => otp.LanguageName != null && string.Equals (otp.LanguageName, project.LanguageName, StringComparison.OrdinalIgnoreCase)); } - static void CheckProjectOutput (DotNetProject project, bool autoUpdate) - { - if (project == null) - throw new ArgumentNullException ("project"); - if (IsOutputTracked (project)) { - var fileName = project.GetOutputFileName (IdeApp.Workspace.ActiveConfiguration); - - var wrapper = GetProjectContentWrapper (project); - if (wrapper == null) - return; - bool update = wrapper.UpdateTrackedOutputAssembly (fileName); - if (autoUpdate && update) { - wrapper.ReconnectAssemblyReferences (); - - // update documents - foreach (var openDocument in IdeApp.Workbench.Documents) { - openDocument.ReparseDocument (); - } - } - } - } - - static void HandleEndBuild (object sender, BuildEventArgs args) - { - var project = args.SolutionItem as DotNetProject; - if (project == null) - return; - CheckProjectOutput (project, true); - } +*/ public static TypeSystemParser GetParser (string mimeType, string buildAction = BuildAction.Compile) { @@ -309,7 +146,7 @@ namespace MonoDevelop.Ide.TypeSystem return n != null ? n.Parser : null; } - static TypeSystemParserNode GetTypeSystemParserNode (string mimeType, string buildAction) + internal static TypeSystemParserNode GetTypeSystemParserNode (string mimeType, string buildAction) { foreach (var mt in DesktopService.GetMimeTypeInheritanceChain (mimeType)) { var provider = Parsers.FirstOrDefault (p => p.CanParse (mt, buildAction)); @@ -319,172 +156,157 @@ namespace MonoDevelop.Ide.TypeSystem return null; } - static List<MimeTypeExtensionNode> foldingParsers; - - static IEnumerable<MimeTypeExtensionNode> FoldingParsers { - get { - if (foldingParsers == null) { - foldingParsers = new List<MimeTypeExtensionNode> (); - AddinManager.AddExtensionNodeHandler ("/MonoDevelop/TypeSystem/FoldingParser", delegate (object sender, ExtensionNodeEventArgs args) { - switch (args.Change) { - case ExtensionChange.Add: - foldingParsers.Add ((MimeTypeExtensionNode)args.ExtensionNode); - break; - case ExtensionChange.Remove: - foldingParsers.Remove ((MimeTypeExtensionNode)args.ExtensionNode); - break; - } - }); - } - return foldingParsers; - } - } - - public static IFoldingParser GetFoldingParser (string mimeType) + public static Task<ParsedDocument> ParseFile (Project project, string fileName, CancellationToken cancellationToken = default(CancellationToken)) { - foreach (var mt in DesktopService.GetMimeTypeInheritanceChain (mimeType)) { - var node = FoldingParsers.FirstOrDefault (n => n.MimeType == mt); - if (node != null) - return node.CreateInstance () as IFoldingParser; - } - return null; - } + StringTextSource text; - public static ParsedDocument ParseFile (Project project, string fileName) - { - string text; - try { if (!File.Exists (fileName)) return null; - text = Mono.TextEditor.Utils.TextFileUtility.ReadAllText (fileName); + text = StringTextSource.ReadFrom (fileName); } catch (Exception) { return null; } - - return ParseFile (project, fileName, DesktopService.GetMimeTypeForUri (fileName), text); + + return ParseFile (project, fileName, DesktopService.GetMimeTypeForUri (fileName), text, cancellationToken); } - static readonly object projectWrapperUpdateLock = new object (); - - public static ParsedDocument ParseFile (Project project, string fileName, string mimeType, string content) + public static Task<ParsedDocument> ParseFile (ParseOptions options, string mimeType, CancellationToken cancellationToken = default(CancellationToken)) { - if (fileName == null) - throw new ArgumentNullException ("fileName"); + if (options == null) + throw new ArgumentNullException ("options"); + if (options.FileName == null) + throw new ArgumentNullException ("options.FileName"); + var parser = GetParser (mimeType); if (parser == null) - return null; + return Task.FromResult ((ParsedDocument)null); - var t = Counters.ParserService.FileParsed.BeginTiming (fileName); + var t = Counters.ParserService.FileParsed.BeginTiming (options.FileName); try { - var result = parser.Parse (true, fileName, new StringReader (content), project); - lock (projectWrapperUpdateLock) { - ProjectContentWrapper wrapper; - if (project != null) { - projectContents.TryGetValue (project, out wrapper); - } else { - wrapper = null; - } - if (wrapper != null && (result.Flags & ParsedDocumentFlags.NonSerializable) != ParsedDocumentFlags.NonSerializable) { - var oldFile = wrapper._content.GetFile (fileName); - wrapper.UpdateContent (c => c.AddOrUpdateFiles (result.ParsedFile)); - UpdateProjectCommentTasks (wrapper, result); - if (oldFile != null) - wrapper.InformFileRemoved (new ParsedFileEventArgs (oldFile)); - wrapper.InformFileAdded (new ParsedFileEventArgs (result.ParsedFile)); - } - - // The parsed file could be included in other projects as well, therefore - // they need to be updated. - foreach (var cnt in projectContents.ToArray ()) { - if (cnt.Key == project) - continue; - // Use the project context because file lookup is faster there than in the project class. - var pcnt = cnt.Value; - var file = pcnt._content.GetFile (fileName); - if (file != null) { - var newResult = parser.Parse (false, fileName, new StringReader (content), pcnt.Project); - if ((newResult.Flags & ParsedDocumentFlags.NonSerializable) != ParsedDocumentFlags.NonSerializable) { - pcnt.UpdateContent (c => c.AddOrUpdateFiles (newResult.ParsedFile)); - pcnt.InformFileRemoved (new ParsedFileEventArgs (file)); - pcnt.InformFileAdded (new ParsedFileEventArgs (newResult.ParsedFile)); - } - } - } - } + var result = parser.Parse (options, cancellationToken); return result; + } catch (OperationCanceledException) { + return Task.FromResult ((ParsedDocument)null); } catch (Exception e) { LoggingService.LogError ("Exception while parsing: " + e); - return null; + return Task.FromResult ((ParsedDocument)null); } finally { t.Dispose (); } } - public static ParsedDocument ParseFile (Project project, string fileName, string mimeType, TextReader content) + internal static bool CanParseProjections (Project project, string mimeType, string fileName) + { + var parser = GetParser (mimeType); + if (parser == null) + return false; + var projectFile = project.GetProjectFile (fileName); + if (projectFile == null) + return false; + + return parser.CanGenerateProjection (mimeType, projectFile.BuildAction, project.SupportedLanguages); + } + + public static Task<ParsedDocument> ParseFile (Project project, string fileName, string mimeType, ITextSource content, CancellationToken cancellationToken = default(CancellationToken)) { - return ParseFile (project, fileName, mimeType, content.ReadToEnd ()); + return ParseFile (new ParseOptions { FileName = fileName, Project = project, Content = content }, mimeType, cancellationToken); } - public static ParsedDocument ParseFile (Project project, TextEditorData data) + public static Task<ParsedDocument> ParseFile (Project project, string fileName, string mimeType, TextReader content, CancellationToken cancellationToken = default(CancellationToken)) { - return ParseFile (project, data.FileName, data.MimeType, data.Text); + return ParseFile (project, fileName, mimeType, new StringTextSource (content.ReadToEnd ()), cancellationToken); } - public static ParsedDocument ParseFile (string fileName, string mimeType, string text, ProjectContentWrapper wrapper = null) + public static Task<ParsedDocument> ParseFile (Project project, IReadonlyTextDocument data, CancellationToken cancellationToken = default(CancellationToken)) { - using (var reader = new StringReader (text)) - return ParseFile (fileName, mimeType, reader, wrapper); + return ParseFile (project, data.FileName, data.MimeType, data, cancellationToken); } - public static ParsedDocument ParseFile (string fileName, string mimeType, TextReader content, ProjectContentWrapper wrapper = null) + internal static Task<ParsedDocumentProjection> ParseProjection (ParseOptions options, string mimeType, CancellationToken cancellationToken = default(CancellationToken)) { + if (options == null) + throw new ArgumentNullException ("options"); + if (options.FileName == null) + throw new ArgumentNullException ("fileName"); + var parser = GetParser (mimeType); if (parser == null) - return null; - var t = Counters.ParserService.FileParsed.BeginTiming (fileName); + return Task.FromResult ((ParsedDocumentProjection)null); + + var t = Counters.ParserService.FileParsed.BeginTiming (options.FileName); try { - var result = parser.Parse (true, fileName, content); - lock (projectWrapperUpdateLock) { - if (wrapper != null && (result.Flags & ParsedDocumentFlags.NonSerializable) != ParsedDocumentFlags.NonSerializable) { - var oldFile = wrapper._content.GetFile (fileName); - wrapper.UpdateContent (c => c.AddOrUpdateFiles (result.ParsedFile)); - UpdateProjectCommentTasks (wrapper, result); - if (oldFile != null) - wrapper.InformFileRemoved (new ParsedFileEventArgs (oldFile)); - wrapper.InformFileAdded (new ParsedFileEventArgs (result.ParsedFile)); + var result = parser.GenerateParsedDocumentProjection (options, cancellationToken); + if (options.Project != null) { + var Workspace = Workspaces.First () ; + var projectId = Workspace.GetProjectId (options.Project); + if (projectId != null) { + foreach (var projection in result.Result.Projections) { + var docId = Workspace.GetDocumentId (projectId, projection.Document.FileName); + if (docId != null) + Workspace.InformDocumentTextChange (docId, new MonoDevelopSourceText (projection.Document)); + } } } return result; + } catch (OperationCanceledException) { + return Task.FromResult ((ParsedDocumentProjection)null); } catch (Exception e) { - LoggingService.LogError ("Exception while parsing :" + e); - return null; + LoggingService.LogError ("Exception while parsing: " + e); + return Task.FromResult ((ParsedDocumentProjection)null); } finally { t.Dispose (); } } - public static event EventHandler ParseOperationStarted; + internal static Task<ParsedDocumentProjection> ParseProjection (Project project, string fileName, string mimeType, ITextSource content, CancellationToken cancellationToken = default(CancellationToken)) + { + return ParseProjection (new ParseOptions { FileName = fileName, Project = project, Content = content }, mimeType, cancellationToken); + } + + internal static Task<ParsedDocumentProjection> ParseProjection (Project project, string fileName, string mimeType, TextReader content, CancellationToken cancellationToken = default(CancellationToken)) + { + return ParseProjection (project, fileName, mimeType, new StringTextSource (content.ReadToEnd ()), cancellationToken); + } - internal static void StartParseOperation () + internal static Task<ParsedDocumentProjection> ParseProjection (Project project, IReadonlyTextDocument data, CancellationToken cancellationToken = default(CancellationToken)) { - if ((parseStatus++) == 0) { - if (ParseOperationStarted != null) - ParseOperationStarted (null, EventArgs.Empty); - } + return ParseProjection (project, data.FileName, data.MimeType, data, cancellationToken); } - public static event EventHandler ParseOperationFinished; + + #region Folding parsers + static List<MimeTypeExtensionNode> foldingParsers; - internal static void EndParseOperation () + static IEnumerable<MimeTypeExtensionNode> FoldingParsers { + get { + if (foldingParsers == null) { + foldingParsers = new List<MimeTypeExtensionNode> (); + AddinManager.AddExtensionNodeHandler ("/MonoDevelop/TypeSystem/FoldingParser", delegate (object sender, ExtensionNodeEventArgs args) { + switch (args.Change) { + case ExtensionChange.Add: + foldingParsers.Add ((MimeTypeExtensionNode)args.ExtensionNode); + break; + case ExtensionChange.Remove: + foldingParsers.Remove ((MimeTypeExtensionNode)args.ExtensionNode); + break; + } + }); + } + return foldingParsers; + } + } + + public static IFoldingParser GetFoldingParser (string mimeType) { - if (parseStatus == 0) - return; - if (--parseStatus == 0) { - if (ParseOperationFinished != null) - ParseOperationFinished (null, EventArgs.Empty); + foreach (var mt in DesktopService.GetMimeTypeInheritanceChain (mimeType)) { + var node = FoldingParsers.FirstOrDefault (n => n.MimeType == mt); + if (node != null) + return node.CreateInstance () as IFoldingParser; } + return null; } + #endregion #region Parser Database Handling @@ -702,7 +524,7 @@ namespace MonoDevelop.Ide.TypeSystem { var t = Counters.ParserService.ObjectDeserialized.BeginTiming (path); try { - using (var fs = new FileStream (path, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.SequentialScan)) { + using (var fs = new FileStream (path, System.IO.FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.SequentialScan)) { using (var reader = new BinaryReaderWith7BitEncodedInts (fs)) { lock (sharedSerializer) { return (T)sharedSerializer.Deserialize (reader); @@ -724,7 +546,7 @@ namespace MonoDevelop.Ide.TypeSystem var t = Counters.ParserService.ObjectSerialized.BeginTiming (path); try { - using (var fs = new FileStream (path, FileMode.Create, FileAccess.Write)) { + using (var fs = new FileStream (path, System.IO.FileMode.Create, FileAccess.Write)) { using (var writer = new BinaryWriterWith7BitEncodedInts (fs)) { lock (sharedSerializer) { sharedSerializer.Serialize (writer, obj); @@ -805,2230 +627,86 @@ namespace MonoDevelop.Ide.TypeSystem } } - static void StoreProjectCache (Project project, ProjectContentWrapper wrapper) - { - if (!wrapper.WasChanged) - return; - string cacheDir = GetCacheDirectory (project, true); - string fileName = Path.GetTempFileName (); - - SerializeObject (fileName, wrapper.Content.RemoveAssemblyReferences (wrapper.Content.AssemblyReferences)); - - string cacheFile = Path.Combine (cacheDir, "completion.cache"); - - try { - if (File.Exists (cacheFile)) - File.Delete (cacheFile); - File.Move (fileName, cacheFile); - } catch (Exception e) { - LoggingService.LogError ("Error whil saving cache " + cacheFile, e); - } - - foreach (var extensionObject in wrapper.ExtensionObjects) { - StoreExtensionObject (cacheDir, extensionObject); - } - } - #endregion - - #region Project loading - - public static void Load (WorkspaceItem item) - { - using (Counters.ParserService.WorkspaceItemLoaded.BeginTiming ()) { - InternalLoad (item); - CleanupCache (); - } - } - static CancellationTokenSource loadCancellationSource = new CancellationTokenSource (); - static bool loadWs = false; - static void InternalLoad (WorkspaceItem item) - { - var ws = item as Workspace; - if (ws != null) { - loadWs = true; - loadCancellationSource.Cancel (); - loadCancellationSource = new CancellationTokenSource (); - foreach (WorkspaceItem it in ws.Items) - InternalLoad (it); - ws.ItemAdded += OnWorkspaceItemAdded; - ws.ItemRemoved += OnWorkspaceItemRemoved; - } else { - if (!loadWs) { - loadCancellationSource.Cancel (); - loadCancellationSource = new CancellationTokenSource (); - } - var solution = item as Solution; - if (solution != null) { - Parallel.ForEach (solution.GetAllProjects (), project => LoadProject (project)); - var list = projectContents.Values.ToList (); - Task.Run (delegate { - foreach (var wrapper in list) { - CheckModifiedFiles (wrapper.Project, wrapper.Project.Files.ToArray (), wrapper, loadCancellationSource.Token); - } - }); - - solution.SolutionItemAdded += OnSolutionItemAdded; - solution.SolutionItemRemoved += OnSolutionItemRemoved; - OnSolutionLoaded (new SolutionEventArgs (solution)); - } - } - } - - public static event EventHandler<SolutionEventArgs> SolutionLoaded; - - static void OnSolutionLoaded (SolutionEventArgs e) - { - var handler = SolutionLoaded; - if (handler != null) - handler (null, e); - } - - [Serializable] - public class UnresolvedAssemblyDecorator : IUnresolvedAssembly - { - readonly ProjectContentWrapper wrapper; - - IUnresolvedAssembly assembly { - get { - if (wrapper.OutputAssembly != null) - return wrapper.OutputAssembly; - return wrapper.Compilation.MainAssembly.UnresolvedAssembly; - } - } - - public UnresolvedAssemblyDecorator (ProjectContentWrapper wrapper) - { - this.wrapper = wrapper; - } - - #region IUnresolvedAssembly implementation - - public string AssemblyName { - get { - return assembly.AssemblyName; - } - } - - public string FullAssemblyName { - get { - return assembly.FullAssemblyName; - } - } - - public string Location { - get { - return assembly.Location; - } - } - - public IEnumerable<IUnresolvedAttribute> AssemblyAttributes { - get { - return assembly.AssemblyAttributes; - } - } - - public IEnumerable<IUnresolvedAttribute> ModuleAttributes { - get { - return assembly.ModuleAttributes; - } - } - - public IEnumerable<IUnresolvedTypeDefinition> TopLevelTypeDefinitions { - get { - return assembly.TopLevelTypeDefinitions; - } - } - - #endregion - - #region IAssemblyReference implementation - - public IAssembly Resolve (ITypeResolveContext context) - { - return assembly.Resolve (context); - } - - #endregion - - } - - [Serializable] - public class ProjectContentWrapper - { - readonly Dictionary<Type, object> extensionObjects = new Dictionary<Type, object> (); - List<ProjectContentWrapper> referencedWrappers = new List<ProjectContentWrapper>(); - List<UnresolvedAssemblyProxy> referencedAssemblies = new List<UnresolvedAssemblyProxy>(); - internal IProjectContent _content; - internal bool referencesConnected; - - /* - static bool GetReferencesConnected (ProjectContentWrapper pcw, HashSet<ProjectContentWrapper> wrapper) - { - if (wrapper.Contains (pcw)) - return true; - wrapper.Add (pcw); - return pcw.referencesConnected && pcw.referencedWrappers.All (w => GetReferencesConnected (w, wrapper)); - }*/ - - public IProjectContent Content { - get { - if (!referencesConnected) { - EnsureReferencesAreLoaded (); - } - return _content; - } - private set { - if (value == null) - throw new InvalidOperationException ("Project content can't be null"); - _content = value; - } - } - - /// <summary> - /// Gets the extension objects attached to the content wrapper. - /// </summary> - public IEnumerable<object> ExtensionObjects { - get { - return extensionObjects.Values; - } - } - - /// <summary> - /// Updates an extension object for the wrapper. Note that only one extension object of a certain - /// type may be stored inside the project content wrapper. - /// - /// The extension objects need to be serializable and are stored in the project cache on project unload. - /// </summary> - public void UpdateExtensionObject (object ext) - { - if (ext == null) - throw new ArgumentNullException ("ext"); - extensionObjects [ext.GetType ()] = ext; - } - - /// <summary> - /// Gets a specific extension object. This may lazy load an existing extension object from disk, - /// if called the first time and a serialized extension object exists. - /// </summary> - /// <returns> - /// The extension object. Or null, if no extension object of the specified type was registered. - /// </returns> - /// <typeparam name='T'> - /// The type of the extension object. - /// </typeparam> - public T GetExtensionObject<T> () where T : class - { - object result; - if (extensionObjects.TryGetValue (typeof(T), out result)) - return (T)result; - - string cacheDir = GetCacheDirectory (Project); - if (cacheDir == null) - return default(T); - - try { - string fileName = Path.Combine (cacheDir, typeof(T).FullName + ".cache"); - if (File.Exists (fileName)) { - var deserialized = DeserializeObject<T> (fileName); - extensionObjects [typeof(T)] = deserialized; - return deserialized; - } - } catch (Exception) { - Console.WriteLine ("Can't deserialize :" + typeof(T).FullName); - } - - return default (T); - } - - List<Action<IProjectContent>> loadActions = new List<Action<IProjectContent>> (); - - public void RunWhenLoaded (Action<IProjectContent> act) - { - lock (updateContentLock) { - var lazyProjectLoader = _content as LazyProjectLoader; - if (loadActions != null) { - lock (loadActions) { - if (lazyProjectLoader != null && !lazyProjectLoader.ContextTask.IsCompleted) { - loadActions.Add (act); - return; - } - } - } - } - act (Content); - } - - void ClearCachedCompilations () - { - // Need to clear this compilation & all compilations that reference this directly or indirectly - var stack = new Stack<ProjectContentWrapper> (); - stack.Push (this); - var cleared = new HashSet<ProjectContentWrapper> (); - while (stack.Count > 0) { - var cur = stack.Pop (); - if (cleared.Contains (cur)) - continue; - cleared.Add (cur); - cur.compilation = null; - foreach (var project in cur.ReferencedProjects) { - var projectContentWrapper = GetProjectContentWrapper (project); - if (projectContentWrapper != null) - stack.Push (projectContentWrapper); - } - } - } - - readonly object updateContentLock = new object (); - - void RunLoadActions () - { - if (loadActions == null) - return; - Action<IProjectContent>[] actions; - lock (loadActions) { - actions = loadActions.ToArray (); - loadActions = null; - } - foreach (var action in actions) - action (Content); - } - - public void UpdateContent (Func<IProjectContent, IProjectContent> updateFunc) - { - LazyProjectLoader lazyProjectLoader; - lock (updateContentLock) { - lazyProjectLoader = _content as LazyProjectLoader; - if (lazyProjectLoader != null) { - lazyProjectLoader.ContextTask.Wait (); - } - _content = updateFunc (_content); - ClearCachedCompilations (); - WasChanged = true; - if (lazyProjectLoader != null && !(_content is LazyProjectLoader)) { - RunLoadActions (); - } - } - } - - public void InformFileRemoved (ParsedFileEventArgs e) - { - var handler = FileRemoved; - if (handler != null) - handler (this, e); - } - - public void InformFileAdded (ParsedFileEventArgs e) - { - var handler = FileAdded; - if (handler != null) - handler (this, e); - } - - public EventHandler<ParsedFileEventArgs> FileAdded; - public EventHandler<ParsedFileEventArgs> FileRemoved; - public bool WasChanged; - [NonSerialized] - ICompilation compilation; - - public ICompilation Compilation { - get { - lock (updateContentLock) { - if (compilation == null) { - compilation = Content.CreateCompilation (); - } - return compilation; - } - } - } - - public Project Project { - get; - private set; - } - - [NonSerialized] - int loadOperationDepth = 0; - [NonSerialized] - readonly object loadOperationLocker = new object (); - - internal void BeginLoadOperation () - { - lock (loadOperationLocker) { - loadOperationDepth++; - if (loadOperationDepth == 1) - UpdateLoadState (); - } - } - - internal void EndLoadOperation () - { - lock (loadOperationLocker) { - if (loadOperationDepth > 0) { - loadOperationDepth--; - } - if (loadOperationDepth == 0) - UpdateLoadState (); - } - } - - bool isLoaded; - public bool IsLoaded { - get { - return isLoaded; - } - } - - [NonSerialized] - CancellationTokenSource src; - - internal void CancelLoad () - { - if (src != null) - src.Cancel (); - } - - void UpdateLoadState () - { - bool wasLoaded = isLoaded; - isLoaded = loadOperationDepth == 0 && referencesConnected && !referencedAssemblies.Any (a => a.InLoad); - if (isLoaded && !wasLoaded) - OnLoad (EventArgs.Empty); - } - - internal void RequestLoad () - { - BeginLoadOperation (); - EnsureReferencesAreLoaded (); - CancelLoad (); - src = new CancellationTokenSource (); - var token = src.Token; - //Task.Factory.StartNew (delegate { - try { - foreach (var asm in referencedAssemblies.ToArray ()) { - if (token.IsCancellationRequested) - break; - var ctxLoader = asm.CtxLoader; - if (ctxLoader != null) - ctxLoader.EnsureAssemblyLoaded (); - } - } finally { - EndLoadOperation (); - } - //}); - } - - public event EventHandler Loaded; - - protected virtual void OnLoad (EventArgs e) - { - var handler = Loaded; - if (handler != null) - handler (this, e); - } - - [NonSerialized] - internal LazyAssemblyLoader OutputAssembly; - - internal bool UpdateTrackedOutputAssembly (FilePath fileName) - { - if (File.Exists (fileName)) { - OutputAssembly = new LazyAssemblyLoader (fileName, null); - // a clean operation could remove the assembly, thefore we need to load it. - OutputAssembly.EnsureAssemblyLoaded (); - return true; - } - return false; - } - - public ProjectContentWrapper (Project project) - { - if (project == null) - throw new ArgumentNullException ("project"); - this.Project = project; - var lazyProjectLoader = new LazyProjectLoader (this); - this.Content = lazyProjectLoader; - } - - public IEnumerable<Project> ReferencedProjects { - get { - return Project.GetReferencedItems (ConfigurationSelector.Default).OfType<DotNetProject> (); - } - } - - class LazyProjectLoader : IProjectContent - { - readonly ProjectContentWrapper wrapper; - readonly Task<IProjectContent> contextTask; - - public Task<IProjectContent> ContextTask { - get { - return contextTask; - } - } - object contentLock = new object (); - IProjectContent contentWithReferences; - public IProjectContent Content { - get { - lock (contentLock) { - if (References != null) { - return contentWithReferences ?? (contentWithReferences = contextTask.Result.AddAssemblyReferences (References)); - } - return contextTask.Result; - } - } - } - - List<IAssemblyReference> references; - public List<IAssemblyReference> References { - get { - return references; - } - set { - lock (contentLock) { - references = value; - } - } - } - - public LazyProjectLoader (ProjectContentWrapper wrapper) - { - this.wrapper = wrapper; - contextTask = Task.Factory.StartNew (delegate { - try { - this.wrapper.BeginLoadOperation (); - var p = this.wrapper.Project; - var context = LoadProjectCache (p); - - var assemblyName = p.ParentSolution != null ? p.GetOutputFileName (p.ParentSolution.DefaultConfigurationSelector).FileNameWithoutExtension : p.Name; - if (string.IsNullOrEmpty (assemblyName)) - assemblyName = p.Name; - - if (context != null) { - return context.SetAssemblyName (assemblyName) ?? context; - } - - context = new MonoDevelopProjectContent (p); - context = context.SetLocation (p.FileName); - context = context.SetAssemblyName (assemblyName); - - QueueParseJob (this.wrapper); - return context; - } finally { - this.wrapper.EndLoadOperation (); - } - }); - } - - static IProjectContent LoadProjectCache (Project project) - { - string cacheDir = GetCacheDirectory (project); - if (cacheDir == null) - return null; - - var cacheFile = Path.Combine (cacheDir, "completion.cache"); - if (!File.Exists (cacheFile)) - return null; - try { - var cache = DeserializeObject<IProjectContent> (cacheFile); - var monoDevelopProjectContent = cache as MonoDevelopProjectContent; - if (monoDevelopProjectContent != null) - monoDevelopProjectContent.Project = project; - return cache; - } catch (Exception e) { - LoggingService.LogWarning ("Error while reading completion cache, regenerating", e); - Directory.Delete (cacheDir, true); - return null; - } - } - - #region IAssemblyReference implementation - - IAssembly IAssemblyReference.Resolve (ITypeResolveContext context) - { - return Content.Resolve (context); - } - - #endregion - - #region IUnresolvedAssembly implementation - - string IUnresolvedAssembly.AssemblyName { - get { - return Content.AssemblyName; - } - } - - string IUnresolvedAssembly.FullAssemblyName { - get { - return Content.FullAssemblyName; - } - } - - string IUnresolvedAssembly.Location { - get { - return Content.Location; - } - } - - IEnumerable<IUnresolvedAttribute> IUnresolvedAssembly.AssemblyAttributes { - get { - return Content.AssemblyAttributes; - } - } - - IEnumerable<IUnresolvedAttribute> IUnresolvedAssembly.ModuleAttributes { - get { - return Content.ModuleAttributes; - } - } - - IEnumerable<IUnresolvedTypeDefinition> IUnresolvedAssembly.TopLevelTypeDefinitions { - get { - return Content.TopLevelTypeDefinitions; - } - } - - #endregion - - #region IProjectContent implementation - - string IProjectContent.ProjectFileName { get { return Content.ProjectFileName; } } - - IUnresolvedFile IProjectContent.GetFile (string fileName) - { - return Content.GetFile (fileName); - } - - ICompilation IProjectContent.CreateCompilation () - { - return Content.CreateCompilation (); - } - - public ICompilation CreateCompilation (ISolutionSnapshot solutionSnapshot) - { - return Content.CreateCompilation (solutionSnapshot); - } - - IProjectContent IProjectContent.SetAssemblyName (string newAssemblyName) - { - return Content.SetAssemblyName (newAssemblyName); - } - - IProjectContent IProjectContent.SetLocation (string newLocation) - { - return Content.SetLocation (newLocation); - } - - IProjectContent IProjectContent.AddAssemblyReferences (IEnumerable<IAssemblyReference> references) - { - return Content.AddAssemblyReferences (references); - } - - IProjectContent IProjectContent.AddAssemblyReferences (params IAssemblyReference[] references) - { - return Content.AddAssemblyReferences (references); - } - - IProjectContent IProjectContent.RemoveAssemblyReferences (IEnumerable<IAssemblyReference> references) - { - return Content.RemoveAssemblyReferences (references); - } - - IProjectContent IProjectContent.RemoveAssemblyReferences (params IAssemblyReference[] references) - { - return Content.RemoveAssemblyReferences (references); - } - #pragma warning disable 618 - IProjectContent IProjectContent.UpdateProjectContent (IUnresolvedFile oldFile, IUnresolvedFile newFile) - { - return Content.UpdateProjectContent (oldFile, newFile); - } - - public IProjectContent UpdateProjectContent (IEnumerable<IUnresolvedFile> oldFiles, IEnumerable<IUnresolvedFile> newFiles) - { - return Content.UpdateProjectContent (oldFiles, newFiles); - } - #pragma warning restore 618 - - public IProjectContent AddOrUpdateFiles (IEnumerable<IUnresolvedFile> newFiles) - { - return Content.AddOrUpdateFiles (newFiles); - } - - public IProjectContent AddOrUpdateFiles (params IUnresolvedFile[] newFiles) - { - return Content.AddOrUpdateFiles (newFiles); - } - - IEnumerable<IUnresolvedFile> IProjectContent.Files { - get { - return Content.Files; - } - } - - IEnumerable<IAssemblyReference> IProjectContent.AssemblyReferences { - get { - return Content.AssemblyReferences; - } - } - - IProjectContent IProjectContent.SetProjectFileName (string newProjectFileName) - { - return Content.SetProjectFileName (newProjectFileName); - } - - IProjectContent IProjectContent.RemoveFiles (IEnumerable<string> fileNames) - { - return Content.RemoveFiles (fileNames); - } - - IProjectContent IProjectContent.RemoveFiles (params string[] fileNames) - { - return Content.RemoveFiles (fileNames); - } - - #endregion - - object compilerSettings; - - public IProjectContent SetCompilerSettings (object compilerSettings) - { - this.compilerSettings = compilerSettings; - return this; - } - - public object CompilerSettings { - get { - return compilerSettings; - } - } - } - - bool HasCyclicRefs (ProjectContentWrapper wrapper, HashSet<Project> nonCyclicCache) - { - if (nonCyclicCache.Contains (wrapper.Project)) - return false; - nonCyclicCache.Add (wrapper.Project); - foreach (var referencedProject in wrapper.ReferencedProjects) { - ProjectContentWrapper w; - if (referencedProject == Project || referencedProject == wrapper.Project || projectContents.TryGetValue (referencedProject, out w) && HasCyclicRefs (w, nonCyclicCache)) { - return true; - } - } - return false; - } - - List<UnresolvedAssemblyProxy> LoadReferencedAssemblies (DotNetProject netProject) - { - var newReferencedAssemblies = new List<UnresolvedAssemblyProxy> (); - try { - foreach (string file in netProject.GetReferencedAssemblies (IdeApp.IsInitialized ? IdeApp.Workspace.ActiveConfiguration : ConfigurationSelector.Default, false)) { - string fileName; - if (!Path.IsPathRooted (file)) { - fileName = Path.Combine (Path.GetDirectoryName (netProject.FileName), file); - } - else { - fileName = Path.GetFullPath (file); - } - var ctx = LoadAssemblyContext (fileName); - if (ctx != null) { - newReferencedAssemblies.Add (ctx); - ctx.Loaded += HandleReferencedProjectInLoadChange; - } - else { - LoggingService.LogWarning ("TypeSystemService: Can't load assembly context for:" + file); - } - } - } - catch (Exception e) { - LoggingService.LogError ("Error while getting assembly references", e); - } - return newReferencedAssemblies; - } - - public void EnsureReferencesAreLoaded () - { - lock (projectContentLock) { - if (referencesConnected) - return; - compilation = null; - referencesConnected = true; - var netProject = Project as DotNetProject; - if (netProject == null) - return; - try { - var contexts = new List<IAssemblyReference> (); - var nonCyclicCache = new HashSet<Project> (); - foreach (var referencedWrapper in referencedWrappers) { - referencedWrapper.Loaded -= HandleReferencedProjectInLoadChange; - } - var newReferencedWrappers = new List<ProjectContentWrapper> (); - foreach (var referencedProject in ReferencedProjects) { - ProjectContentWrapper wrapper; - if (projectContents.TryGetValue (referencedProject, out wrapper)) { - if (HasCyclicRefs (wrapper, nonCyclicCache)) - continue; - wrapper.Loaded += HandleReferencedProjectInLoadChange; - newReferencedWrappers.Add (wrapper); - contexts.Add (new UnresolvedAssemblyDecorator (wrapper)); - } - } - this.referencedWrappers = newReferencedWrappers; - UnresolvedAssemblyProxy ctx; - // Add mscorlib reference - var config = IdeApp.Workspace != null ? netProject.GetConfiguration (IdeApp.Workspace.ActiveConfiguration) as DotNetProjectConfiguration : null; - bool noStdLib = false; - if (config != null) { - var parameters = config.CompilationParameters as DotNetCompilerParameters; - if (parameters != null) { - noStdLib = parameters.NoStdLib; - } - } - if (!noStdLib && netProject.TargetRuntime != null && netProject.TargetRuntime.AssemblyContext != null) { - var corLibRef = netProject.TargetRuntime.AssemblyContext.GetAssemblyForVersion (typeof(object).Assembly.FullName, null, netProject.TargetFramework); - if (corLibRef != null) { - ctx = LoadAssemblyContext (corLibRef.Location); - if (ctx != null) - contexts.Add (ctx); - } - } - // Get the assembly references throught the project, since it may have custom references - foreach (var asm in referencedAssemblies) { - asm.Loaded += HandleReferencedProjectInLoadChange; - } - var newReferencedAssemblies = LoadReferencedAssemblies (netProject); - contexts.AddRange (newReferencedAssemblies); - referencedAssemblies = newReferencedAssemblies; - bool changed = WasChanged; - var lazyProjectLoader = Content as LazyProjectLoader; - if (lazyProjectLoader != null) { - lazyProjectLoader.References = contexts; - } - else { - UpdateContent (c => c.RemoveAssemblyReferences (Content.AssemblyReferences).AddAssemblyReferences (contexts)); - } - WasChanged = changed; - } catch (Exception e) { - if (netProject.TargetRuntime == null) { - LoggingService.LogError ("Target runtime was null: " + Project.Name); - } else if (netProject.TargetRuntime.AssemblyContext == null) { - LoggingService.LogError ("Target runtime assembly context was null: " + Project.Name); - } - LoggingService.LogError ("Error while reloading all references of project: " + Project.Name, e); - } finally { - UpdateLoadState (); - } - } - } - static readonly object reconnectLock = new object(); - public void ReconnectAssemblyReferences () - { - var netProject = Project as DotNetProject; - if (netProject == null) - return; - lock (reconnectLock) { - CancelLoad (); - this.referencesConnected = false; - RequestLoad (); - } - } - - void HandleReferencedProjectInLoadChange (object sender, EventArgs e) - { - UpdateLoadState (); - } - - internal void Unload () - { - CancelLoad (); - foreach (var asm in referencedAssemblies) { - asm.Loaded -= HandleReferencedProjectInLoadChange; - } - foreach (var wrapper in referencedWrappers) { - wrapper.Loaded -= HandleReferencedProjectInLoadChange; - } - loadActions = null; - foreach (var wrapper in referencedWrappers) { - wrapper.Loaded -= HandleReferencedProjectInLoadChange; - } - referencedWrappers.Clear (); - referencedAssemblies.Clear (); - Loaded = null; - Content = new CSharpProjectContent (); - } - } - - static readonly object projectContentLock = new object (); - static readonly Dictionary<Project, ProjectContentWrapper> projectContents = new Dictionary<Project, ProjectContentWrapper> (); - - public static ProjectContentWrapper LoadProject (Project project) + + internal static Microsoft.CodeAnalysis.Document GetCodeAnalysisDocument (Microsoft.CodeAnalysis.DocumentId analysisDocument, CancellationToken cancellationToken = default (CancellationToken)) { - if (IncLoadCount (project) != 1) - return null; - lock (projectContentLock) { - if (projectContents.ContainsKey (project)) - return null; - try { - Counters.ParserService.ProjectsLoaded++; - ProjectContentWrapper wrapper; - projectContents [project] = wrapper = new ProjectContentWrapper (project); - var dotNetProject = project as DotNetProject; - if (dotNetProject != null) - CheckProjectOutput (dotNetProject, false); - - project.FileAddedToProject += OnFileAdded; - project.FileRemovedFromProject += OnFileRemoved; - project.FileRenamedInProject += OnFileRenamed; - project.Modified += OnProjectModified; - - if (dotNetProject != null) { - StartFrameworkLookup (dotNetProject); - } - return wrapper; - } catch (Exception ex) { - LoggingService.LogError ("Parser database for project '" + project.Name + " could not be loaded", ex); - } - return null; + foreach (var w in Workspaces) { + var doc = w.GetDocument (analysisDocument, cancellationToken); + if (doc != null) + return doc; } - } - - public static Project GetProject (IEntity entity) - { - if (entity == null) - throw new ArgumentNullException ("entity"); - return GetProject (entity.ParentAssembly.UnresolvedAssembly.Location); - } - - public static Project GetProject (string location) - { - foreach (var wrapper in projectContents) - if (wrapper.Value.Compilation.MainAssembly.UnresolvedAssembly.Location == location) - return wrapper.Key; return null; } - #region Project modification handlers - - static void OnFileAdded (object sender, ProjectFileEventArgs args) - { - var project = (Project)sender; - foreach (ProjectFileEventInfo fargs in args) { - QueueParseJob (projectContents [project], new [] { fargs.ProjectFile }); - } - } - - static void OnFileRemoved (object sender, ProjectFileEventArgs args) - { - var project = (Project)sender; - foreach (ProjectFileEventInfo fargs in args) { - var wrapper = projectContents [project]; - var fileName = fargs.ProjectFile.Name; - var file = wrapper._content.GetFile (fileName); - if (file == null) - continue; - wrapper.UpdateContent (c => c.RemoveFiles (fileName)); - wrapper.InformFileRemoved (new ParsedFileEventArgs (file)); - - var tags = wrapper.GetExtensionObject <ProjectCommentTags> (); - if (tags != null) - tags.RemoveFile (wrapper.Project, fileName); - } - } - - static void OnFileRenamed (object sender, ProjectFileRenamedEventArgs args) - { - var project = (Project)sender; - foreach (ProjectFileRenamedEventInfo fargs in args) { - var content = projectContents [project]; - var file = content._content.GetFile (fargs.OldName); - if (file == null) - continue; - content.UpdateContent (c => c.RemoveFiles (fargs.OldName)); - content.InformFileRemoved (new ParsedFileEventArgs (file)); - - var tags = content.GetExtensionObject <ProjectCommentTags> (); - if (tags != null) - tags.RemoveFile (project, fargs.OldName); - - QueueParseJob (content, new [] { fargs.ProjectFile }); - } - } - - static void OnProjectModified (object sender, SolutionItemModifiedEventArgs args) + internal static void InformDocumentClose (Microsoft.CodeAnalysis.DocumentId analysisDocument, FilePath fileName) { - if (!args.Any (x => x.Hint == "TargetFramework" || x.Hint == "References")) - return; - var project = (Project)sender; - - ProjectContentWrapper wrapper; - projectContents.TryGetValue (project, out wrapper); - if (wrapper == null) - return; - wrapper.ReconnectAssemblyReferences (); - } - - #endregion - - internal static void Unload (WorkspaceItem item) - { - var ws = item as Workspace; - TrackFileChanges = false; - loadCancellationSource.Cancel (); - if (ws != null) { - foreach (WorkspaceItem it in ws.Items) - Unload (it); - ws.ItemAdded -= OnWorkspaceItemAdded; - ws.ItemRemoved -= OnWorkspaceItemRemoved; - projectContents.Clear (); - loadWs = false; - } else { - var solution = item as Solution; - if (solution != null) { - foreach (var project in solution.GetAllProjects ()) { - UnloadProject (project); - } - solution.SolutionItemAdded -= OnSolutionItemAdded; - solution.SolutionItemRemoved -= OnSolutionItemRemoved; - } - } + foreach (var w in Workspaces) { + if (w.GetOpenDocumentIds ().Contains (analysisDocument) ) + w.InformDocumentClose (analysisDocument, fileName); - cachedAssemblyContents.Clear (); - lock (parseQueueLock) { - parseQueueIndex.Clear (); - parseQueue.Clear (); } - TrackFileChanges = true; } - internal static void UnloadProject (Project project, bool skipProjectSerialization = false) + internal static void InformDocumentOpen (Microsoft.CodeAnalysis.DocumentId analysisDocument, TextEditor editor) { - if (DecLoadCount (project) != 0) - return; - Counters.ParserService.ProjectsLoaded--; - project.FileAddedToProject -= OnFileAdded; - project.FileRemovedFromProject -= OnFileRemoved; - project.FileRenamedInProject -= OnFileRenamed; - project.Modified -= OnProjectModified; - - ProjectContentWrapper wrapper; - lock (projectWrapperUpdateLock) { - if (!projectContents.TryGetValue (project, out wrapper)) + foreach (var w in Workspaces) { + if (w.Contains (analysisDocument.ProjectId)) { + w.InformDocumentOpen (analysisDocument, editor); return; - projectContents.Remove (project); - } - if (!skipProjectSerialization) - StoreProjectCache (project, wrapper); - OnProjectUnloaded (new ProjectUnloadEventArgs (project, wrapper)); - wrapper.Unload (); - } - - public static event EventHandler<ProjectUnloadEventArgs> ProjectUnloaded; - - static void OnProjectUnloaded (ProjectUnloadEventArgs e) - { - var handler = ProjectUnloaded; - if (handler != null) - handler (null, e); - } - - static void OnWorkspaceItemAdded (object s, WorkspaceItemEventArgs args) - { - Load (args.Item); - } - - static void OnWorkspaceItemRemoved (object s, WorkspaceItemEventArgs args) - { - Unload (args.Item); - } - - static void OnSolutionItemAdded (object sender, SolutionItemChangeEventArgs args) - { - var project = args.SolutionItem as Project; - if (project != null) { - var wrapper = LoadProject (project); - if (wrapper != null) { - var files = wrapper.Project.Files.ToArray (); - Task.Run (delegate { - CheckModifiedFiles (wrapper.Project, files, wrapper); - wrapper.RequestLoad (); - }); } } - } - - static void OnSolutionItemRemoved (object sender, SolutionItemChangeEventArgs args) - { - var project = args.SolutionItem as Project; - if (project != null) - UnloadProject (project); - } - - #endregion - - #region Reference Counting - - static readonly Dictionary<Project,int> loadCount = new Dictionary<Project,int> (); - static readonly object rwLock = new object (); - - static int DecLoadCount (Project ob) - { - lock (rwLock) { - int c; - if (loadCount.TryGetValue (ob, out c)) { - c--; - if (c == 0) - loadCount.Remove (ob); - else - loadCount [ob] = c; - return c; - } - LoggingService.LogError ("DecLoadCount: Object not registered."); - return 0; - } - } - - static int IncLoadCount (Project ob) - { - lock (rwLock) { - int c; - if (loadCount.TryGetValue (ob, out c)) { - c++; - loadCount [ob] = c; - return c; - } - loadCount [ob] = 1; - return 1; - } - } - - #endregion - - static bool GetXml (string baseName, TargetRuntime runtime, out FilePath xmlFileName) - { - try { - xmlFileName = LookupLocalizedXmlDoc (baseName); - if (!xmlFileName.IsNull) - return true; - } catch (Exception e) { - LoggingService.LogError ("Error while looking up XML docs.", e); - } - - if (MonoDevelop.Core.Platform.IsWindows) { - string windowsFileName = FindWindowsXmlDocumentation (baseName, runtime); - if (File.Exists (windowsFileName)) { - xmlFileName = windowsFileName; - return true; - } - } - - xmlFileName = ""; - return false; - } - - #region Lookup XML documentation - - // ProgramFilesX86 is broken on 32-bit WinXP, this is a workaround - static string GetProgramFilesX86 () - { - return Environment.GetFolderPath (IntPtr.Size == 8 ? - Environment.SpecialFolder.ProgramFilesX86 : Environment.SpecialFolder.ProgramFiles); - } - - static readonly string referenceAssembliesPath = Path.Combine (GetProgramFilesX86 (), @"Reference Assemblies\Microsoft\\Framework"); - static readonly string frameworkPath = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.Windows), @"Microsoft.NET\Framework"); - - static string FindWindowsXmlDocumentation (string assemblyFileName, TargetRuntime runtime) - { - string fileName; - ClrVersion version = runtime != null && runtime.CustomFrameworks.Any () ? runtime.CustomFrameworks.First ().ClrVersion : ClrVersion.Default; - switch (version) { -// case "1.0": -// fileName = LookupLocalizedXmlDoc (Path.Combine (frameworkPath, "v1.0.3705", assemblyFileName)); -// break; - case ClrVersion.Net_1_1: - fileName = LookupLocalizedXmlDoc (Path.Combine (frameworkPath, "v1.1.4322", assemblyFileName)); - break; - case ClrVersion.Net_2_0: - case ClrVersion.Clr_2_1: - fileName = LookupLocalizedXmlDoc (Path.Combine (frameworkPath, "v2.0.50727", assemblyFileName)) - ?? LookupLocalizedXmlDoc (Path.Combine (referenceAssembliesPath, "v3.5")) - ?? LookupLocalizedXmlDoc (Path.Combine (referenceAssembliesPath, "v3.0")) - ?? LookupLocalizedXmlDoc (Path.Combine (referenceAssembliesPath, @".NETFramework\v3.5\Profile\Client")); - break; - default: - fileName = LookupLocalizedXmlDoc (Path.Combine (referenceAssembliesPath, @".NETFramework\v4.0", assemblyFileName)) - ?? LookupLocalizedXmlDoc (Path.Combine (frameworkPath, "v4.0.30319", assemblyFileName)); - break; - } - return fileName; - } - - static string LookupLocalizedXmlDoc (string fileName) - { - return XmlDocumentationProvider.LookupLocalizedXmlDoc (fileName); - } - - #endregion - - class UnresolvedAssemblyProxy : IUnresolvedAssembly - { - public readonly string FileName; - internal LazyAssemblyLoader CtxLoader; - - public IUnresolvedAssembly Ctx { - get { - return CtxLoader; - } - } - - public bool InLoad { - get { - return CtxLoader == null || CtxLoader.InLoad; - } - } - - public event EventHandler Loaded { - add { - var ctxLoader = CtxLoader; - if (ctxLoader != null) - ctxLoader.Loaded += value; - } - remove { - var ctxLoader = CtxLoader; - if (ctxLoader != null) - ctxLoader.Loaded -= value; - } - } - - public UnresolvedAssemblyProxy (string fileName) - { - if (fileName == null) - throw new ArgumentNullException ("fileName"); - this.FileName = fileName; - } - - #region IUnresolvedAssembly implementation - - string IUnresolvedAssembly.AssemblyName { - get { - return Ctx.AssemblyName; - } - } - - string IUnresolvedAssembly.FullAssemblyName { - get { - return Ctx.FullAssemblyName; - } - } - - string IUnresolvedAssembly.Location { - get { - return Ctx.Location; - } - } - - IEnumerable<IUnresolvedAttribute> IUnresolvedAssembly.AssemblyAttributes { - get { - return Ctx.AssemblyAttributes; - } - } - - IEnumerable<IUnresolvedAttribute> IUnresolvedAssembly.ModuleAttributes { - get { - return Ctx.ModuleAttributes; - } - } - - IEnumerable<IUnresolvedTypeDefinition> IUnresolvedAssembly.TopLevelTypeDefinitions { - get { - return Ctx.TopLevelTypeDefinitions; - } - } - - #endregion - - #region IAssemblyReference implementation - - IAssembly IAssemblyReference.Resolve (ITypeResolveContext context) - { - var ctx = Ctx; - if (ctx == null) - return null; - return ctx.Resolve (context); - } - - #endregion - - public override string ToString () - { - return string.Format ("[UnresolvedAssemblyProxy: FileName={0}]", FileName); - } - } - - internal class LazyAssemblyLoader : IUnresolvedAssembly - { - class LazyAssembly : IAssembly - { - readonly LazyAssemblyLoader loader; - readonly ITypeResolveContext context; - IAssembly assembly; - - IAssembly Assembly { - get { - lock (loader) { - if (assembly == null) { - loader.EnsureAssemblyLoaded (); - assembly = loader.assembly.Resolve (context); - } - return assembly; - } - } - } - - - public LazyAssembly (LazyAssemblyLoader loader, ITypeResolveContext context) - { - this.loader = loader; - this.context = context; - } - - #region IAssembly implementation - - bool IAssembly.InternalsVisibleTo (IAssembly assembly) - { - return Assembly.InternalsVisibleTo (assembly); - } - - ITypeDefinition IAssembly.GetTypeDefinition (TopLevelTypeName typeName) - { - return Assembly.GetTypeDefinition (typeName); - } - - IUnresolvedAssembly IAssembly.UnresolvedAssembly { - get { - return Assembly.UnresolvedAssembly; - } - } - - bool IAssembly.IsMainAssembly { - get { - return Assembly.IsMainAssembly; - } - } - - string IAssembly.AssemblyName { - get { - return Assembly.AssemblyName; - } - } - - string IAssembly.FullAssemblyName { - get { - return Assembly.FullAssemblyName; - } - } - - IList<IAttribute> IAssembly.AssemblyAttributes { - get { - return Assembly.AssemblyAttributes; - } - } - - IList<IAttribute> IAssembly.ModuleAttributes { - get { - return Assembly.ModuleAttributes; - } - } - - INamespace IAssembly.RootNamespace { - get { - return Assembly.RootNamespace; - } - } - - IEnumerable<ITypeDefinition> IAssembly.TopLevelTypeDefinitions { - get { - return Assembly.TopLevelTypeDefinitions; - } - } - - #endregion - - #region ICompilationProvider implementation - - ICompilation ICompilationProvider.Compilation { - get { - return Assembly.Compilation; - } - } - - #endregion - - } - - #region IAssemblyReference implementation - - IAssembly IAssemblyReference.Resolve (ITypeResolveContext context) - { - if (assembly != null) - return assembly.Resolve (context); - return new LazyAssembly (this, context); - } - - #endregion - - #region IUnresolvedAssembly implementation - - readonly object assemblyLock = new object (); - - string IUnresolvedAssembly.AssemblyName { - get { - lock (assemblyLock) { - EnsureAssemblyLoaded (); - return assembly.AssemblyName; - } - } - } - - string IUnresolvedAssembly.FullAssemblyName { - get { - lock (assemblyLock) { - EnsureAssemblyLoaded (); - return assembly.FullAssemblyName; - } - } - } - - string IUnresolvedAssembly.Location { - get { - lock (assemblyLock) { - EnsureAssemblyLoaded (); - return assembly.Location; - } - } - } - - IEnumerable<IUnresolvedAttribute> IUnresolvedAssembly.AssemblyAttributes { - get { - lock (assemblyLock) { - EnsureAssemblyLoaded (); - return assembly.AssemblyAttributes; - } - } - } - - IEnumerable<IUnresolvedAttribute> IUnresolvedAssembly.ModuleAttributes { - get { - lock (assemblyLock) { - EnsureAssemblyLoaded (); - return assembly.ModuleAttributes; - } - } - } - - IEnumerable<IUnresolvedTypeDefinition> IUnresolvedAssembly.TopLevelTypeDefinitions { - get { - lock (assemblyLock) { - EnsureAssemblyLoaded (); - return assembly.TopLevelTypeDefinitions; - } - } - } - - #endregion - - readonly string fileName; - readonly string cache; - IUnresolvedAssembly assembly; - - readonly object asmLocker = new object (); - internal void EnsureAssemblyLoaded () - { - lock (asmLocker) { - if (assembly != null) - return; - var loadedAssembly = LoadAssembly (); - if (loadedAssembly == null) { - LoggingService.LogWarning ("Assembly " + fileName + " could not be loaded cleanly."); - assembly = new DefaultUnresolvedAssembly (fileName); - } else { - assembly = loadedAssembly; - } - - OnLoad (EventArgs.Empty); - } - } - - public override string ToString () - { - return string.Format ("[LazyAssemblyLoader: fileName={0}, assembly={1}]", fileName, assembly); - } - - public bool InLoad { - get { - return assembly == null; - } - } - - public event EventHandler Loaded; - - protected virtual void OnLoad (EventArgs e) - { - var handler = Loaded; - if (handler != null) - handler (this, e); - } - - public LazyAssemblyLoader (string fileName, string cache) - { - this.fileName = fileName; - this.cache = cache; - } - - - IUnresolvedAssembly LoadAssembly () - { - var assemblyPath = cache != null ? Path.Combine (cache, "assembly.data") : null; - var assemblyTag = cache != null ? Path.Combine (cache, "assembly.tag") : null; - try { - if (assemblyPath != null && assemblyTag != null && File.Exists (assemblyPath) && File.Exists (assemblyTag)) { - var deserializedAssembly = DeserializeObject <IUnresolvedAssembly> (assemblyPath); - if (deserializedAssembly != null) { - return deserializedAssembly; - } - } - } catch (Exception) { - } - IUnresolvedAssembly result; - try { - var loader = new IkvmLoader (); - loader.IncludeInternalMembers = true; - loader.DocumentationProvider = new CombinedDocumentationProvider (fileName); - result = loader.LoadAssemblyFile (fileName); - } catch (Exception e) { - LoggingService.LogError ("Can't convert assembly: " + fileName, e); - return null; - } - - if (cache != null) { - var writeTime = File.GetLastWriteTimeUtc (fileName); - SerializeObject (assemblyPath, result); - SerializeObject (assemblyTag, new AssemblyTag (writeTime)); - } - return result; - } - } - - [Serializable] - class CombinedDocumentationProvider : IDocumentationProvider - { - readonly string fileName; - [NonSerialized] - IDocumentationProvider baseProvider; - - public IDocumentationProvider BaseProvider { - get { - if (baseProvider == null) { - FilePath xmlDocFile; - if (GetXml (fileName, null, out xmlDocFile)) { - try { - baseProvider = new XmlDocumentationProvider (xmlDocFile); - } catch (Exception ex) { - LoggingService.LogWarning ("Ignoring error while reading xml doc from " + xmlDocFile, ex); - } - } - if (baseProvider == null) - baseProvider = new MonoDocDocumentationProvider (); - } - return baseProvider; - } - } - - public CombinedDocumentationProvider (string fileName) - { - this.fileName = fileName; - } - - #region IDocumentationProvider implementation - - public DocumentationComment GetDocumentation (IEntity entity) - { - var provider = BaseProvider; - return provider != null ? provider.GetDocumentation (entity) : null; - } - - #endregion - - } - - static readonly object assemblyContextLock = new object (); - - static UnresolvedAssemblyProxy LoadAssemblyContext (FilePath fileName) - { - CanonicalizePath (ref fileName); - - UnresolvedAssemblyProxy loadedContext; - if (cachedAssemblyContents.TryGetValue (fileName, out loadedContext)) { - return loadedContext; - } - if (!File.Exists (fileName)) - return null; - lock (assemblyContextLock) { - if (cachedAssemblyContents.TryGetValue (fileName, out loadedContext)) { - CheckModifiedFile (loadedContext); - return loadedContext; - } - - string cache = GetCacheDirectory (fileName, true); - - try { - var result = new UnresolvedAssemblyProxy (fileName); - result.CtxLoader = new LazyAssemblyLoader (fileName, cache); - CheckModifiedFile (result); - var newcachedAssemblyContents = new Dictionary<string, UnresolvedAssemblyProxy> (cachedAssemblyContents); - newcachedAssemblyContents [fileName] = result; - cachedAssemblyContents = newcachedAssemblyContents; - OnAssemblyLoaded (new AssemblyLoadedEventArgs (result.CtxLoader)); - return result; - } catch (Exception ex) { - LoggingService.LogError ("Error loading assembly " + fileName, ex); - return null; - } + if (!gotDocumentRequestError) { + gotDocumentRequestError = true; + LoggingService.LogWarning ("Can't open requested document : " + analysisDocument + ":" + editor.FileName); } } - internal static event EventHandler<AssemblyLoadedEventArgs> AssemblyLoaded; - - static void OnAssemblyLoaded (AssemblyLoadedEventArgs e) - { - var handler = AssemblyLoaded; - if (handler != null) - handler (null, e); - } - - public static IUnresolvedAssembly LoadAssemblyContext (TargetRuntime runtime, TargetFramework fx, string fileName) + internal static void InformDocumentOpen (Microsoft.CodeAnalysis.Workspace ws, Microsoft.CodeAnalysis.DocumentId analysisDocument, TextEditor editor) { - if (File.Exists (fileName)) - return LoadAssemblyContext (fileName); - var corLibRef = runtime.AssemblyContext.GetAssemblyForVersion (fileName, null, fx); - return corLibRef == null ? null : LoadAssemblyContext (corLibRef.Location); + ((MonoDevelopWorkspace)ws).InformDocumentOpen (analysisDocument, editor); } - public static IProjectContent GetProjectContext (Project project) - { - if (project == null) - throw new ArgumentNullException ("project"); - var content = GetProjectContentWrapper (project); - if (content == null) - return null; - return content.Content; - } + static bool gotDocumentRequestError = false; - public static ICompilation GetCompilation (Project project) + public static Microsoft.CodeAnalysis.ProjectId GetProjectId (MonoDevelop.Projects.Project project) { if (project == null) throw new ArgumentNullException ("project"); - var content = GetProjectContentWrapper (project); - if (content == null) - return null; - return content.Compilation; - } - - public static ICompilation GetCompilation (SystemAssembly assembly, ICompilation compilation) - { - var ctx = LoadAssemblyContext (assembly.Location); - var list = compilation.ReferencedAssemblies.Select (r => r.UnresolvedAssembly).ToList (); - list.Add (compilation.MainAssembly.UnresolvedAssembly); - var result = new SimpleCompilation (ctx, list); - return result; - } - - static IEnumerable<SystemAssembly> GetFrameworkAssemblies (DotNetProject netProject) - { - var assemblies = new Dictionary<string, SystemAssembly> (); - foreach (var assembly in netProject.AssemblyContext.GetAssemblies ()) { - SystemAssembly existing; - if (assemblies.TryGetValue (assembly.Name, out existing)) { - Version v1, v2; - if (!Version.TryParse (existing.Version, out v1)) - continue; - if (!Version.TryParse (assembly.Version, out v2)) - continue; - if (v1 > v2) - continue; + foreach (var w in Workspaces) { + var projectId = w.GetProjectId (project); + if (projectId != null) { + return projectId; } - assemblies [assembly.Name] = assembly; - } - return assemblies.Values; - } - - class FrameworkTask - { - public int RetryCount { get; set; } - - public Task<FrameworkLookup> Task { get; set; } - } - - readonly static Dictionary<string, FrameworkTask> frameworkLookup = new Dictionary<string, FrameworkTask> (); - - static void StartFrameworkLookup (DotNetProject netProject) - { - if (netProject == null) - throw new ArgumentNullException ("netProject"); - lock (frameworkLookup) { - FrameworkTask result; - if (netProject.TargetFramework == null) - return; - var frameworkName = netProject.TargetFramework.Name; - if (!frameworkLookup.TryGetValue (frameworkName, out result)) - frameworkLookup [frameworkName] = result = new FrameworkTask (); - if (result.Task != null) - return; - result.Task = Task.Factory.StartNew (delegate { - return GetFrameworkLookup (netProject); - }); - } - } - - public static bool TryGetFrameworkLookup (DotNetProject project, out FrameworkLookup lookup) - { - lock (frameworkLookup) { - FrameworkTask result; - if (frameworkLookup.TryGetValue (project.TargetFramework.Name, out result)) { - if (!result.Task.IsCompleted) { - lookup = null; - return false; - } - lookup = result.Task.Result; - return true; - } - } - lookup = null; - return false; - } - - public static bool RecreateFrameworkLookup (DotNetProject netProject) - { - lock (frameworkLookup) { - FrameworkTask result; - var frameworkName = netProject.TargetFramework.Name; - if (!frameworkLookup.TryGetValue (frameworkName, out result)) - return false; - if (result.RetryCount > 5) { - LoggingService.LogError ("Can't create framework lookup for:" + frameworkName); - return false; - } - result.RetryCount++; - LoggingService.LogInfo ("Trying to recreate framework lookup for {0}, try {1}.", frameworkName, result.RetryCount); - result.Task = null; - StartFrameworkLookup (netProject); - return true; } + return null; } - static FrameworkLookup GetFrameworkLookup (DotNetProject netProject) + public static Microsoft.CodeAnalysis.Document GetCodeAnysisDocument (Microsoft.CodeAnalysis.DocumentId docId, CancellationToken cancellationToken = default (CancellationToken)) { - FrameworkLookup result; - string fileName; - var cache = GetCacheDirectory (netProject.TargetFramework); - fileName = Path.Combine (cache, "FrameworkLookup_" + FrameworkLookup.CurrentVersion + ".dat"); - try { - if (File.Exists (fileName)) { - result = FrameworkLookup.Load (fileName); - if (result != null) { - return result; - } + if (docId == null) + throw new ArgumentNullException ("docId"); + foreach (var w in Workspaces) { + var documentId = w.GetDocument (docId, cancellationToken); + if (documentId != null) { + return documentId; } - } catch (Exception e) { - LoggingService.LogWarning ("Can't read framework cache - recreating...", e); - } - - try { - using (var creator = FrameworkLookup.Create (fileName)) { - foreach (var assembly in GetFrameworkAssemblies (netProject)) { - var ctx = LoadAssemblyContext (assembly.Location); - foreach (var type in ctx.Ctx.GetAllTypeDefinitions ()) { - if (!type.IsPublic) - continue; - creator.AddLookup (assembly.Package.Name, assembly.FullName, type); - } - } - } - } catch (Exception e) { - LoggingService.LogError ("Error while storing framework lookup", e); - return FrameworkLookup.Empty; - } - - try { - result = FrameworkLookup.Load (fileName); - return result; - } catch (Exception e) { - LoggingService.LogError ("Error loading framework lookup", e); - return FrameworkLookup.Empty; } + return null; } - public static ProjectContentWrapper GetProjectContentWrapper (Project project) + public static MonoDevelop.Projects.Project GetMonoProject (Microsoft.CodeAnalysis.Project project) { if (project == null) throw new ArgumentNullException ("project"); - ProjectContentWrapper content; - if (projectContents.TryGetValue (project, out content)) - return content; - // in case of outdated projects try to get the most recent project wrapper. - foreach (var cnt in projectContents) { - if (cnt.Key.FileName == project.FileName) - return cnt.Value; - } - return null; - } - - public static IProjectContent GetContext (FilePath file, string mimeType, string text) - { - using (var reader = new StringReader (text)) { - var parsedDocument = ParseFile (file, mimeType, reader); - - var content = new CSharpProjectContent (); - return content.AddOrUpdateFiles (parsedDocument.ParsedFile); - } - } - - static Dictionary<string, UnresolvedAssemblyProxy> cachedAssemblyContents = new Dictionary<string, UnresolvedAssemblyProxy> (); - - /// <summary> - /// Force the update of a project context. Note: This method blocks the thread. - /// It was just implemented for use inside unit tests. - /// </summary> - public static void ForceUpdate (ProjectContentWrapper context) - { - CheckModifiedFiles (); - while (!context.IsLoaded) { - Thread.Sleep (10); - } - } - - #region Parser queue - - static bool threadRunning; - - public static IProgressMonitorFactory ParseProgressMonitorFactory { - get; - set; - } - - class InternalProgressMonitor - : ProgressMonitor - { - public InternalProgressMonitor () - { - StartParseOperation (); - } - - public override void Dispose () - { - EndParseOperation (); - } - } - - internal static ProgressMonitor GetParseProgressMonitor () - { - var mon = ParseProgressMonitorFactory != null ? ParseProgressMonitorFactory.CreateProgressMonitor () : new ProgressMonitor (); - - return new AggregatedProgressMonitor (mon, new InternalProgressMonitor ()); - } - - static readonly Queue<ParsingJob> parseQueue = new Queue<ParsingJob> (); - - class ParsingJob - { - public ProjectContentWrapper Context; - public IEnumerable<ProjectFile> FileList; - // public Action<string, IProgressMonitor> ParseCallback; - public void Run (ProgressMonitor monitor, CancellationToken token) - { - TypeSystemParserNode node = null; - TypeSystemParser parser = null; - var tags = Context.GetExtensionObject <ProjectCommentTags> (); - try { - Context.BeginLoadOperation (); - var parsedFiles = new List<Tuple<ParsedDocument, IUnresolvedFile>> (); - foreach (var file in (FileList ?? Context.Project.Files)) { - if (token.IsCancellationRequested) - return; - var fileName = file.FilePath; - if (filesSkippedInParseThread.Any (f => f == fileName)) { - continue; - } - if (node == null || !node.CanParse (fileName, file.BuildAction)) { - var newNode = GetTypeSystemParserNode (DesktopService.GetMimeTypeForUri (fileName), file.BuildAction); - var newParser = newNode != null ? newNode.Parser : null; - if (newParser == null) - continue; - node = newNode; - parser = newParser; - } - - if (parser == null || !File.Exists (fileName)) - continue; - ParsedDocument parsedDocument; - try { - parsedDocument = parser.Parse (false, fileName, Context.Project); - } catch (Exception e) { - LoggingService.LogError ("Error while parsing " + fileName, e); - continue; - } - if (token.IsCancellationRequested) - return; - if (tags != null) - tags.UpdateTags (Context.Project, parsedDocument.FileName, parsedDocument.TagComments); - if (token.IsCancellationRequested) - return; - parsedFiles.Add (Tuple.Create (parsedDocument, Context._content.GetFile (fileName))); - } - Context.UpdateContent (c => c.AddOrUpdateFiles (parsedFiles.Where (f => (f.Item1.Flags & ParsedDocumentFlags.NonSerializable) != ParsedDocumentFlags.NonSerializable).Select (p => p.Item1.ParsedFile))); - foreach (var file in parsedFiles) { - if (token.IsCancellationRequested) - return; - if (file.Item2 != null) - Context.InformFileRemoved (new ParsedFileEventArgs (file.Item2)); - var parsedDocument = file.Item1; - if ((parsedDocument.Flags & ParsedDocumentFlags.NonSerializable) != ParsedDocumentFlags.NonSerializable) - Context.InformFileAdded (new ParsedFileEventArgs (parsedDocument.ParsedFile)); - } - } finally { - Context.EndLoadOperation (); + foreach (var w in Workspaces) { + var documentId = w.GetMonoProject (project); + if (documentId != null) { + return documentId; } } + return null; } - static void UpdateProjectCommentTasks (ProjectContentWrapper context, ParsedDocument parsedDocument) - { - var tags = context.GetExtensionObject <ProjectCommentTags> (); - if (tags != null) // When tags are not there they're updated first time the tasks are requested. - tags.UpdateTags (context.Project, parsedDocument.FileName, parsedDocument.TagComments); - } - // public static event EventHandler<ProjectFileEventArgs> FileParsed; - static readonly object parseQueueLock = new object (); - static readonly AutoResetEvent parseEvent = new AutoResetEvent (false); - static readonly ManualResetEvent queueEmptied = new ManualResetEvent (true); - static bool trackingFileChanges; - - public static bool TrackFileChanges { - get { - return trackingFileChanges; - } - set { - lock (parseQueueLock) { - if (value != trackingFileChanges) { - trackingFileChanges = value; - if (value) - StartParserThread (); - } - } - } - } - - static int parseStatus; - - public static bool IsParsing { - get { return parseStatus > 0; } - } - - static readonly Dictionary<ProjectContentWrapper, ParsingJob> parseQueueIndex = new Dictionary<ProjectContentWrapper, ParsingJob> (); - - internal static int PendingJobCount { - get { - lock (parseQueueLock) { - return parseQueueIndex.Count; - } - } - } - - static void QueueParseJob (ProjectContentWrapper context, IEnumerable<ProjectFile> fileList = null) - { - var job = new ParsingJob { - Context = context, - FileList = fileList - }; - lock (parseQueueLock) { - RemoveParseJob (context); - context.BeginLoadOperation (); - parseQueueIndex [context] = job; - parseQueue.Enqueue (job); - parseEvent.Set (); - - if (parseQueueIndex.Count == 1) - queueEmptied.Reset (); - } - } - - static bool WaitForParseJob (int timeout = 5000) - { - return parseEvent.WaitOne (timeout, true); - } - - static ParsingJob DequeueParseJob () - { - lock (parseQueueLock) { - if (parseQueue.Count > 0) { - var job = parseQueue.Dequeue (); - parseQueueIndex.Remove (job.Context); - return job; - } - return null; - } - } - - internal static void WaitForParseQueue () - { - queueEmptied.WaitOne (); - } - - static void RemoveParseJob (ProjectContentWrapper project) - { - lock (parseQueueLock) { - ParsingJob job; - if (parseQueueIndex.TryGetValue (project, out job)) { - parseQueueIndex.Remove (project); - project.EndLoadOperation (); - } - } - } - - static void StartParserThread () - { - lock (parseQueueLock) { - if (!threadRunning) { - threadRunning = true; - var t = new Thread (new ThreadStart (ParserUpdateThread)); - t.Name = "Background parser"; - t.IsBackground = true; - t.Priority = ThreadPriority.AboveNormal; - t.Start (); - } - } - } - - static void ParserUpdateThread () - { - try { - while (trackingFileChanges) { - WaitForParseJob (); -// CheckModifiedFiles (); - if (trackingFileChanges) - ConsumeParsingQueue (); - } - } catch (Exception ex) { - LoggingService.LogError ("Unhandled error in parsing thread", ex); - } - lock (parseQueueLock) { - threadRunning = false; - if (trackingFileChanges) - StartParserThread (); - } - } - - static bool IsFileModified (ProjectFile file, IUnresolvedFile parsedFile) - { - if (parsedFile == null || !parsedFile.LastWriteTime.HasValue) - return true; - try { - return File.GetLastWriteTimeUtc (file.FilePath) != parsedFile.LastWriteTime; - } catch (Exception) { - return true; - } - } - - static void CheckModifiedFiles (Project project, ProjectFile[] projectFiles, ProjectContentWrapper content, CancellationToken token = default (CancellationToken)) - { - if (token.IsCancellationRequested) { - return; - } -// Console.WriteLine ("add modified file check for :" + project.Name); - content.RunWhenLoaded (delegate(IProjectContent cnt) { - try { -// Console.WriteLine ("check for " + project.Name); - content.BeginLoadOperation (); - var modifiedFiles = new List<ProjectFile> (); - var oldFileNewFile = new List<Tuple<ProjectFile, IUnresolvedFile>> (); - foreach (var file in projectFiles) { - if (token.IsCancellationRequested) { - return; - } - if (file.BuildAction == null) - continue; - // if the file is already inside the content a parser exists for it, if not check if it can be parsed. - var oldFile = cnt.GetFile (file.Name); - oldFileNewFile.Add (Tuple.Create (file, oldFile)); - } - - // This is disk intensive and slow - oldFileNewFile.RemoveAll (t => !IsFileModified (t.Item1, t.Item2)); - - foreach (var v in oldFileNewFile) { - var file = v.Item1; - var oldFile = v.Item2; - if (oldFile == null) { - var parser = TypeSystemService.GetParser (DesktopService.GetMimeTypeForUri (file.Name), file.BuildAction); - if (parser == null) - continue; - } - modifiedFiles.Add (file); - } - var tags = content.GetExtensionObject <ProjectCommentTags> (); - - // check if file needs to be removed from project content - foreach (var file in cnt.Files) { - if (token.IsCancellationRequested) { - return; - } - if (project.GetProjectFile (file.FileName) == null) { - content.UpdateContent (c => c.RemoveFiles (file.FileName)); - content.InformFileRemoved (new ParsedFileEventArgs (file)); - if (tags != null) - tags.RemoveFile (project, file.FileName); - } - } - if (token.IsCancellationRequested) { - return; - } - if (modifiedFiles.Count > 0) { - QueueParseJob (content, modifiedFiles); - WaitForParseJob (); - } - } catch (Exception e) { - LoggingService.LogError ("Exception in check modified files.", e); - } finally { - content.EndLoadOperation (); - } - }); - } - - /// <summary> - /// Used to store meta data information about the assembly. - /// </summary> - [Serializable] - class AssemblyTag - { - public DateTime LastWriteTimeUTC { get; set; } - - public AssemblyTag (DateTime lastWriteTimeUTC) - { - this.LastWriteTimeUTC = lastWriteTimeUTC; - } - } - - static void CheckModifiedFile (UnresolvedAssemblyProxy context) - { - try { - string cache = GetCacheDirectory (context.FileName); - if (cache == null) - return; - var assemblyDataDirectory = Path.Combine (cache, "assembly.tag"); - var writeTime = File.GetLastWriteTimeUtc (context.FileName); - var cacheTime = File.Exists (assemblyDataDirectory) ? DeserializeObject<AssemblyTag> (assemblyDataDirectory) : new AssemblyTag (writeTime); - if (writeTime != cacheTime.LastWriteTimeUTC) { - cache = GetCacheDirectory (context.FileName); - if (cache != null) { - try { - // Files will be reloaded by the lazy loader - File.Delete (assemblyDataDirectory); - File.Delete (Path.Combine (cache, "assembly.data")); - } catch { - } - context.CtxLoader = new LazyAssemblyLoader (context.FileName, cache); - } - } - } catch (Exception e) { - LoggingService.LogError ("Error while updating assembly " + context.FileName, e); - } - } - - static void CheckModifiedFiles () - { - Queue<KeyValuePair<Project, ProjectContentWrapper>> list; - - lock (projectContentLock) { - list = new Queue<KeyValuePair<Project, ProjectContentWrapper>> (projectContents); - } - - while (list.Count > 0) { - var readydb = list.Dequeue (); - var files = readydb.Key.Files.ToArray (); - CheckModifiedFiles (readydb.Key, files, readydb.Value); - } - - var assemblyList = new Queue<KeyValuePair<string, UnresolvedAssemblyProxy>> (cachedAssemblyContents); - - while (assemblyList.Count > 0) { - var readydb = assemblyList.Dequeue (); - CheckModifiedFile (readydb.Value); - } - } - - static void ConsumeParsingQueue () - { - int pending = 0; - ProgressMonitor monitor = null; - var token = loadCancellationSource.Token; - StartParseOperation (); - try { - do { - if (pending > 5 && monitor == null) { - monitor = GetParseProgressMonitor (); - monitor.BeginTask (GettextCatalog.GetString ("Generating database"), 0); - } - var job = DequeueParseJob (); - if (job != null) { - try { - job.Run (monitor, token); - } catch (Exception ex) { - if (monitor == null) - monitor = GetParseProgressMonitor (); - monitor.ReportError (null, ex); - } finally { - job.Context.EndLoadOperation (); - } - } - - if (token.IsCancellationRequested) - break; - pending = PendingJobCount; - } while (pending > 0); - queueEmptied.Set (); - } finally { - if (monitor != null) - monitor.Dispose (); - EndParseOperation (); - } - } - - #endregion - - } - - sealed class AssemblyLoadedEventArgs : EventArgs - { - public readonly TypeSystemService.LazyAssemblyLoader Assembly; - - public AssemblyLoadedEventArgs (TypeSystemService.LazyAssemblyLoader assembly) - { - this.Assembly = assembly; - } - } - - public sealed class ProjectUnloadEventArgs : EventArgs - { - public readonly Project Project; - public readonly TypeSystemService.ProjectContentWrapper Wrapper; - - public ProjectUnloadEventArgs (Project project, TypeSystemService.ProjectContentWrapper wrapper) - { - this.Project = project; - this.Wrapper = wrapper; - } } } - - diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService_WorkspaceHandling.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService_WorkspaceHandling.cs new file mode 100644 index 0000000000..e623dbf213 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService_WorkspaceHandling.cs @@ -0,0 +1,488 @@ +// +// TypeSystemService.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 Microsoft.CodeAnalysis; +using MonoDevelop.Core; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Mono.Addins; +using MonoDevelop.Projects; +using System.IO; +using System.Linq; +using System.Collections.Immutable; +using System.Collections.Concurrent; + +namespace MonoDevelop.Ide.TypeSystem +{ + +// static class MonoDevelopWorkspaceFeatures +// { +// static FeaturePack pack; +// +// public static FeaturePack Features { +// get { +// if (pack == null) +// Interlocked.CompareExchange (ref pack, ComputePack (), null); +// return pack; +// } +// } +// +// static FeaturePack ComputePack () +// { +// var assemblies = new List<Assembly> (); +// var workspaceCoreAssembly = typeof(Workspace).Assembly; +// assemblies.Add (workspaceCoreAssembly); +// +// LoadAssembly (assemblies, "Microsoft.CodeAnalysis.CSharp.Workspaces"); +// //LoadAssembly (assemblies, "Microsoft.CodeAnalysis.VisualBasic.Workspaces"); +// +// var catalogs = assemblies.Select (a => new System.ComponentModel.Composition.Hosting.AssemblyCatalog (a)); +// +// return new MefExportPack (catalogs); +// } +// +// static void LoadAssembly (List<Assembly> assemblies, string assemblyName) +// { +// try { +// var loadedAssembly = Assembly.Load (assemblyName); +// assemblies.Add (loadedAssembly); +// } catch (Exception e) { +// LoggingService.LogWarning ("Couldn't load assembly:" + assemblyName, e); +// } +// } +// } + + public static partial class TypeSystemService + { + static readonly MonoDevelopWorkspace emptyWorkspace; + + static ConcurrentBag<MonoDevelopWorkspace> workspaces = new ConcurrentBag<MonoDevelopWorkspace>(); + + static ImmutableArray<MonoDevelopWorkspace> Workspaces { + get { + return workspaces.ToImmutableArray (); + } + } + public static ImmutableArray<Microsoft.CodeAnalysis.Workspace> AllWorkspaces { + get { + return workspaces.ToImmutableArray<Microsoft.CodeAnalysis.Workspace> (); + } + } + + + internal static MonoDevelopWorkspace GetWorkspace (MonoDevelop.Projects.Solution solution) + { + if (solution == null) + throw new ArgumentNullException ("solution"); + foreach (var ws in Workspaces) { + if (ws.MonoDevelopSolution == solution) + return ws; + } + return emptyWorkspace; + } + + public static Microsoft.CodeAnalysis.Workspace Workspace { + get { + var solution = IdeApp.ProjectOperations?.CurrentSelectedSolution; + if (solution == null) + return emptyWorkspace; + return GetWorkspace (solution); + } + } + + + public static void NotifyFileChange (string fileName, string text) + { + foreach (var ws in Workspaces) + ws.UpdateFileContent (fileName, text); + } + + internal static Microsoft.CodeAnalysis.Workspace Load (WorkspaceItem item, IProgressMonitor progressMonitor, bool loadInBackground = true) + { + using (Counters.ParserService.WorkspaceItemLoaded.BeginTiming ()) { + var workspace = new MonoDevelopWorkspace (); + if (!(item is MonoDevelop.Projects.Workspace)) + workspaces.Add (workspace); + workspace.ShowStatusIcon (); + InternalLoad (item, progressMonitor, workspace, loadInBackground).ContinueWith (t => { + workspace.HideStatusIcon (); + }); + return workspace; + } + } + + static Task InternalLoad (MonoDevelop.Projects.WorkspaceItem item, IProgressMonitor progressMonitor, MonoDevelopWorkspace workspace, bool loadInBackground) + { + var ws = item as MonoDevelop.Projects.Workspace; + if (ws != null) { + Action loadAction = () => { + var newWorkspace = new MonoDevelopWorkspace (); + foreach (var it in ws.Items) + InternalLoad (it, progressMonitor, newWorkspace, false); + workspaces.Add (workspace); + ws.ItemAdded += OnWorkspaceItemAdded; + ws.ItemRemoved += OnWorkspaceItemRemoved; + }; + if (loadInBackground) { + return Task.Run (loadAction); + } else { + loadAction (); + } + } else { + var solution = item as MonoDevelop.Projects.Solution; + if (solution != null) { + Action loadAction = () => { + workspace.TryLoadSolution (solution/*, progressMonitor*/); + solution.SolutionItemAdded += OnSolutionItemAdded; + solution.SolutionItemRemoved += OnSolutionItemRemoved; + }; + if (loadInBackground) { + return Task.Run (loadAction); + } else { + loadAction (); + } + } + } + return Task.FromResult(false); + } + + internal static void Unload (MonoDevelop.Projects.WorkspaceItem item) + { + var ws = item as MonoDevelop.Projects.Workspace; + if (ws != null) { + foreach (var it in ws.Items) + Unload (it); + ws.ItemAdded -= OnWorkspaceItemAdded; + ws.ItemRemoved -= OnWorkspaceItemRemoved; + MonoDocDocumentationProvider.ClearCommentCache (); + } else { + var solution = item as MonoDevelop.Projects.Solution; + if (solution != null) { + MonoDevelopWorkspace result = GetWorkspace (solution); + if (result != emptyWorkspace) { + workspaces = new ConcurrentBag<MonoDevelopWorkspace> (Workspaces.Where (w => w != result)); + result.Dispose (); + } + solution.SolutionItemAdded -= OnSolutionItemAdded; + solution.SolutionItemRemoved -= OnSolutionItemRemoved; + if (solution.ParentWorkspace == null) + MonoDocDocumentationProvider.ClearCommentCache (); + } + } + } + + public static DocumentId GetDocumentId (MonoDevelop.Projects.Project project, string fileName) + { + if (project == null) + throw new ArgumentNullException ("project"); + if (fileName == null) + throw new ArgumentNullException ("fileName"); + fileName = FileService.GetFullPath (fileName); + foreach (var w in Workspaces) { + var projectId = w.GetProjectId (project); + if (projectId != null) + return w.GetDocumentId (projectId, fileName); + } + return null; + } + + public static DocumentId GetDocumentId (Microsoft.CodeAnalysis.Workspace workspace, MonoDevelop.Projects.Project project, string fileName) + { + if (project == null) + throw new ArgumentNullException ("project"); + if (fileName == null) + throw new ArgumentNullException ("fileName"); + fileName = FileService.GetFullPath (fileName); + var projectId = ((MonoDevelopWorkspace)workspace).GetProjectId (project); + if (projectId != null) + return ((MonoDevelopWorkspace)workspace).GetDocumentId (projectId, fileName); + return null; + } + + + public static DocumentId GetDocumentId (ProjectId projectId, string fileName) + { + if (projectId == null) + throw new ArgumentNullException ("projectId"); + if (fileName == null) + throw new ArgumentNullException ("fileName"); + foreach (var w in Workspaces) { + if (w.Contains (projectId)) + return w.GetDocumentId (projectId, fileName); + } + return null; + } + + public static IEnumerable<DocumentId> GetDocuments (string fileName) + { + if (fileName == null) + throw new ArgumentNullException ("fileName"); + fileName = FileService.GetFullPath (fileName); + foreach (var w in Workspaces) { + foreach (var projectId in w.CurrentSolution.ProjectIds) { + var docId = w.GetDocumentId (projectId, fileName); + if (docId != null) + yield return docId; + } + } + } + + public static Microsoft.CodeAnalysis.Project GetCodeAnalysisProject (MonoDevelop.Projects.Project project) + { + if (project == null) + throw new ArgumentNullException ("project"); + foreach (var w in Workspaces) { + var projectId = w.GetProjectId (project); + if (projectId != null) + return w.CurrentSolution.GetProject (projectId); + } + return null; + } + + public static async Task<Compilation> GetCompilationAsync (MonoDevelop.Projects.Project project, CancellationToken cancellationToken = default(CancellationToken)) + { + if (project == null) + throw new ArgumentNullException ("project"); + foreach (var w in Workspaces) { + var projectId = w.GetProjectId (project); + if (projectId == null) + continue; + var roslynProject = w.CurrentSolution.GetProject (projectId); + if (roslynProject == null) + continue; + return await roslynProject.GetCompilationAsync (cancellationToken).ConfigureAwait (false); + } + return null; + } + + static void OnWorkspaceItemAdded (object s, MonoDevelop.Projects.WorkspaceItemEventArgs args) + { + Task.Run (() => TypeSystemService.Load (args.Item, null)); + } + + static void OnWorkspaceItemRemoved (object s, MonoDevelop.Projects.WorkspaceItemEventArgs args) + { + Unload (args.Item); + } + + static void OnSolutionItemAdded (object sender, MonoDevelop.Projects.SolutionItemChangeEventArgs args) + { + var project = args.SolutionItem as MonoDevelop.Projects.Project; + if (project != null) { + var ws = GetWorkspace (project.ParentSolution); + ws.AddProject (project); + } + } + + static void OnSolutionItemRemoved (object sender, MonoDevelop.Projects.SolutionItemChangeEventArgs args) + { + var project = args.SolutionItem as MonoDevelop.Projects.Project; + var solution = sender as MonoDevelop.Projects.Solution; + if (project != null) { + var ws = GetWorkspace (solution); + ws.RemoveProject (project); + } + } + + #region Tracked project handling + static readonly List<string> outputTrackedProjects = new List<string> (); + + static void IntitializeTrackedProjectHandling () + { + AddinManager.AddExtensionNodeHandler ("/MonoDevelop/TypeSystem/OutputTracking", delegate (object sender, ExtensionNodeEventArgs args) { + var projectType = ((TypeSystemOutputTrackingNode)args.ExtensionNode).ProjectType; + switch (args.Change) { + case ExtensionChange.Add: + outputTrackedProjects.Add (projectType); + break; + case ExtensionChange.Remove: + outputTrackedProjects.Remove (projectType); + break; + } + }); + if (IdeApp.ProjectOperations != null) + IdeApp.ProjectOperations.EndBuild += HandleEndBuild; + if (IdeApp.Workspace != null) + IdeApp.Workspace.ActiveConfigurationChanged += HandleActiveConfigurationChanged; + + + } + + static void HandleEndBuild (object sender, BuildEventArgs args) + { + var project = args.SolutionItem as DotNetProject; + if (project == null) + return; + CheckProjectOutput (project, true); + } + + static void HandleActiveConfigurationChanged (object sender, EventArgs e) + { + foreach (var pr in IdeApp.ProjectOperations.CurrentSelectedSolution.GetAllProjects ()) { + var project = pr as DotNetProject; + if (project != null) + CheckProjectOutput (project, true); + } + } + + internal static bool IsOutputTrackedProject (DotNetProject project) + { + if (project == null) + throw new ArgumentNullException ("project"); + return project.GetProjectTypes ().Any (p => outputTrackedProjects.Contains (p, StringComparer.OrdinalIgnoreCase)); + } + + static void CheckProjectOutput (DotNetProject project, bool autoUpdate) + { + if (project == null) + throw new ArgumentNullException ("project"); + if (IsOutputTrackedProject (project)) { + var fileName = project.GetOutputFileName (IdeApp.Workspace.ActiveConfiguration); + if (!File.Exists (fileName)) + return; + FileService.NotifyFileChanged (fileName); + if (autoUpdate) { + // update documents + foreach (var openDocument in IdeApp.Workbench.Documents) { + openDocument.ReparseDocument (); + } + } + } + } + #endregion + +// TODO: Port framework lookup to NR6 +// #region FrameworkLookup +// class FrameworkTask +// { +// public int RetryCount { get; set; } +// +// public Task<FrameworkLookup> Task { get; set; } +// } +// +// readonly static Dictionary<string, FrameworkTask> frameworkLookup = new Dictionary<string, FrameworkTask> (); +// +// static void StartFrameworkLookup (DotNetProject netProject) +// { +// if (netProject == null) +// throw new ArgumentNullException ("netProject"); +// lock (frameworkLookup) { +// FrameworkTask result; +// if (netProject.TargetFramework == null) +// return; +// var frameworkName = netProject.TargetFramework.Name; +// if (!frameworkLookup.TryGetValue (frameworkName, out result)) +// frameworkLookup [frameworkName] = result = new FrameworkTask (); +// if (result.Task != null) +// return; +// result.Task = Task.Factory.StartNew (delegate { +// return GetFrameworkLookup (netProject); +// }); +// } +// } +// +// public static bool TryGetFrameworkLookup (DotNetProject project, out FrameworkLookup lookup) +// { +// lock (frameworkLookup) { +// FrameworkTask result; +// if (frameworkLookup.TryGetValue (project.TargetFramework.Name, out result)) { +// if (!result.Task.IsCompleted) { +// lookup = null; +// return false; +// } +// lookup = result.Task.Result; +// return true; +// } +// } +// lookup = null; +// return false; +// } +// +// public static bool RecreateFrameworkLookup (DotNetProject netProject) +// { +// lock (frameworkLookup) { +// FrameworkTask result; +// var frameworkName = netProject.TargetFramework.Name; +// if (!frameworkLookup.TryGetValue (frameworkName, out result)) +// return false; +// if (result.RetryCount > 5) { +// LoggingService.LogError ("Can't create framework lookup for:" + frameworkName); +// return false; +// } +// result.RetryCount++; +// LoggingService.LogInfo ("Trying to recreate framework lookup for {0}, try {1}.", frameworkName, result.RetryCount); +// result.Task = null; +// StartFrameworkLookup (netProject); +// return true; +// } +// } +// +// static FrameworkLookup GetFrameworkLookup (DotNetProject netProject) +// { +// FrameworkLookup result; +// string fileName; +// var cache = GetCacheDirectory (netProject.TargetFramework); +// fileName = Path.Combine (cache, "FrameworkLookup_" + FrameworkLookup.CurrentVersion + ".dat"); +// try { +// if (File.Exists (fileName)) { +// result = FrameworkLookup.Load (fileName); +// if (result != null) { +// return result; +// } +// } +// } catch (Exception e) { +// LoggingService.LogWarning ("Can't read framework cache - recreating...", e); +// } +// +// try { +// using (var creator = FrameworkLookup.Create (fileName)) { +// foreach (var assembly in GetFrameworkAssemblies (netProject)) { +// var ctx = LoadAssemblyContext (assembly.Location); +// foreach (var type in ctx.Ctx.GetAllTypeDefinitions ()) { +// if (!type.IsPublic) +// continue; +// creator.AddLookup (assembly.Package.Name, assembly.FullName, type); +// } +// } +// } +// } catch (Exception e) { +// LoggingService.LogError ("Error while storing framework lookup", e); +// return FrameworkLookup.Empty; +// } +// +// try { +// result = FrameworkLookup.Load (fileName); +// return result; +// } catch (Exception e) { +// LoggingService.LogError ("Error loading framework lookup", e); +// return FrameworkLookup.Empty; +// } +// } +// #endregion + } + +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Updater/AddinsUpdateHandler.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Updater/AddinsUpdateHandler.cs index a40a755af4..bd5a88814f 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Updater/AddinsUpdateHandler.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Updater/AddinsUpdateHandler.cs @@ -32,8 +32,8 @@ using Mono.Addins.Gui; using MonoDevelop.Ide.ProgressMonitoring; using Mono.Addins; using MonoDevelop.Core.Setup; -using Mono.TextEditor; using System.Threading.Tasks; +using MonoDevelop.Components; namespace MonoDevelop.Ide.Updater { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.WelcomePage/WelcomePageFirstRun.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.WelcomePage/WelcomePageFirstRun.cs index 2f4d880a34..df2f768770 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.WelcomePage/WelcomePageFirstRun.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.WelcomePage/WelcomePageFirstRun.cs @@ -31,7 +31,6 @@ using MonoDevelop.Ide.Gui; using MonoDevelop.Components; using System.Collections.Generic; using Xwt.Motion; -using Mono.TextEditor; namespace MonoDevelop.Ide.WelcomePage { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.WelcomePage/WelcomePageLinkButton.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.WelcomePage/WelcomePageLinkButton.cs index 6fb3f20e73..9fa482fc34 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.WelcomePage/WelcomePageLinkButton.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.WelcomePage/WelcomePageLinkButton.cs @@ -187,7 +187,7 @@ namespace MonoDevelop.Ide.WelcomePage if (uri.StartsWith ("project://")) {
string projectUri = uri.Substring ("project://".Length);
Uri fileuri = new Uri (projectUri);
- Gdk.ModifierType mtype = Mono.TextEditor.GtkWorkarounds.GetCurrentKeyModifiers ();
+ Gdk.ModifierType mtype = GtkWorkarounds.GetCurrentKeyModifiers ();
bool inWorkspace = (mtype & Gdk.ModifierType.ControlMask) != 0;
IdeApp.Workspace.OpenWorkspaceItem (fileuri.LocalPath, !inWorkspace);
} else if (uri.StartsWith ("monodevelop://")) {
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.WelcomePage/WelcomePageListButton.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.WelcomePage/WelcomePageListButton.cs index ec6e00a91a..08cc70f4cb 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.WelcomePage/WelcomePageListButton.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.WelcomePage/WelcomePageListButton.cs @@ -27,7 +27,6 @@ using System; using MonoDevelop.Core; using MonoDevelop.Components; using Gtk; -using Mono.TextEditor; namespace MonoDevelop.Ide.WelcomePage { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.WelcomePage/WelcomePageSection.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.WelcomePage/WelcomePageSection.cs index b5f450b8b9..02401d3679 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.WelcomePage/WelcomePageSection.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.WelcomePage/WelcomePageSection.cs @@ -28,7 +28,6 @@ using Gtk; using System.Xml.Linq; using MonoDevelop.Core; using MonoDevelop.Components; -using Mono.TextEditor; namespace MonoDevelop.Ide.WelcomePage { @@ -129,8 +128,18 @@ namespace MonoDevelop.Ide.WelcomePage if (uri.StartsWith ("project://")) { string projectUri = uri.Substring ("project://".Length); Uri fileuri = new Uri (projectUri); - Gdk.ModifierType mtype = Mono.TextEditor.GtkWorkarounds.GetCurrentKeyModifiers (); + Gdk.ModifierType mtype = GtkWorkarounds.GetCurrentKeyModifiers (); bool inWorkspace = (mtype & Gdk.ModifierType.ControlMask) != 0; + + // Notify the RecentFiles that this item does not exist anymore. + // Possible other solution would be to check the recent projects list on focus in + // and update them accordingly. + if (!System.IO.File.Exists (fileuri.LocalPath)) { + MessageService.ShowError (GettextCatalog.GetString ("File not found {0}", fileuri.LocalPath)); + FileService.NotifyFileRemoved (fileuri.LocalPath); + return; + } + IdeApp.Workspace.OpenWorkspaceItem (fileuri.LocalPath, !inWorkspace); } else if (uri.StartsWith ("monodevelop://")) { var cmdId = uri.Substring ("monodevelop://".Length); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj index 530204b514..c170ba7116 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj @@ -100,10 +100,45 @@ <Reference Include="System.Web" /> <Reference Include="System.Xml.Linq" /> <Reference Include="System.Design" /> + <Reference Include="System.ComponentModel.Composition" /> + <Reference Include="System.Collections.Immutable"> + <HintPath>..\..\..\packages\System.Collections.Immutable.1.1.33-beta\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll</HintPath> + </Reference> + <Reference Include="System.Reflection.Metadata"> + <HintPath>..\..\..\packages\System.Reflection.Metadata.1.0.18-beta\lib\portable-net45+win8\System.Reflection.Metadata.dll</HintPath> + </Reference> <Reference Include="System.Windows.Forms" /> + <Reference Include="Microsoft.CodeAnalysis.Desktop"> + <HintPath>..\..\..\packages\Microsoft.CodeAnalysis.Common.1.0.0-rc1\lib\net45\Microsoft.CodeAnalysis.Desktop.dll</HintPath> + </Reference> + <Reference Include="Microsoft.CodeAnalysis"> + <HintPath>..\..\..\packages\Microsoft.CodeAnalysis.Common.1.0.0-rc1\lib\net45\Microsoft.CodeAnalysis.dll</HintPath> + </Reference> + <Reference Include="System.Runtime.Serialization" /> <Reference Include="Xamarin.Mac" Condition=" '$(Configuration)' == 'DebugMac' Or '$(Configuration)' == 'ReleaseMac' "> <HintPath>..\..\..\external\Xamarin.Mac.dll</HintPath> </Reference> + <Reference Include="Microsoft.CodeAnalysis.Workspaces.Desktop"> + <HintPath>..\..\..\packages\Microsoft.CodeAnalysis.Workspaces.Common.1.0.0-rc1\lib\net45\Microsoft.CodeAnalysis.Workspaces.Desktop.dll</HintPath> + </Reference> + <Reference Include="Microsoft.CodeAnalysis.Workspaces"> + <HintPath>..\..\..\packages\Microsoft.CodeAnalysis.Workspaces.Common.1.0.0-rc1\lib\net45\Microsoft.CodeAnalysis.Workspaces.dll</HintPath> + </Reference> + <Reference Include="System.Composition.AttributedModel"> + <HintPath>..\..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.AttributedModel.dll</HintPath> + </Reference> + <Reference Include="System.Composition.Convention"> + <HintPath>..\..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.Convention.dll</HintPath> + </Reference> + <Reference Include="System.Composition.Hosting"> + <HintPath>..\..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.Hosting.dll</HintPath> + </Reference> + <Reference Include="System.Composition.Runtime"> + <HintPath>..\..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.Runtime.dll</HintPath> + </Reference> + <Reference Include="System.Composition.TypedParts"> + <HintPath>..\..\..\packages\Microsoft.Composition.1.0.27\lib\portable-net45+win8+wp8+wpa81\System.Composition.TypedParts.dll</HintPath> + </Reference> </ItemGroup> <ItemGroup> <ProjectReference Include="..\MonoDevelop.Core\MonoDevelop.Core.csproj"> @@ -111,11 +146,6 @@ <Name>MonoDevelop.Core</Name> <Private>False</Private> </ProjectReference> - <ProjectReference Include="..\Mono.Texteditor\Mono.TextEditor.csproj"> - <Project>{A2329308-3751-4DBD-9A75-5F7B8B024625}</Project> - <Name>Mono.TextEditor</Name> - <Private>False</Private> - </ProjectReference> <ProjectReference Include="..\MonoDevelop.Projects.Formats.MSBuild\MonoDevelop.Projects.Formats.MSBuild.csproj"> <Project>{A437F1A3-78DF-4F00-8053-D32A8B1EB679}</Project> <Name>MonoDevelop.Projects.Formats.MSBuild</Name> @@ -124,12 +154,10 @@ <ProjectReference Include="..\..\..\external\nrefactory\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj"> <Project>{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}</Project> <Name>ICSharpCode.NRefactory</Name> - <Private>False</Private> </ProjectReference> <ProjectReference Include="..\..\..\external\nrefactory\ICSharpCode.NRefactory.CSharp\ICSharpCode.NRefactory.CSharp.csproj"> <Project>{53DCA265-3C3C-42F9-B647-F72BA678122B}</Project> <Name>ICSharpCode.NRefactory.CSharp</Name> - <Private>False</Private> </ProjectReference> <ProjectReference Include="..\..\..\external\xwt\Xwt\Xwt.csproj"> <Project>{92494904-35FA-4DC9-BDE9-3A3E87AC49D3}</Project> @@ -170,6 +198,18 @@ <Project>{4CB170EF-DFE6-4A56-9E1B-A85449E827A7}</Project> <Name>IKVM.Reflection</Name> </ProjectReference> + <ProjectReference Include="..\..\..\external\NRefactory6\ICSharpCode.NRefactory.CSharp\ICSharpCode.NRefactory6.CSharp.csproj"> + <Project>{7E891659-45F3-42B5-B940-A728780CCAE9}</Project> + <Name>ICSharpCode.NRefactory6.CSharp</Name> + </ProjectReference> + <ProjectReference Include="..\..\..\external\NRefactory6\ICSharpCode.NRefactory.CSharp.Refactoring\ICSharpCode.NRefactory6.CSharp.Refactoring.csproj"> + <Project>{C465A5DC-AD28-49A2-89C0-F81838814A7E}</Project> + <Name>ICSharpCode.NRefactory6.CSharp.Refactoring</Name> + </ProjectReference> + <ProjectReference Include="..\..\..\external\nrefactory\ICSharpCode.NRefactory.Cecil\ICSharpCode.NRefactory.Cecil.csproj"> + <Project>{2B8F4F83-C2B3-4E84-A27B-8DEE1BE0E006}</Project> + <Name>ICSharpCode.NRefactory.Cecil</Name> + </ProjectReference> </ItemGroup> <ItemGroup> <EmbeddedResource Include="templates\AppConfigFile.xft.xml"> @@ -913,7 +953,6 @@ <EmbeddedResource Include="icons\tree-popup-button-down%402x.png"> <LogicalName>tree-popup-button-down@2x.png</LogicalName> </EmbeddedResource> - <EmbeddedResource Include="MonoDevelop.Components\SearchEntry.cs" /> <EmbeddedResource Include="icons\clear-all-bookmarks-16.png"> <LogicalName>clear-all-bookmarks-16.png</LogicalName> </EmbeddedResource> @@ -2351,7 +2390,7 @@ </EmbeddedResource> <EmbeddedResource Include="icons\mac\status-pushing-1-16%402x.png"> <LogicalName>status-pushing-1-16@2x.png</LogicalName> - </EmbeddedResource> + </EmbeddedResource> <EmbeddedResource Include="icons\mac\status-pushing-2-16.png"> <LogicalName>status-pushing-2-16.png</LogicalName> </EmbeddedResource> @@ -2540,9 +2579,7 @@ <Compile Include="MonoDevelop.Ide.Gui\ViewCommandHandlers.cs" /> <Compile Include="MonoDevelop.Ide.Gui.Content\IBookmarkBuffer.cs" /> <Compile Include="MonoDevelop.Ide.Gui.Content\IClipboardHandler.cs" /> - <Compile Include="MonoDevelop.Ide.Gui.Content\IEditableTextBuffer.cs" /> <Compile Include="MonoDevelop.Ide.Gui.Content\IPrintable.cs" /> - <Compile Include="MonoDevelop.Ide.Gui.Content\ITextBuffer.cs" /> <Compile Include="MonoDevelop.Ide.Gui.Dialogs\CommonAboutDialog.cs" /> <Compile Include="MonoDevelop.Ide.Gui.Dialogs\DirtyFilesDialog.cs" /> <Compile Include="MonoDevelop.Ide.Gui.Dialogs\NewLayoutDialog.cs" /> @@ -2595,7 +2632,6 @@ <Compile Include="MonoDevelop.Ide.Templates\CodeTranslationFileDescriptionTemplate.cs" /> <Compile Include="MonoDevelop.Ide.Gui.Dialogs\SelectEncodingsDialog.cs" /> <Compile Include="MonoDevelop.Ide.Gui.Dialogs\FileSelectorDialog.cs" /> - <Compile Include="MonoDevelop.Ide.Gui.Content\IEncodedTextContent.cs" /> <Compile Include="MonoDevelop.Ide.Tasks\TaskPriority.cs" /> <Compile Include="MonoDevelop.Ide.Gui.Pads\ErrorListPad.cs" /> <Compile Include="gtk-gui\generated.cs" /> @@ -2604,9 +2640,7 @@ <Compile Include="MonoDevelop.Ide.Tasks\CommentTasksView.cs" /> <Compile Include="MonoDevelop.Ide.Tasks\UserTasksView.cs" /> <Compile Include="MonoDevelop.Ide.Gui\LayoutComboBox.cs" /> - <Compile Include="MonoDevelop.Ide.Gui.Content\IExtensibleTextEditor.cs" /> <Compile Include="MonoDevelop.Ide.Gui.Pads.ProjectPad\UnknownEntryNodeBuilder.cs" /> - <Compile Include="MonoDevelop.Ide.Gui.Content\TextEditorExtension.cs" /> <Compile Include="MonoDevelop.Ide.Commands\NavigationCommands.cs" /> <Compile Include="MonoDevelop.Ide.Templates\ISolutionItemFeature.cs" /> <Compile Include="MonoDevelop.Ide.StandardHeader\StandardHeaderService.cs" /> @@ -2631,14 +2665,15 @@ <Compile Include="MonoDevelop.Ide.Templates\FileTemplateCondition.cs" /> <Compile Include="MonoDevelop.Ide.Templates\PartialTypeFileTemplateCondition.cs" /> <Compile Include="MonoDevelop.Ide.Templates\ParentProjectFileTemplateCondition.cs" /> - <Compile Include="MonoDevelop.Ide.Gui.Content\CompletionTextEditorExtension.cs" /> <Compile Include="MonoDevelop.Ide.Gui\ToolbarComboBox.cs" /> <Compile Include="MonoDevelop.Ide.Gui.Content\ISplittable.cs" /> <Compile Include="MonoDevelop.Ide.Gui.Content\IFoldable.cs" /> <Compile Include="MonoDevelop.Ide.ExternalTools\ExternalToolPanel.cs" /> <Compile Include="gtk-gui\MonoDevelop.Ide.ExternalTools.ExternalToolPanelWidget.cs" /> <Compile Include="MonoDevelop.Ide.Gui\MonoDevelopStatusBar.cs" /> - <Compile Include="MonoDevelop.Ide.Gui\DocumentSwitcher.cs" /> + <Compile Include="MonoDevelop.Ide.Gui\DocumentSwitcher.cs"> + <DependentUpon>GuiSyncContext.cs</DependentUpon> + </Compile> <Compile Include="MonoDevelop.Ide.Gui.OptionPanels\IDEStyleOptionsPanel.cs" /> <Compile Include="gtk-gui\MonoDevelop.Ide.Gui.OptionPanels.IDEStyleOptionsPanelWidget.cs" /> <Compile Include="MonoDevelop.Ide.Gui.Content\IZoomable.cs" /> @@ -2667,7 +2702,6 @@ <Compile Include="MonoDevelop.Ide.Gui.Components\TransactedTreeBuilder.cs" /> <Compile Include="MonoDevelop.Ide.Gui.Content\INavigable.cs" /> <Compile Include="MonoDevelop.Ide.Gui.Content\IOpenNamedElementHandler.cs" /> - <Compile Include="MonoDevelop.Ide.Gui.Content\ISmartIndenter.cs" /> <Compile Include="AssemblyInfo.cs" /> <Compile Include="MonoDevelop.Ide.Gui.Content\ITextEditorResolver.cs" /> <Compile Include="MonoDevelop.Ide.Gui.OptionPanels\AuthorInformationPanel.cs" /> @@ -2741,7 +2775,9 @@ <Compile Include="MonoDevelop.Ide.Extensions\IOpenFileDialogHandler.cs" /> <Compile Include="MonoDevelop.Ide.Extensions\IAddFileDialogHandler.cs" /> <Compile Include="MonoDevelop.Ide.Extensions\TextEditorExtensionNode.cs" /> - <Compile Include="MonoDevelop.Ide.Gui\DockItemToolbarLoader.cs" /> + <Compile Include="MonoDevelop.Ide.Gui\DockItemToolbarLoader.cs"> + <DependentUpon>DisplayBindingService.cs</DependentUpon> + </Compile> <Compile Include="MonoDevelop.Ide.Gui.Components\LogView.cs" /> <Compile Include="MonoDevelop.Ide.Gui\WorkbenchContext.cs" /> <Compile Include="MonoDevelop.Ide.Extensions\LayoutExtensionNode.cs" /> @@ -2867,7 +2903,6 @@ <Compile Include="MonoDevelop.Components.PropertyGrid.Editors\FlagsSelectorDialog.cs" /> <Compile Include="MonoDevelop.Components.PropertyGrid.Editors\FloatRange.cs" /> <Compile Include="MonoDevelop.Components.PropertyGrid.Editors\IntRange.cs" /> - <Compile Include="MonoDevelop.Components.PropertyGrid.Editors\TextEditor.cs" /> <Compile Include="MonoDevelop.Components.PropertyGrid.Editors\TextEditorDialog.cs" /> <Compile Include="MonoDevelop.Components.PropertyGrid.Editors\TimeSpanEditor.cs" /> <Compile Include="MonoDevelop.Components.PropertyGrid.Editors\BooleanEditorCell.cs" /> @@ -3137,7 +3172,6 @@ <Compile Include="MonoDevelop.Ide.WelcomePage\WelcomePageRecentProjectsList.cs" /> <Compile Include="MonoDevelop.Ide.WelcomePage\WelcomePageLinksList.cs" /> <Compile Include="MonoDevelop.Ide.Gui\ProjectLoadProgressMonitor.cs" /> - <Compile Include="MonoDevelop.Ide.Gui\CommonTextEditorOptions.cs" /> <Compile Include="MonoDevelop.Components\FileFilterSet.cs" /> <Compile Include="MonoDevelop.Ide\IdeVersionInfo.cs" /> <Compile Include="MonoDevelop.Components\ContextMenuTreeView.cs" /> @@ -3150,22 +3184,14 @@ <Compile Include="MonoDevelop.Ide\ProjectCreatedEventArgs.cs" /> <Compile Include="MonoDevelop.Ide.FindInFiles\MemberCollector.cs" /> <Compile Include="MonoDevelop.Ide.FindInFiles\SearchCollector.cs" /> - <Compile Include="MonoDevelop.Ide.TypeSystem\ProjectContentEventArgs.cs" /> <Compile Include="MonoDevelop.Ide.TypeSystem\TypeSystemService.cs" /> <Compile Include="MonoDevelop.Ide.TypeSystem\Ambience.cs" /> - <Compile Include="MonoDevelop.Ide.TypeSystem\AmbienceService.cs" /> - <Compile Include="MonoDevelop.Ide.TypeSystem\NetAmbience.cs" /> - <Compile Include="MonoDevelop.Ide.TypeSystem\OutputFlags.cs" /> - <Compile Include="MonoDevelop.Ide.TypeSystem\OutputSettings.cs" /> <Compile Include="MonoDevelop.Ide.TypeSystem\StockIcons.cs" /> <Compile Include="MonoDevelop.Ide.TypeSystem\FoldingRegion.cs" /> <Compile Include="MonoDevelop.Ide.TypeSystem\ParsedDocument.cs" /> <Compile Include="MonoDevelop.Ide.TypeSystem\Comment.cs" /> <Compile Include="MonoDevelop.Ide.TypeSystem\Tag.cs" /> - <Compile Include="MonoDevelop.Ide.TypeSystem\PreProcessorDefine.cs" /> - <Compile Include="MonoDevelop.Ide.TypeSystem\ConditionalRegion.cs" /> <Compile Include="MonoDevelop.Ide.TypeSystem\MarkupUtilities.cs" /> - <Compile Include="MonoDevelop.Ide.TypeSystem\CodeGenerationService.cs" /> <Compile Include="MonoDevelop.Ide.TypeSystem\CodeGenerator.cs" /> <Compile Include="MonoDevelop.Ide.TypeSystem\TypeSystemParserNode.cs" /> <Compile Include="MonoDevelop.Ide.TypeSystem\MonoDocDocumentationProvider.cs" /> @@ -3177,7 +3203,6 @@ <Compile Include="MonoDevelop.Components.MainToolbar\StatusArea.cs" /> <Compile Include="MonoDevelop.Components.MainToolbar\RoundButton.cs" /> <Compile Include="MonoDevelop.Components.MainToolbar\ButtonBar.cs" /> - <Compile Include="MonoDevelop.Ide.TypeSystem\ProjectCommentTags.cs" /> <Compile Include="MonoDevelop.Components.MainToolbar\ProjectSearchCategory.cs" /> <Compile Include="MonoDevelop.Components.MainToolbar\SearchCategory.cs" /> <Compile Include="MonoDevelop.Components.MainToolbar\ISearchDataSource.cs" /> @@ -3191,7 +3216,6 @@ <Compile Include="MonoDevelop.Components\TooltipPopoverWindow.cs" /> <Compile Include="MonoDevelop.Ide.Gui.Components\AnimatedIcon.cs" /> <Compile Include="MonoDevelop.Ide\DefaultWebCertificateProvider.cs" /> - <Compile Include="MonoDevelop.Ide.TypeSystem\ParsedFileEventArgs.cs" /> <Compile Include="MonoDevelop.Ide.Extensions\MimeTypeExtensionNode.cs" /> <Compile Include="MonoDevelop.Ide.Gui\DocumentView.cs" /> <Compile Include="MonoDevelop.Components\VPanedThin.cs" /> @@ -3200,7 +3224,6 @@ <Compile Include="MonoDevelop.Components\ExtendedLabel.cs" /> <Compile Include="MonoDevelop.Ide.CodeCompletion\TooltipInformation.cs" /> <Compile Include="MonoDevelop.Ide.CodeCompletion\TooltipInformationWindow.cs" /> - <Compile Include="MonoDevelop.Ide.CodeCompletion\ParameterDataProvider.cs" /> <Compile Include="MonoDevelop.Ide.TypeSystem\TypeSystemParser.cs" /> <Compile Include="MonoDevelop.Ide.WelcomePage\WelcomePageSection.cs" /> <Compile Include="MonoDevelop.Ide.WelcomePage\Style.cs" /> @@ -3220,7 +3243,6 @@ <Compile Include="MonoDevelop.Ide.TextEditing\TopLevelWidgetExtension.cs" /> <Compile Include="MonoDevelop.Ide.TextEditing\VerticalAlignment.cs" /> <Compile Include="MonoDevelop.Ide.TextEditing\HorizontalAlignment.cs" /> - <Compile Include="MonoDevelop.Ide.TextEditing\TextLineMarkerExtension.cs" /> <Compile Include="MonoDevelop.Ide.TextEditing\TextFileEventArgs.cs" /> <Compile Include="MonoDevelop.Ide.TextEditing\LineCountEventArgs.cs" /> <Compile Include="MonoDevelop.Ide.TextEditing\FileExtension.cs" /> @@ -3247,7 +3269,6 @@ <Compile Include="MonoDevelop.Ide.Gui\StatusBarIcon.cs" /> <Compile Include="MonoDevelop.Ide.Gui\StatusBarContextImpl.cs" /> <Compile Include="MonoDevelop.Components.MainToolbar\MainStatusBarContextImpl.cs" /> - <Compile Include="MonoDevelop.Ide.TypeSystem\MonoDevelopProjectContent.cs" /> <Compile Include="MonoDevelop.Components.Commands\ICommandDelegator.cs" /> <Compile Include="MonoDevelop.Ide.CustomTools\ResXFileCodeGenerator.cs" /> <Compile Include="MonoDevelop.Components\CellRendererImage.cs" /> @@ -3256,7 +3277,6 @@ <Compile Include="AddinInfo.cs" /> <Compile Include="MonoDevelop.Ide.CodeCompletion\CompletionCharacters.cs" /> <Compile Include="MonoDevelop.Ide.CodeCompletion\CompletionCharacterCodon.cs" /> - <Compile Include="MonoDevelop.Ide.TypeSystem\IRefactoringContext.cs" /> <Compile Include="MonoDevelop.Ide.Gui.Pads.ProjectPad\ImplicitFrameworkAssemblyReferenceNodeBuilder.cs" /> <Compile Include="MonoDevelop.Ide.Gui.Pads.ProjectPad\PortableFrameworkSubsetNodeBuilder.cs" /> <Compile Include="MonoDevelop.Components.MainToolbar\SearchInSolutionSearchCategory.cs" /> @@ -3273,6 +3293,57 @@ <Compile Include="MonoDevelop.Ide.Templates\WorkspaceItemCreatedInformation.cs" /> <Compile Include="MonoDevelop.Ide.Templates\PackageReferencesForCreatedProject.cs" /> <Compile Include="MonoDevelop.Ide.Templates\ProjectTemplatePackageInstaller.cs" /> + <Compile Include="MonoDevelop.Ide.TypeSystem\MonoDevelopTextLoader.cs" /> + <Compile Include="MonoDevelop.Ide.TypeSystem\MonoDevelopSourceTextContainer.cs" /> + <Compile Include="MonoDevelop.Ide.CustomTools\PublicResXFileCodeGenerator.cs" /> + <Compile Include="MonoDevelop.Components\ImageLoader.cs" /> + <Compile Include="MonoDevelop.Ide.CodeCompletion\ParameterHintingData.cs" /> + <Compile Include="MonoDevelop.Ide.TypeSystem\MonoDevelopWorkspace.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\IDocumentLine.cs" /> + <Compile Include="MonoDevelop.Ide.CodeTemplates\IListDataProvider.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\ITextEditorOptions.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\WordFindStrategy.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\InsertionPoint.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\TextLink.cs"> + <DependentUpon>TextLinkModeEventArgs.cs</DependentUpon> + </Compile> + <Compile Include="MonoDevelop.Components\WindowTransparencyDecorator.cs" /> + <Compile Include="MonoDevelop.Components\GtkWorkarounds.cs" /> + <Compile Include="MonoDevelop.Components\PangoUtil.cs" /> + <Compile Include="MonoDevelop.Components\HslColor.cs" /> + <Compile Include="MonoDevelop.Components\HelperMethods.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\SelectionMode.cs" /> + <Compile Include="MonoDevelop.Components\GtkGestures.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\FileSettingsStore.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\InsertionCursorEventArgs.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\IFoldSegment.cs" /> + <Compile Include="MonoDevelop.Ide.TextEditing\TextLineMarkerExtension.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\ITextLineMarker.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\TooltipProvider.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\ITextSegmentMarker.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\DocumentRegion.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\TextMarkerMouseEventArgs.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\DefaultSourceEditorOptions.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\TextEditor.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\ITextDocument.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\TextEditorDisplayBinding.cs" /> + <Compile Include="MonoDevelop.Ide.Editor.Extension\AbstractUsagesExtension.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\TextEditorViewContent.cs" /> + <Compile Include="MonoDevelop.Ide.Editor.Extension\IndentationTracker.cs" /> + <Compile Include="MonoDevelop.Ide.Editor.Extension\SelectionSurroundingProvider.cs" /> + <Compile Include="MonoDevelop.Ide.Editor.Extension\TextPasteHandler.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\DocumentLocation.cs" /> + <Compile Include="MonoDevelop.Ide.Editor.Highlighting\SyntaxModeService.cs" /> + <Compile Include="MonoDevelop.Ide.Editor.Highlighting\AmbientColor.cs" /> + <Compile Include="MonoDevelop.Ide.Editor.Highlighting\ColorScheme.cs" /> + <Compile Include="MonoDevelop.Ide.Editor.Highlighting\ColorDescriptionAttribute.cs" /> + <Compile Include="MonoDevelop.Ide.Editor.Highlighting\TemplateCodon.cs" /> + <Compile Include="MonoDevelop.Ide.Editor.Highlighting\IStreamProvider.cs" /> + <Compile Include="MonoDevelop.Ide.Editor.Highlighting\TemplateExtensionNodeLoader.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\TextLinkModeEventArgs.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\InsertionModeOptions.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\TextLinkModeOptions.cs" /> + <Compile Include="MonoDevelop.Ide.Editor.Extension\CompletionTextEditorExtension.cs" /> <Compile Include="MonoDevelop.Components.DockNotebook\DockNotebook.cs" /> <Compile Include="MonoDevelop.Components.DockNotebook\PlaceholderWindow.cs" /> <Compile Include="MonoDevelop.Components.DockNotebook\TabStrip.cs" /> @@ -3289,12 +3360,25 @@ <Compile Include="MonoDevelop.Components\ContextMenuExtensionsMac.cs" /> <Compile Include="MonoDevelop.Components\ContextMenuExtensionsGtk.cs" /> <Compile Include="MonoDevelop.Ide.Gui\Split.cs" /> - <Compile Include="MonoDevelop.Ide.CustomTools\PublicResXFileCodeGenerator.cs" /> - <Compile Include="MonoDevelop.Components\ImageLoader.cs" /> <Compile Include="MonoDevelop.Components.DockNotebook\DockWindow.cs" /> <Compile Include="MonoDevelop.Ide.CustomTools\MSBuildCustomTool.cs" /> <Compile Include="MonoDevelop.Components\Mac\GtkMacInterop.cs" /> <Compile Include="MonoDevelop.Components\Control.cs" /> + <Compile Include="MonoDevelop.Ide.Editor.Extension\TextEditorExtension.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\IUnitTestMarker.cs" /> + <Compile Include="MonoDevelop.Ide.Editor.Highlighting\ChunkStyle.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\TextMarkerFactory.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\InternalExtensionAPI\ITextEditorImpl.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\InternalExtensionAPI\ITextMarkerFactory.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\InternalExtensionAPI\IEditorActionHost.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\EditActions.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\DocumentContext.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\InternalExtensionAPI\ITextEditorFactory.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\FoldSegmentFactory.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\IReadonlyTextDocument.cs" /> + <Compile Include="MonoDevelop.Ide.Editor.Extension\Usage.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\TextEditorFactory.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\AutoSave.cs" /> <Compile Include="MonoDevelop.Ide.Templates\SolutionTemplate.cs" /> <Compile Include="MonoDevelop.Ide.Templates\TemplateCategory.cs" /> <Compile Include="MonoDevelop.Ide.Templates\TemplateWizard.cs" /> @@ -3339,7 +3423,38 @@ <Compile Include="MonoDevelop.Components\Mac\NSViewContainer.cs" /> <Compile Include="MonoDevelop.Components\Mac\GtkEmbed.cs" /> <Compile Include="MonoDevelop.Components\Mac\WidgetWithNativeWindow.cs" /> - <Compile Include="MonoDevelop.Ide.Tasks\UserTask.cs" /> + <Compile Include="MonoDevelop.Ide.Editor.Extension\IQuickTaskProvider.cs" /> + <Compile Include="MonoDevelop.Ide.Editor.Extension\QuickTask.cs" /> + <Compile Include="MonoDevelop.Ide.Editor.Highlighting\SemanticHighlighting.cs" /> + <Compile Include="MonoDevelop.Ide.Editor.Highlighting\ColoredSegment.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\SegmentTree.cs" /> + <Compile Include="MonoDevelop.Ide.Editor.Util\Diff.cs" /> + <Compile Include="MonoDevelop.Ide.Editor.Util\SimpleBracketMatcher.cs" /> + <Compile Include="MonoDevelop.Ide.Editor.Util\SimpleReadonlyDocument.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\Commands\DynamicAbbrevHandler.cs" /> + <Compile Include="MonoDevelop.Ide.Editor.Extension\ModifierKeys.cs" /> + <Compile Include="MonoDevelop.Ide.Editor.Extension\SpecialKey.cs" /> + <Compile Include="MonoDevelop.Ide.Editor.Extension\KeyDescriptor.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\MessageBubbles\MessageBubbleCommands.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\LineEventArgs.cs" /> + <Compile Include="MonoDevelop.Ide.TypeSystem\Error.cs" /> + <Compile Include="MonoDevelop.Ide.TypeSystem\MetadataReferenceCache.cs" /> + <Compile Include="MonoDevelop.Ide.TypeSystem\IFoldingParser.cs" /> + <Compile Include="MonoDevelop.Ide.TypeSystem\TypeSystemService_WorkspaceHandling.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\Projection\ProjectedSegment.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\Projection\Projection.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\Projection\ProjectedSemanticHighlighting.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\Projection\ProjectedTooltipProvider.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\Projection\ProjectedCompletionExtension.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\Projection\ProjectedDocumentContext.cs" /> + <Compile Include="MonoDevelop.Ide.CodeCompletion\CompletionCategory.cs" /> + <Compile Include="MonoDevelop.Ide.CodeCompletion\DisplayFlags.cs" /> + <Compile Include="MonoDevelop.Ide.CodeCompletion\ParameterHintingResult.cs" /> + <Compile Include="MonoDevelop.Ide.Tasks\TaskListEntry.cs" /> + <Compile Include="MonoDevelop.Ide.TypeSystem\AmbienceTooltipProvider.cs" /> + <Compile Include="MonoDevelop.Components.PropertyGrid.Editors\PropertyTextEditor.cs" /> + <Compile Include="MonoDevelop.Ide.Tasks\ProjectCommentTags.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\CustomEditorOptions.cs" /> <Compile Include="MonoDevelop.Ide.Projects\NewProjectConfiguration.cs" /> <Compile Include="MonoDevelop.Components\EventBoxTooltip.cs" /> <Compile Include="MonoDevelop.Ide.Templates\ProjectTemplateCreateInformation.cs" /> @@ -3352,6 +3467,10 @@ <Compile Include="MonoDevelop.Components.MainToolbar\StatusBarContextHandler.cs" /> <Compile Include="MonoDevelop.Components.MainToolbar\IButtonBarButton.cs" /> <Compile Include="MonoDevelop.Components.MainToolbar\MainToolbarModels.cs" /> + <Compile Include="MonoDevelop.Ide.CodeCompletion\MruCache.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\TooltipExtensionNode.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\Projection\IProjectionExtension.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\Projection\ProjectedFilterCompletionTextEditorExtension.cs" /> </ItemGroup> <ItemGroup> <None Include="Makefile.am" /> @@ -3363,6 +3482,7 @@ <None Include="MonoDevelop.Ide.dll.config"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> + <None Include="packages.config" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Target Name="AfterBuild"> @@ -3395,6 +3515,10 @@ <Folder Include="MonoDevelop.Ide.WelcomePage\icons\" /> <Folder Include="MonoDevelop.Ide.TextEditing\" /> <Folder Include="MonoDevelop.Components.DockNotebook\" /> + <Folder Include="MonoDevelop.Ide.Editor.Util\" /> + <Folder Include="MonoDevelop.Ide.Editor\Commands\" /> + <Folder Include="MonoDevelop.Ide.Editor\MessageBubbles\" /> + <Folder Include="MonoDevelop.Ide.Editor\Projection\" /> <Folder Include="icons\mac\" /> </ItemGroup> <ItemGroup> diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/DesktopService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/DesktopService.cs index b8cba9ec3e..8455c21735 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/DesktopService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/DesktopService.cs @@ -166,14 +166,7 @@ namespace MonoDevelop.Ide if (!File.Exists (file)) return false; - using (var f = File.OpenRead (file)) { - var buf = new byte[8192]; - var read = f.Read (buf, 0, buf.Length); - for (int i = 0; i < read; i++) - if (buf [i] == 0) - return false; - } - return true; + return !MonoDevelop.Core.Text.TextFileUtility.IsBinary (file); } public async static Task<bool> GetFileIsTextAsync (string file, string mimeType = null) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/HelpOperations.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/HelpOperations.cs index 166415e254..df86d6de4c 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/HelpOperations.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/HelpOperations.cs @@ -35,7 +35,6 @@ using MonoDevelop.Core.Execution; using System.IO; using MonoDevelop.Core; using MonoDevelop.Projects; -using ICSharpCode.NRefactory.Semantics; namespace MonoDevelop.Ide { @@ -227,14 +226,14 @@ namespace MonoDevelop.Ide } } - public bool CanShowHelp (ResolveResult result) - { - try { - return CanShowHelp (HelpService.GetMonoDocHelpUrl (result)); - } catch (Exception e) { - LoggingService.LogError ("Error while trying to get monodoc help.", e); - return false; - } - } +// public bool CanShowHelp (ResolveResult result) +// { +// try { +// return CanShowHelp (HelpService.GetMonoDocHelpUrl (result)); +// } catch (Exception e) { +// LoggingService.LogError ("Error while trying to get monodoc help.", e); +// return false; +// } +// } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Ide.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Ide.cs index deeb48a636..79f510c135 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Ide.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Ide.cs @@ -223,7 +223,6 @@ namespace MonoDevelop.Ide // Perser service initialization TypeSystemService.TrackFileChanges = true; - TypeSystemService.ParseProgressMonitorFactory = new ParseProgressMonitorFactory (); Customizer.OnIdeInitialized (); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartup.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartup.cs index 1fd7b88e78..d096632d8e 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartup.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartup.cs @@ -53,6 +53,7 @@ using MonoDevelop.Ide.Extensions; using MonoDevelop.Components.Extensions; using MonoDevelop.Ide.Desktop; using System.Threading.Tasks; +using MonoDevelop.Components; namespace MonoDevelop.Ide { @@ -117,11 +118,11 @@ namespace MonoDevelop.Ide Xwt.Toolkit.CurrentEngine.RegisterBackend<IExtendedTitleBarDialogBackend,GtkExtendedTitleBarDialogBackend> (); //default to Windows IME on Windows - if (Platform.IsWindows && Mono.TextEditor.GtkWorkarounds.GtkMinorVersion >= 16) { + if (Platform.IsWindows && GtkWorkarounds.GtkMinorVersion >= 16) { var settings = Gtk.Settings.Default; - var val = Mono.TextEditor.GtkWorkarounds.GetProperty (settings, "gtk-im-module"); + var val = GtkWorkarounds.GetProperty (settings, "gtk-im-module"); if (string.IsNullOrEmpty (val.Val as string)) - Mono.TextEditor.GtkWorkarounds.SetProperty (settings, "gtk-im-module", new GLib.Value ("ime")); + GtkWorkarounds.SetProperty (settings, "gtk-im-module", new GLib.Value ("ime")); } InternalLog.Initialize (); @@ -610,6 +611,14 @@ namespace MonoDevelop.Ide static void HandleException (Exception ex, bool willShutdown) { var msg = String.Format ("An unhandled exception has occured. Terminating {0}? {1}", BrandingService.ApplicationName, willShutdown); + var aggregateException = ex as AggregateException; + if (aggregateException != null) { + aggregateException.Flatten ().Handle (innerEx => { + HandleException (innerEx, willShutdown); + return true; + }); + return; + } if (willShutdown) LoggingService.LogFatalError (msg, ex); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ImageService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ImageService.cs index 60e64618c6..2cff1a6d5a 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ImageService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ImageService.cs @@ -212,12 +212,12 @@ namespace MonoDevelop.Ide static Gdk.Pixbuf Get2xIconVariant (Gdk.Pixbuf px) { - return Mono.TextEditor.GtkWorkarounds.Get2xVariant (px); + return GtkWorkarounds.Get2xVariant (px); } static void Set2xIconVariant (Gdk.Pixbuf px, Gdk.Pixbuf variant2x) { - Mono.TextEditor.GtkWorkarounds.Set2xVariant (px, variant2x); + GtkWorkarounds.Set2xVariant (px, variant2x); } static Dictionary<string,Xwt.Drawing.Image> icons = new Dictionary<string, Xwt.Drawing.Image> (); @@ -489,7 +489,7 @@ namespace MonoDevelop.Ide Gtk.IconSource source2x = null; if (Platform.IsWindows) { - var pixel_scale = Mono.TextEditor.GtkWorkarounds.GetPixelScale (); + var pixel_scale = GtkWorkarounds.GetPixelScale (); source.Pixbuf = pixbuf.ScaleSimple ((int)(pixbuf.Width * pixel_scale), (int)(pixbuf.Height * pixel_scale), Gdk.InterpType.Bilinear); } else { source.Pixbuf = pixbuf; @@ -499,17 +499,17 @@ namespace MonoDevelop.Ide source.SizeWildcarded = iconSize == Gtk.IconSize.Invalid; if (pixbuf2x != null) { - if (Mono.TextEditor.GtkWorkarounds.SetSourceScale (source, 1)) { - Mono.TextEditor.GtkWorkarounds.SetSourceScaleWildcarded (source, false); + if (GtkWorkarounds.SetSourceScale (source, 1)) { + GtkWorkarounds.SetSourceScaleWildcarded (source, false); source2x = new Gtk.IconSource (); source2x.Pixbuf = pixbuf2x; source2x.Size = iconSize; source2x.SizeWildcarded = iconSize == Gtk.IconSize.Invalid; - Mono.TextEditor.GtkWorkarounds.SetSourceScale (source2x, 2); - Mono.TextEditor.GtkWorkarounds.SetSourceScaleWildcarded (source2x, false); + GtkWorkarounds.SetSourceScale (source2x, 2); + GtkWorkarounds.SetSourceScaleWildcarded (source2x, false); } } else { - Mono.TextEditor.GtkWorkarounds.SetSourceScaleWildcarded (source, true); + GtkWorkarounds.SetSourceScaleWildcarded (source, true); } iconSet.AddSource (source); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/MessageService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/MessageService.cs index ea4319b4bc..b8d750f1f1 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/MessageService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/MessageService.cs @@ -30,6 +30,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading; +using MonoDevelop.Components; using Gtk; using MonoDevelop.Core; using MonoDevelop.Components.Extensions; @@ -374,7 +375,7 @@ namespace MonoDevelop.Ide else PlaceDialog (dialog, parent); #endif - return Mono.TextEditor.GtkWorkarounds.RunDialogWithNotification (dialog); + return GtkWorkarounds.RunDialogWithNotification (dialog); } #if MAC diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ProjectOperations.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ProjectOperations.cs index 660423cc7c..4caf1b5021 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ProjectOperations.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ProjectOperations.cs @@ -45,18 +45,17 @@ using MonoDevelop.Ide.Gui; using MonoDevelop.Ide.Projects; using MonoDevelop.Core.Assemblies; using MonoDevelop.Core.Instrumentation; -using Mono.TextEditor; using System.Diagnostics; -using ICSharpCode.NRefactory.Documentation; -using ICSharpCode.NRefactory.TypeSystem.Implementation; using System.Text; using MonoDevelop.Ide.TypeSystem; -using ICSharpCode.NRefactory.TypeSystem; using System.Threading.Tasks; using System.Threading; using ExecutionContext = MonoDevelop.Projects.ExecutionContext; using MonoDevelop.Ide.Tasks; using MonoDevelop.Projects.Formats.MSBuild; +using System.Collections.Immutable; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Core.Text; namespace MonoDevelop.Ide { @@ -231,31 +230,33 @@ namespace MonoDevelop.Ide return (GetDeclaredFile(item) != null); }*/ - public bool CanJumpToDeclaration (object element) + public bool CanJumpToDeclaration (Microsoft.CodeAnalysis.ISymbol symbol) { - if (element is ICSharpCode.NRefactory.TypeSystem.IVariable) - return true; - var entity = element as ICSharpCode.NRefactory.TypeSystem.IEntity; - if (entity == null && element is ICSharpCode.NRefactory.TypeSystem.IType) - entity = ((ICSharpCode.NRefactory.TypeSystem.IType)element).GetDefinition (); - if (entity == null) + if (symbol == null) return false; - if (entity.Region.IsEmpty) { - var parentAssembly = entity.ParentAssembly; - if (parentAssembly == null) - return false; - return !string.IsNullOrEmpty (parentAssembly.UnresolvedAssembly.Location); + switch (symbol.Kind) { + case Microsoft.CodeAnalysis.SymbolKind.Local: + case Microsoft.CodeAnalysis.SymbolKind.Parameter: + case Microsoft.CodeAnalysis.SymbolKind.NamedType: + case Microsoft.CodeAnalysis.SymbolKind.Method: + case Microsoft.CodeAnalysis.SymbolKind.Field: + case Microsoft.CodeAnalysis.SymbolKind.Property: + case Microsoft.CodeAnalysis.SymbolKind.Event: + case Microsoft.CodeAnalysis.SymbolKind.Label: + case Microsoft.CodeAnalysis.SymbolKind.TypeParameter: + case Microsoft.CodeAnalysis.SymbolKind.RangeVariable: + return true; } - return true; + return false; } - static MonoDevelop.Ide.FindInFiles.SearchResult GetJumpTypePartSearchResult (ICSharpCode.NRefactory.TypeSystem.IUnresolvedTypeDefinition part) + static MonoDevelop.Ide.FindInFiles.SearchResult GetJumpTypePartSearchResult (Microsoft.CodeAnalysis.ISymbol part, Microsoft.CodeAnalysis.Location location) { - var provider = new MonoDevelop.Ide.FindInFiles.FileProvider (part.Region.FileName); - var doc = new Mono.TextEditor.TextDocument (); + var provider = new MonoDevelop.Ide.FindInFiles.FileProvider (location.SourceTree.FilePath); + var doc = TextEditorFactory.CreateNewDocument (); doc.Text = provider.ReadString (); - int position = doc.LocationToOffset (part.Region.BeginLine, part.Region.BeginColumn); - while (position + part.Name.Length < doc.TextLength) { + int position = location.SourceSpan.Start; + while (position + part.Name.Length < doc.Length) { if (doc.GetTextAt (position, part.Name.Length) == part.Name) break; position++; @@ -263,67 +264,90 @@ namespace MonoDevelop.Ide return new MonoDevelop.Ide.FindInFiles.SearchResult (provider, position, part.Name.Length); } - public void JumpToDeclaration (ICSharpCode.NRefactory.TypeSystem.INamedElement visitable, bool askIfMultipleLocations = true) + public void JumpTo (Microsoft.CodeAnalysis.ISymbol symbol, Microsoft.CodeAnalysis.Location location, Project project = null) { - if (askIfMultipleLocations) { - var type = visitable as ICSharpCode.NRefactory.TypeSystem.IType; - if (type != null && type.GetDefinition () != null && type.GetDefinition ().Parts.Count > 1) { - using (var monitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, true)) { - foreach (var part in type.GetDefinition ().Parts) - monitor.ReportResult (GetJumpTypePartSearchResult (part)); + if (location == null) + return; + if (location.IsInMetadata) { + string fileName = null; + var dn = project as DotNetProject; + if (dn == null) + return; + var metadataDllName = location.MetadataModule.Name; + if (metadataDllName == "CommonLanguageRuntimeLibrary") + metadataDllName = "corlib.dll"; + foreach (var assembly in dn.GetReferencedAssemblies (IdeApp.Workspace.ActiveConfiguration)) { + if (assembly.IndexOf (metadataDllName) > 0) { + fileName = dn.GetAbsoluteChildPath (assembly); + break; } + } + if (fileName == null) return; + var doc = IdeApp.Workbench.OpenDocument (new FileOpenInformation (fileName, project)); + + if (doc != null) { + doc.RunWhenLoaded (delegate { + var handler = doc.PrimaryView.GetContent<MonoDevelop.Ide.Gui.Content.IOpenNamedElementHandler> (); + if (handler != null) + handler.Open (symbol); + }); } - } - JumpToDeclaration (visitable); + return; + } + IdeApp.Workbench.OpenDocument (new FileOpenInformation (location.SourceTree.FilePath, project) { + Offset = location.SourceSpan.Start + }); } - - void JumpToDeclaration (ICSharpCode.NRefactory.TypeSystem.INamedElement element) + + public void JumpToDeclaration (Microsoft.CodeAnalysis.ISymbol symbol, Project project = null, bool askIfMultipleLocations = true) { - var entity = element as ICSharpCode.NRefactory.TypeSystem.IEntity; - - if (entity == null && element is ICSharpCode.NRefactory.TypeSystem.IType) - entity = ((ICSharpCode.NRefactory.TypeSystem.IType)element).GetDefinition (); - if (entity is SpecializedMember) - entity = ((SpecializedMember)entity).MemberDefinition; - - if (entity == null) { - LoggingService.LogError ("Unknown element:" + element); + if (symbol == null) + throw new ArgumentNullException ("symbol"); + var locations = symbol.Locations; + + if (askIfMultipleLocations && locations.Length > 1) { + using (var monitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, true)) { + foreach (var part in locations) + monitor.ReportResult (GetJumpTypePartSearchResult (symbol, part)); + } return; } - string fileName; - bool isCecilProjectContent = entity.Region.IsEmpty; - if (isCecilProjectContent) { - fileName = entity.ParentAssembly.UnresolvedAssembly.Location; - } else { - fileName = entity.Region.FileName; - } - var project = (entity is ITypeDefinition ? ((ITypeDefinition )entity) : entity.DeclaringTypeDefinition).GetProjectWhereTypeIsDefined (); - var doc = IdeApp.Workbench.OpenDocument (fileName, - project, - entity.Region.BeginLine, - entity.Region.BeginColumn); + JumpTo (symbol, locations.FirstOrDefault (), project); + } - if (isCecilProjectContent && doc != null) { + public void JumpToMetadata (string metadataDllName, string documentationCommentId, Project project = null) + { + if (metadataDllName == null) + throw new ArgumentNullException ("metadataDllName"); + if (documentationCommentId == null) + throw new ArgumentNullException ("documentationCommentId"); + string fileName = metadataDllName; + if (metadataDllName == "CommonLanguageRuntimeLibrary") + metadataDllName = "corlib.dll"; + var dn = project as DotNetProject; + if (dn != null) { + foreach (var assembly in dn.GetReferencedAssemblies (IdeApp.Workspace.ActiveConfiguration)) { + if (assembly.IndexOf(metadataDllName, StringComparison.Ordinal) > 0) { + fileName = dn.GetAbsoluteChildPath (assembly); + break; + } + } + } + if (fileName == null || !File.Exists (fileName)) + return; + var doc = IdeApp.Workbench.OpenDocument (new FileOpenInformation (fileName)); + if (doc != null) { doc.RunWhenLoaded (delegate { var handler = doc.PrimaryView.GetContent<MonoDevelop.Ide.Gui.Content.IOpenNamedElementHandler> (); if (handler != null) - handler.Open (entity); + handler.Open (documentationCommentId); }); } } - public void JumpToDeclaration (ICSharpCode.NRefactory.TypeSystem.IVariable entity) - { - if (entity == null) - throw new ArgumentNullException ("entity"); - string fileName = entity.Region.FileName; - // variables are always in the same file -> file is already open, project not needed. - IdeApp.Workbench.OpenDocument (fileName, null, entity.Region.BeginLine, entity.Region.BeginColumn); - } - - public async Task RenameItem (IWorkspaceFileObject item, string newName) + public async void RenameItem (IWorkspaceFileObject item, string newName) { ProjectOptionsDialog.RenameItem (item, newName); if (item is SolutionFolderItem) { @@ -2100,7 +2124,12 @@ namespace MonoDevelop.Ide return new BackgroundProgressMonitor (GettextCatalog.GetString ("Code completion database generation"), "md-parser"); } } - + + public interface ITextFileProvider + { + ITextDocument GetEditableTextFile (FilePath filePath); + } + public class TextFileProvider : ITextFileProvider { static TextFileProvider instance = new TextFileProvider (); @@ -2113,101 +2142,19 @@ namespace MonoDevelop.Ide TextFileProvider () { } - - class ProviderProxy : ITextEditorDataProvider, IEditableTextFile - { - TextEditorData data; - string encoding; - bool bom; - - public ProviderProxy (TextEditorData data, string encoding, bool bom) - { - this.data = data; - this.encoding = encoding; - this.bom = bom; - } - - public TextEditorData GetTextEditorData () - { - return data; - } - - void Save () - { - TextFile.WriteFile (Name, Text, encoding, bom); - } - - #region IEditableTextFile implementation - public FilePath Name { get { return data.Document.FileName; } } - public int Length { get { return data.Length; } } - - public string GetText (int startPosition, int endPosition) - { - return data.GetTextBetween (startPosition, endPosition); - } - - public char GetCharAt (int position) - { - return data.GetCharAt (position); - } - - public int GetPositionFromLineColumn (int line, int column) - { - return data.Document.LocationToOffset (line, column); - } - - public void GetLineColumnFromPosition (int position, out int line, out int column) - { - var loc = data.Document.OffsetToLocation (position); - line = loc.Line; - column = loc.Column; - } - - public int InsertText (int position, string text) - { - int result = data.Insert (position, text); - Save (); - - return result; - } - - public void DeleteText (int position, int length) - { - data.Remove (position, length); - Save (); - } - - public string Text { - get { - return data.Text; - } - set { - data.Text = value; - Save (); - } - } - - #endregion - } - - public IEditableTextFile GetEditableTextFile (FilePath filePath) + public ITextDocument GetEditableTextFile (FilePath filePath) { if (IdeApp.IsInitialized) { foreach (var doc in IdeApp.Workbench.Documents) { if (doc.FileName == filePath) { - IEditableTextFile ef = doc.GetContent<IEditableTextFile> (); + var ef = doc.Editor; if (ef != null) return ef; } } } - - TextFile file = TextFile.ReadFile (filePath); - TextEditorData data = new TextEditorData (); - data.Document.FileName = filePath; - data.Text = file.Text; - - return new ProviderProxy (data, file.SourceEncoding, file.HadBOM); + + return TextEditorFactory.CreateNewDocument (StringTextSource.ReadFrom (filePath), filePath); } /// <summary> @@ -2216,18 +2163,16 @@ namespace MonoDevelop.Ide /// <returns><c>true</c>, if file operation was saved, <c>false</c> otherwise.</returns> /// <param name="filePath">File path.</param> /// <param name="operation">The operation.</param> - public bool EditFile (FilePath filePath, Action<TextEditorData> operation) + public bool EditFile (FilePath filePath, Action<ITextDocument> operation) { if (operation == null) throw new ArgumentNullException ("operation"); - bool hadBom; - Encoding encoding; bool isOpen; - var data = GetTextEditorData (filePath, out hadBom, out encoding, out isOpen); + var data = GetTextEditorData (filePath, out isOpen); operation (data); if (!isOpen) { - try { - Mono.TextEditor.Utils.TextFileUtility.WriteText (filePath, data.Text, encoding, hadBom); + try { + data.Save (); } catch (Exception e) { LoggingService.LogError ("Error while saving changes to : " + filePath, e); return false; @@ -2236,13 +2181,13 @@ namespace MonoDevelop.Ide return true; } - public TextEditorData GetTextEditorData (FilePath filePath) + public ITextDocument GetTextEditorData (FilePath filePath) { bool isOpen; return GetTextEditorData (filePath, out isOpen); } - public TextEditorData GetReadOnlyTextEditorData (FilePath filePath) + public IReadonlyTextDocument GetReadOnlyTextEditorData (FilePath filePath) { if (filePath.IsNullOrEmpty) throw new ArgumentNullException ("filePath"); @@ -2251,42 +2196,26 @@ namespace MonoDevelop.Ide return doc.Editor; } } - bool hadBom; - Encoding encoding; - var text = Mono.TextEditor.Utils.TextFileUtility.ReadAllText (filePath, out hadBom, out encoding); - var data = new TextEditorData (TextDocument.CreateImmutableDocument (text)); - data.Document.MimeType = DesktopService.GetMimeTypeForUri (filePath); - data.Document.FileName = filePath; - data.Text = text; + var data = TextEditorFactory.CreateNewReadonlyDocument (StringTextSource.ReadFrom (filePath), filePath); return data; } - public TextEditorData GetTextEditorData (FilePath filePath, out bool isOpen) - { - bool hadBom; - Encoding encoding; - return GetTextEditorData (filePath, out hadBom, out encoding, out isOpen); - } - - public TextEditorData GetTextEditorData (FilePath filePath, out bool hadBom, out Encoding encoding, out bool isOpen) + public ITextDocument GetTextEditorData (FilePath filePath, out bool isOpen) { foreach (var doc in IdeApp.Workbench.Documents) { if (doc.FileName == filePath) { - var content = doc.GetContent <MonoDevelop.Ide.Gui.Content.IEncodedTextContent> (); - var theEncoding = content != null ? content.SourceEncoding : null; - isOpen = true; - hadBom = false; - encoding = theEncoding ?? Encoding.Default; return doc.Editor; } } - - var text = Mono.TextEditor.Utils.TextFileUtility.ReadAllText (filePath, out hadBom, out encoding); - TextEditorData data = new TextEditorData (); - data.Document.SuppressHighlightUpdate = true; - data.Document.MimeType = DesktopService.GetMimeTypeForUri (filePath); - data.Document.FileName = filePath; + bool hadBom; + Encoding encoding; + var text = TextFileUtility.ReadAllText (filePath, out hadBom, out encoding); + var data = TextEditorFactory.CreateNewDocument (); + data.UseBOM = hadBom; + data.Encoding = encoding; + data.MimeType = DesktopService.GetMimeTypeForUri (filePath); + data.FileName = filePath; data.Text = text; isOpen = false; return data; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/RootWorkspace.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/RootWorkspace.cs index 33cf83c704..7bfd13a306 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/RootWorkspace.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/RootWorkspace.cs @@ -554,7 +554,7 @@ namespace MonoDevelop.Ide if (reloading) SetReloading (false); } - + using (monitor) { try { // Add the item in the GUI thread. It is not safe to do it in the background thread. @@ -910,6 +910,11 @@ namespace MonoDevelop.Ide internal void NotifyItemAdded (WorkspaceItem item) { + try { + MonoDevelop.Ide.TypeSystem.TypeSystemService.Load (item, null); + } catch (Exception ex) { + LoggingService.LogError ("Could not load parser database.", ex); + } if (DispatchService.IsGuiThread) NotifyItemAddedGui (item, IsReloading); else { @@ -919,18 +924,9 @@ namespace MonoDevelop.Ide }); } } - + void NotifyItemAddedGui (WorkspaceItem item, bool reloading) { - try { -// Mono.Profiler.RuntimeControls.EnableProfiler (); - MonoDevelop.Ide.TypeSystem.TypeSystemService.Load (item); -// Mono.Profiler.RuntimeControls.DisableProfiler (); -// Console.WriteLine ("PARSE LOAD: " + (DateTime.Now - t).TotalMilliseconds); - } catch (Exception ex) { - LoggingService.LogError ("Could not load parser database.", ex); - } - Workspace ws = item as Workspace; if (ws != null) { ws.DescendantItemAdded += NotifyDescendantItemAdded; @@ -982,10 +978,8 @@ namespace MonoDevelop.Ide if (LastWorkspaceItemClosed != null) LastWorkspaceItemClosed (this, EventArgs.Empty); } - MonoDevelop.Ide.TypeSystem.TypeSystemService.Unload (item); -// ParserDatabase.Unload (item); - + NotifyDescendantItemRemoved (this, args); } @@ -1223,7 +1217,7 @@ namespace MonoDevelop.Ide /// once for the workspace, and once for each solution. /// </remarks> public event EventHandler<WorkspaceItemEventArgs> WorkspaceItemLoaded; - + /// <summary> /// Fired when a workspace item (a solution or workspace) is unloaded /// </summary> diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Services.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Services.cs index bdc908fc2d..8be5d967a0 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Services.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Services.cs @@ -53,6 +53,7 @@ namespace MonoDevelop.Ide internal static TimerCounter OpenDocumentTimer = InstrumentationService.CreateTimerCounter ("Document opened", "IDE"); internal static TimerCounter DocumentOpened = InstrumentationService.CreateTimerCounter ("Document opened", "IDE", id:"Ide.Shell.DocumentOpened"); internal static TimerCounter BuildItemTimer = InstrumentationService.CreateTimerCounter ("Project/Solution built in the IDE", "IDE"); + internal static Counter AutoSavedFiles = InstrumentationService.CreateCounter ("Autosaved Files", "Text Editor"); internal static Counter PadShown = InstrumentationService.CreateCounter ("Pad focused", "IDE", id:"Ide.Shell.PadShown"); internal static class ParserService { diff --git a/main/src/core/MonoDevelop.Ide/packages.config b/main/src/core/MonoDevelop.Ide/packages.config new file mode 100644 index 0000000000..0b521ddcb5 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/packages.config @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="Microsoft.CodeAnalysis.Common" version="1.0.0.0-beta2" targetFramework="net45" />
+ <package id="Microsoft.CodeAnalysis.CSharp" version="1.0.0.0-beta2" targetFramework="net45" />
+ <package id="Microsoft.CodeAnalysis.CSharp.Workspaces" version="1.0.0.0-beta2" targetFramework="net45" />
+ <package id="Microsoft.CodeAnalysis.Workspaces.Common" version="1.0.0.0-beta2" targetFramework="net45" />
+ <package id="Microsoft.Composition" version="1.0.27" targetFramework="net45" />
+ <package id="System.Collections.Immutable" version="1.1.33-beta" targetFramework="net45" />
+ <package id="System.Reflection.Metadata" version="1.0.18-beta" targetFramework="net45" />
+</packages>
\ No newline at end of file |