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

github.com/mono/monodevelop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/main/src
diff options
context:
space:
mode:
Diffstat (limited to 'main/src')
-rw-r--r--main/src/addins/AspNet/Properties/MonoDevelop.AspNet.addin.xml8
-rw-r--r--main/src/addins/CSharpBinding/CSharpBinding.csproj37
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Parser/CSharpParsedDocument.cs4
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp/SignatureMarkupCreator.cs2
-rw-r--r--main/src/addins/CSharpBinding/Razor/ContainedDocumentPreserveFormattingRule.cs34
-rw-r--r--main/src/addins/CSharpBinding/Razor/IMonoDevelopContainedLanguageHost.cs7
-rw-r--r--main/src/addins/CSharpBinding/Razor/IMyRoslynCompletionDataProvider.cs11
-rw-r--r--main/src/addins/CSharpBinding/Razor/MonoDevelopContainedDocument.cs881
-rw-r--r--main/src/addins/CSharpBinding/Razor/MonoDevelopFormattingRuleFactoryServiceFactory.cs91
-rw-r--r--main/src/addins/CSharpBinding/Razor/MyCSharpCompletionData.cs114
-rw-r--r--main/src/addins/CSharpBinding/Razor/MyCSharpCompletionDataProvider.cs19
-rw-r--r--main/src/addins/CSharpBinding/Razor/MyRoslynCompletionData.cs188
-rw-r--r--main/src/addins/CSharpBinding/Razor/RoslynCommandTarget.cs121
-rw-r--r--main/src/addins/CSharpBinding/Razor/RoslynCompletionPresenter.cs81
-rw-r--r--main/src/addins/CSharpBinding/Razor/RoslynCompletionPresenterSession.cs155
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj1
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DisassemblyView.cs4
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Refactoring/PackageCodeDiagnosticProvider.cs206
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.AnalysisCore/AnalysisCommands.cs8
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.AnalysisCore/MonoDevelopWorkspaceDiagnosticAnalyzerProviderService.OptionsTable.cs114
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.AnalysisCore/MonoDevelopWorkspaceDiagnosticAnalyzerProviderService.cs55
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionContainer.cs72
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionEditorExtension.cs85
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionPanelWidget.cs7
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeFixMenuService.cs240
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeRefactoringService.cs111
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/AnalyzersFromAssembly.cs137
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/BuiltInCodeDiagnosticProvider.cs112
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/CodeDiagnosticDescriptor.cs5
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/CodeDiagnosticFixDescriptor.cs89
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/CodeDiagnosticProvider.cs45
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/CodeDiagnosticRunner.cs145
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/CodeIssuePanelWidget.cs11
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.addin.xml16
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.csproj8
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring/AnalyzeWholeSolutionHandler.cs13
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MdTextViewLineCollection.MdTextViewLine.cs226
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MdTextViewLineCollection.cs145
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MonoTextEditor.ITextView.MouseHover.cs226
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MonoTextEditor.ITextView.cs510
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MonoTextEditor.IViewScroller.cs109
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MonoTextEditor.cs5
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/TextArea.cs12
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor.csproj94
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/EditorFactory.cs8
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorView.IMonoDevelopEditorOperations.cs642
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorView.cs267
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/TextMarker/WavedLineMarker.cs10
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/FakeWpf/Geometry.cs61
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/FakeWpf/Mouse.cs19
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/FakeWpf/SimulatingExtensions.cs29
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/IMdTextView.cs23
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Def/Intellisense/IPopupIntellisensePresenter.cs75
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Helpers.cs232
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/BaseIntellisenseSession.cs147
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/CurrentLineSpaceReservationAgent.cs220
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/IMultiSessionIntellisensePresenter.cs21
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/IntellisenseManager.cs194
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/IntellisenseSession.cs30
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/IntellisenseSessionStack.cs550
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/IntellisenseSessionStackMapService.cs65
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/IntellisenseSpaceReservationManagers.cs38
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/MultiSessionIntellisensePresenterProvider.cs101
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/DefaultSignatureHelpPresenter.cs389
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/DefaultSignatureHelpPresenterProvider.cs91
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/DefaultSignatureHelpPresenterSurfaceElement.cs181
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/SignatureHelpBroker.cs184
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/SignatureHelpParameterBoldingClassfier.cs146
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/SignatureHelpSession.cs255
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/SignatureHelpSessionView.cs473
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/QuickInfo/AsyncQuickInfoBroker.cs148
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/QuickInfo/AsyncQuickInfoSession.cs535
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/QuickInfo/QuickInfoController.cs165
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/QuickInfo/QuickInfoTextViewCreationListener.cs40
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/QuickInfo/SquiggleQuickInfoSource.cs139
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/QuickInfo/SquiggleQuickInfoSourceProvider.cs26
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Util/LanguageUtil/IntellisenseSourceCache.cs115
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Util/LanguageUtil/IntellisenseUtilities.cs100
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/MDUtils.cs165
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TagBasedSyntaxHighlighting.cs15
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Def/TextUIWpf/Adornments/ToolTipPresenterStyle.cs35
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Def/TextUIWpf/Editor/ISpaceReservationAgent.cs49
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Def/TextUIWpf/Editor/ISpaceReservationManager.cs79
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Def/TextUIWpf/Editor/SpaceReservationAgentChangedEventArgs.cs39
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Def/TextUIWpf/Editor/SpaceReservationManagerDefinition.cs27
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/ToolTipAdornment/GuardedToolTipPresenter.cs56
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/ToolTipAdornment/IViewElementFactoryMetadata.cs11
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/ToolTipAdornment/ToolTipService.cs61
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/ToolTipAdornment/ViewElementFactoryService.cs143
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/BaseWpfToolTipPresenter.cs176
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/DefaultToolTipPresenterStyle.cs18
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/Legacy/ToolTipProvider.cs104
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/Legacy/ToolTipProviderFactory.cs47
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/MouseTrackingWpfToolTipPresenter.cs327
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/SpanTrackingWpfToolTipPresenter.cs115
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/ToolTipStyleFactory.cs36
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/ViewElementFactories/WpfClassifiedTextElementViewElementFactory.cs74
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/ViewElementFactories/WpfContainerElementViewElementFactory.cs58
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/ViewElementFactories/WpfImageElementViewElementFactory.cs39
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/WpfToolTipPresenterProvider.cs45
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfView/PopupAgent.cs794
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfView/SpaceReservationManager.cs244
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfView/SpaceReservationStack.cs129
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Util/TextDataUtil/MappingHelper.cs275
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TextCaret.cs256
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TextEditorFactoryService.cs26
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TextSelection.cs14
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TextView.cs562
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/VisualStudio/Impl/ViewAdapter/TipManager.cs21
-rw-r--r--main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.csproj4
-rw-r--r--main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Shared.projitems1
-rw-r--r--main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/ClipboardActions.cs11
-rw-r--r--main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/MiscActions.cs9
-rw-r--r--main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/CaretImpl.ITextCaret.cs236
-rw-r--r--main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/CaretImpl.cs8
-rw-r--r--main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/TextDocument.cs54
-rw-r--r--main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/TextEditorData.cs7
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/RemoteBuildEngineManager.cs10
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/SdkResolution.cs2
-rw-r--r--main/src/core/MonoDevelop.Core/packages.config181
-rw-r--r--main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml4
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuExtensionsMac.cs4
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionController.cs26
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionWindowManager.cs23
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/InlineRenameService.cs24
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/PlatformCatalog.cs12
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Desktop/PlatformService.cs20
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/EditActions.cs92
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/IEditorActionHost.cs125
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/IMonoDevelopEditorOperations.cs (renamed from main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/ValidCodeAction.cs)48
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/ITextEditorImpl.cs4
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditor.cs8
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorDisplayBinding.cs18
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/FindInFilesDialog.cs20
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/Scope.cs34
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Document.cs2
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/ViewCommandHandlers.cs20
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/IMonoDevelopHostDocument.cs52
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs31
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService_WorkspaceHandling.cs96
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/WorkspaceExtensions.cs (renamed from main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/ValidCodeDiagnosticAction.cs)42
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj9
142 files changed, 12876 insertions, 2684 deletions
diff --git a/main/src/addins/AspNet/Properties/MonoDevelop.AspNet.addin.xml b/main/src/addins/AspNet/Properties/MonoDevelop.AspNet.addin.xml
index 2cc9d27589..eb60c3311a 100644
--- a/main/src/addins/AspNet/Properties/MonoDevelop.AspNet.addin.xml
+++ b/main/src/addins/AspNet/Properties/MonoDevelop.AspNet.addin.xml
@@ -342,7 +342,7 @@
<Extension path = "/MonoDevelop/Ide/TextEditorExtensions">
<Class fileExtensions=".aspx, .ascx, .master" class = "MonoDevelop.AspNet.WebForms.WebFormsEditorExtension" />
<Class mimeTypes="text/html,application/x-spark" class = "MonoDevelop.AspNet.Html.HtmlEditorExtension" />
- <Class fileExtensions=".cshtml" class = "MonoDevelop.AspNet.Razor.RazorCSharpEditorExtension" />
+ <Class mimeTypes="text/x-cshtml" class = "MonoDevelop.AspNet.Razor.RazorCSharpEditorExtension" />
</Extension>
<Extension path = "/MonoDevelop/Html/DocTypes">
@@ -378,7 +378,11 @@
<CommandItem id = "MonoDevelop.AspNet.Commands.AspNetCommands.AddViewFromController" />
<SeparatorItem id = "MvcSeparator1" />
</Condition>
- <Condition id="FileType" fileExtensions=".cshtml,.aspx">
+ <Condition id="FileType" fileExtensions=".aspx">
+ <CommandItem id = "MonoDevelop.AspNet.Commands.AspNetCommands.GoToController" insertbefore="MonoDevelop.Ide.Commands.EditCommands.Cut" />
+ <SeparatorItem id = "MvcSeparator2" />
+ </Condition>
+ <Condition id="FileType" mimeType="text/x-cshtml">
<CommandItem id = "MonoDevelop.AspNet.Commands.AspNetCommands.GoToController" insertbefore="MonoDevelop.Ide.Commands.EditCommands.Cut" />
<SeparatorItem id = "MvcSeparator2" />
</Condition>
diff --git a/main/src/addins/CSharpBinding/CSharpBinding.csproj b/main/src/addins/CSharpBinding/CSharpBinding.csproj
index ee7265d81a..dedf92b2be 100644
--- a/main/src/addins/CSharpBinding/CSharpBinding.csproj
+++ b/main/src/addins/CSharpBinding/CSharpBinding.csproj
@@ -1,6 +1,7 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<Import Project="..\..\..\MonoDevelop.props" />
<Import Project="$(ReferencesVSEditor)" />
+ <Import Project="$(ReferencesRoslyn)" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
@@ -144,30 +145,6 @@
<HintPath>..\..\..\build\bin\System.Collections.Immutable.dll</HintPath>
<Private>False</Private>
</Reference>
- <Reference Include="Microsoft.CodeAnalysis.Workspaces">
- <HintPath>..\..\..\build\bin\Microsoft.CodeAnalysis.Workspaces.dll</HintPath>
- <Private>False</Private>
- </Reference>
- <Reference Include="Microsoft.CodeAnalysis">
- <HintPath>..\..\..\build\bin\Microsoft.CodeAnalysis.dll</HintPath>
- <Private>False</Private>
- </Reference>
- <Reference Include="Microsoft.CodeAnalysis.CSharp.Workspaces">
- <HintPath>..\..\..\build\bin\Microsoft.CodeAnalysis.CSharp.Workspaces.dll</HintPath>
- <Private>False</Private>
- </Reference>
- <Reference Include="Microsoft.CodeAnalysis.CSharp">
- <HintPath>..\..\..\build\bin\Microsoft.CodeAnalysis.CSharp.dll</HintPath>
- <Private>False</Private>
- </Reference>
- <Reference Include="Microsoft.CodeAnalysis.CSharp.Features">
- <HintPath>..\..\..\build\bin\Microsoft.CodeAnalysis.CSharp.Features.dll</HintPath>
- <Private>False</Private>
- </Reference>
- <Reference Include="Microsoft.CodeAnalysis.Features">
- <HintPath>..\..\..\build\bin\Microsoft.CodeAnalysis.Features.dll</HintPath>
- <Private>False</Private>
- </Reference>
<Reference Include="System.ValueTuple">
<HintPath>..\..\..\packages\System.ValueTuple.4.4.0\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
<Private>False</Private>
@@ -324,6 +301,17 @@
<Compile Include="MonoDevelop.CSharp.Features\Formatter\Indent.cs" />
<Compile Include="MonoDevelop.CSharp\DeclaredSymbolInfo.cs" />
<Compile Include="MonoDevelop.CSharp\ProjectSearchCategory.cs" />
+ <Compile Include="Razor\ContainedDocumentPreserveFormattingRule.cs" />
+ <Compile Include="Razor\IMonoDevelopContainedLanguageHost.cs" />
+ <Compile Include="Razor\IMyRoslynCompletionDataProvider.cs" />
+ <Compile Include="Razor\MonoDevelopContainedDocument.cs" />
+ <Compile Include="Razor\MonoDevelopFormattingRuleFactoryServiceFactory.cs" />
+ <Compile Include="Razor\MyCSharpCompletionData.cs" />
+ <Compile Include="Razor\MyCSharpCompletionDataProvider.cs" />
+ <Compile Include="Razor\MyRoslynCompletionData.cs" />
+ <Compile Include="Razor\RoslynCommandTarget.cs" />
+ <Compile Include="Razor\RoslynCompletionPresenter.cs" />
+ <Compile Include="Razor\RoslynCompletionPresenterSession.cs" />
<Compile Include="Util\7BitEncodedInts.cs" />
<Compile Include="Util\ArgumentSyntaxExtensions.cs" />
<Compile Include="Util\CloneableStack.cs" />
@@ -416,6 +404,7 @@
<InternalsVisibleTo Include="MonoDevelop.Unity">
<PublicKey>0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9</PublicKey>
</InternalsVisibleTo>
+ <InternalsVisibleTo Include="WebToolingAddin" />
</ItemGroup>
<ItemGroup>
<Folder Include="MonoDevelop.CSharp.Features\LineSeparators\" />
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Parser/CSharpParsedDocument.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Parser/CSharpParsedDocument.cs
index 6be03cc78c..faff647bc7 100644
--- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Parser/CSharpParsedDocument.cs
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Parser/CSharpParsedDocument.cs
@@ -517,8 +517,8 @@ namespace MonoDevelop.CSharp.Parser
static readonly IReadOnlyList<Error> emptyErrors = Array.Empty<Error> ();
public override async Task<IReadOnlyList<Error>> GetErrorsAsync (CancellationToken cancellationToken = default(CancellationToken))
{
- //if (Ide.IdeApp.Preferences.EnableSourceAnalysis)
- // return emptyErrors;
+ if (Ide.IdeApp.Preferences.EnableSourceAnalysis)
+ return emptyErrors;
var model = GetAst<SemanticModel> ();
if (model == null)
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp/SignatureMarkupCreator.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp/SignatureMarkupCreator.cs
index 7e95e4e9ac..7e1fe63c05 100644
--- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp/SignatureMarkupCreator.cs
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp/SignatureMarkupCreator.cs
@@ -51,7 +51,7 @@ using System.Threading;
namespace MonoDevelop.CSharp
{
- class SignatureMarkupCreator
+ internal class SignatureMarkupCreator
{
const double optionalAlpha = 0.7;
readonly DocumentContext ctx;
diff --git a/main/src/addins/CSharpBinding/Razor/ContainedDocumentPreserveFormattingRule.cs b/main/src/addins/CSharpBinding/Razor/ContainedDocumentPreserveFormattingRule.cs
new file mode 100644
index 0000000000..cf29b3d373
--- /dev/null
+++ b/main/src/addins/CSharpBinding/Razor/ContainedDocumentPreserveFormattingRule.cs
@@ -0,0 +1,34 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Formatting.Rules;
+using Microsoft.CodeAnalysis.Options;
+
+namespace Microsoft.VisualStudio.Platform
+{
+ internal class ContainedDocumentPreserveFormattingRule : AbstractFormattingRule
+ {
+ public static readonly IFormattingRule Instance = new ContainedDocumentPreserveFormattingRule ();
+
+ private static readonly AdjustSpacesOperation s_preserveSpace = FormattingOperations.CreateAdjustSpacesOperation (0, AdjustSpacesOption.PreserveSpaces);
+ private static readonly AdjustNewLinesOperation s_preserveLine = FormattingOperations.CreateAdjustNewLinesOperation (0, AdjustNewLinesOption.PreserveLines);
+
+ public override AdjustSpacesOperation GetAdjustSpacesOperation (SyntaxToken previousToken, SyntaxToken currentToken, OptionSet optionSet, NextOperation<AdjustSpacesOperation> nextOperation)
+ {
+ var operation = base.GetAdjustSpacesOperation (previousToken, currentToken, optionSet, nextOperation);
+ if (operation != null) {
+ return s_preserveSpace;
+ }
+
+ return operation;
+ }
+
+ public override AdjustNewLinesOperation GetAdjustNewLinesOperation (SyntaxToken previousToken, SyntaxToken currentToken, OptionSet optionSet, NextOperation<AdjustNewLinesOperation> nextOperation)
+ {
+ var operation = base.GetAdjustNewLinesOperation (previousToken, currentToken, optionSet, nextOperation);
+ if (operation != null) {
+ return s_preserveLine;
+ }
+
+ return operation;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/Razor/IMonoDevelopContainedLanguageHost.cs b/main/src/addins/CSharpBinding/Razor/IMonoDevelopContainedLanguageHost.cs
new file mode 100644
index 0000000000..3f2fbf6ba2
--- /dev/null
+++ b/main/src/addins/CSharpBinding/Razor/IMonoDevelopContainedLanguageHost.cs
@@ -0,0 +1,7 @@
+namespace Microsoft.VisualStudio.Platform
+{
+ public interface IMonoDevelopContainedLanguageHost
+ {
+ void GetLineIndent (int lLineNumber, out string indentString, out int parentIndentLevel, out int indentSize, out bool useTabs, out int tabSize);
+ }
+}
diff --git a/main/src/addins/CSharpBinding/Razor/IMyRoslynCompletionDataProvider.cs b/main/src/addins/CSharpBinding/Razor/IMyRoslynCompletionDataProvider.cs
new file mode 100644
index 0000000000..e9d26eb234
--- /dev/null
+++ b/main/src/addins/CSharpBinding/Razor/IMyRoslynCompletionDataProvider.cs
@@ -0,0 +1,11 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Completion;
+using Microsoft.VisualStudio.Text;
+
+namespace MonoDevelop.Ide.CodeCompletion
+{
+ public interface IMyRoslynCompletionDataProvider
+ {
+ MyRoslynCompletionData CreateCompletionData (Document document, ITextSnapshot textSnapshtot, CompletionService completionService, CompletionItem item);
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/CSharpBinding/Razor/MonoDevelopContainedDocument.cs b/main/src/addins/CSharpBinding/Razor/MonoDevelopContainedDocument.cs
new file mode 100644
index 0000000000..00d8f1f685
--- /dev/null
+++ b/main/src/addins/CSharpBinding/Razor/MonoDevelopContainedDocument.cs
@@ -0,0 +1,881 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Editor;
+using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
+using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
+using Microsoft.CodeAnalysis.Formatting;
+using Microsoft.CodeAnalysis.Formatting.Rules;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.Options;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Differencing;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Editor.OptionsExtensionMethods;
+using Microsoft.VisualStudio.Text.Projection;
+using MonoDevelop.Ide.Composition;
+using MonoDevelop.Ide.TypeSystem;
+using Roslyn.Utilities;
+
+namespace Microsoft.VisualStudio.Platform
+{
+ public class MonoDevelopContainedDocument : IMonoDevelopHostDocument
+ {
+ private const string ReturnReplacementString = @"{|r|}";
+ private const string NewLineReplacementString = @"{|n|}";
+
+ private const char RazorExplicit = '@';
+
+ private const string CSharpRazorBlock = "{";
+ private const string VBRazorBlock = "code";
+
+ private const string HelperRazor = "helper";
+ private const string FunctionsRazor = "functions";
+
+ private static readonly EditOptions s_venusEditOptions = new EditOptions (new StringDifferenceOptions {
+ DifferenceType = StringDifferenceTypes.Character,
+ IgnoreTrimWhiteSpace = false
+ });
+
+ public ITextBuffer LanguageBuffer { get; private set; }
+ public IProjectionBuffer DataBuffer { get; private set; }
+ public IMonoDevelopContainedLanguageHost ContainedLanguageHost { get; private set; }
+
+ // _workspace will only be set once the Workspace is available
+ private Workspace _workspace;
+
+ private readonly ITextDifferencingSelectorService _differenceSelectorService;
+
+ public DocumentId Id { get; private set; }
+
+ // Language will only be set once the Workspace is available
+ private string Language { get; set; }
+
+ public static MonoDevelopContainedDocument FromDocument(Document document)
+ {
+ return MonoDevelopHostDocumentRegistration.FromDocument(document) as MonoDevelopContainedDocument;
+ }
+
+ public static MonoDevelopContainedDocument AttachToBuffer(ITextBuffer languageBuffer, IProjectionBuffer dataBuffer, IMonoDevelopContainedLanguageHost containedLanguageHost)
+ {
+ return new MonoDevelopContainedDocument(languageBuffer, dataBuffer, containedLanguageHost);
+ }
+
+ public static void DetachFromBuffer(ITextBuffer languageBuffer)
+ {
+ var document = languageBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges ();
+ MonoDevelopHostDocumentRegistration.UnRegister (document);
+ }
+
+ private MonoDevelopContainedDocument (ITextBuffer languageBuffer, IProjectionBuffer dataBuffer, IMonoDevelopContainedLanguageHost containedLanguageHost)
+ {
+ LanguageBuffer = languageBuffer;
+ DataBuffer = dataBuffer;
+ ContainedLanguageHost = containedLanguageHost;
+
+ _differenceSelectorService = CompositionManager.GetExportedValue<ITextDifferencingSelectorService> ();
+
+ var container = languageBuffer.CurrentSnapshot.AsText ().Container;
+ var registration = Workspace.GetWorkspaceRegistration (container);
+
+ if (registration.Workspace == null)
+ {
+ registration.WorkspaceChanged += Registration_WorkspaceChanged;
+ }
+ else
+ {
+ FinishInitialization ();
+ }
+ }
+
+ private void Registration_WorkspaceChanged (object sender, EventArgs e)
+ {
+ var container = LanguageBuffer.CurrentSnapshot.AsText ().Container;
+ var registration = Workspace.GetWorkspaceRegistration (container);
+
+ registration.WorkspaceChanged -= Registration_WorkspaceChanged;
+
+ FinishInitialization ();
+ }
+
+ private void FinishInitialization ()
+ {
+ var document = LanguageBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges ();
+ var project = document.Project;
+
+ _workspace = project.Solution.Workspace;
+
+ Language = project.Language;
+ Id = document.Id;
+
+ MonoDevelopHostDocumentRegistration.Register (document, this);
+ }
+
+ public ITextBuffer GetOpenTextBuffer ()
+ {
+ return LanguageBuffer;
+ }
+
+ public void UpdateText (SourceText newText)
+ {
+ if (_workspace == null) {
+ return;
+ }
+
+ var subjectBuffer = (IProjectionBuffer)this.GetOpenTextBuffer ();
+ var originalSnapshot = LanguageBuffer.CurrentSnapshot;
+ var originalText = originalSnapshot.AsText ();
+
+ var changes = newText.GetTextChanges (originalText);
+
+ IEnumerable<int> affectedVisibleSpanIndices = null;
+ var editorVisibleSpansInOriginal = SharedPools.Default<List<TextSpan>> ().AllocateAndClear ();
+
+ try {
+ var originalDocument = _workspace.CurrentSolution.GetDocument (this.Id);
+
+ editorVisibleSpansInOriginal.AddRange (GetEditorVisibleSpans ());
+ var newChanges = FilterTextChanges (originalText, editorVisibleSpansInOriginal, changes).ToList ();
+ if (newChanges.Count == 0) {
+ // no change to apply
+ return;
+ }
+
+ ApplyChanges (subjectBuffer, newChanges, editorVisibleSpansInOriginal, out affectedVisibleSpanIndices);
+ AdjustIndentation (subjectBuffer, affectedVisibleSpanIndices);
+ }
+ finally {
+ SharedPools.Default<HashSet<int>> ().ClearAndFree ((HashSet<int>)affectedVisibleSpanIndices);
+ SharedPools.Default<List<TextSpan>> ().ClearAndFree (editorVisibleSpansInOriginal);
+ }
+ }
+
+ private IEnumerable<TextChange> FilterTextChanges (SourceText originalText, List<TextSpan> editorVisibleSpansInOriginal, IReadOnlyList<TextChange> changes)
+ {
+ // no visible spans or changes
+ if (editorVisibleSpansInOriginal.Count == 0 || changes.Count == 0) {
+ // return empty one
+ yield break;
+ }
+
+ using (var pooledObject = SharedPools.Default<List<TextChange>> ().GetPooledObject ()) {
+ var changeQueue = pooledObject.Object;
+ changeQueue.AddRange (changes);
+
+ var spanIndex = 0;
+ var changeIndex = 0;
+ for (; spanIndex < editorVisibleSpansInOriginal.Count; spanIndex++) {
+ var visibleSpan = editorVisibleSpansInOriginal[spanIndex];
+ var visibleTextSpan = GetVisibleTextSpan (originalText, visibleSpan, uptoFirstAndLastLine: true);
+
+ for (; changeIndex < changeQueue.Count; changeIndex++) {
+ var change = changeQueue[changeIndex];
+
+ // easy case first
+ if (change.Span.End < visibleSpan.Start) {
+ // move to next change
+ continue;
+ }
+
+ if (visibleSpan.End < change.Span.Start) {
+ // move to next visible span
+ break;
+ }
+
+ // make sure we are not replacing whitespace around start and at the end of visible span
+ if (WhitespaceOnEdges (originalText, visibleTextSpan, change)) {
+ continue;
+ }
+
+ if (visibleSpan.Contains (change.Span)) {
+ yield return change;
+ continue;
+ }
+
+ // now it is complex case where things are intersecting each other
+ var subChanges = GetSubTextChanges (originalText, change, visibleSpan).ToList ();
+ if (subChanges.Count > 0) {
+ if (subChanges.Count == 1 && subChanges[0] == change) {
+ // we can't break it. not much we can do here. just don't touch and ignore this change
+ continue;
+ }
+
+ changeQueue.InsertRange (changeIndex + 1, subChanges);
+ continue;
+ }
+ }
+ }
+ }
+ }
+
+ private bool WhitespaceOnEdges (SourceText text, TextSpan visibleTextSpan, TextChange change)
+ {
+ if (!string.IsNullOrWhiteSpace (change.NewText)) {
+ return false;
+ }
+
+ if (change.Span.End <= visibleTextSpan.Start) {
+ return true;
+ }
+
+ if (visibleTextSpan.End <= change.Span.Start) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private IEnumerable<TextChange> GetSubTextChanges (SourceText originalText, TextChange changeInOriginalText, TextSpan visibleSpanInOriginalText)
+ {
+ using (var changes = SharedPools.Default<List<TextChange>> ().GetPooledObject ()) {
+ var leftText = originalText.ToString (changeInOriginalText.Span);
+ var rightText = changeInOriginalText.NewText;
+ var offsetInOriginalText = changeInOriginalText.Span.Start;
+
+ if (TryGetSubTextChanges (originalText, visibleSpanInOriginalText, leftText, rightText, offsetInOriginalText, changes.Object)) {
+ return changes.Object.ToList ();
+ }
+
+ return GetSubTextChanges (originalText, visibleSpanInOriginalText, leftText, rightText, offsetInOriginalText);
+ }
+ }
+
+ private bool TryGetSubTextChanges (
+ SourceText originalText, TextSpan visibleSpanInOriginalText, string leftText, string rightText, int offsetInOriginalText, List<TextChange> changes)
+ {
+ // these are expensive. but hopefully we don't hit this as much except the boundary cases.
+ using (var leftPool = SharedPools.Default<List<TextSpan>> ().GetPooledObject ())
+ using (var rightPool = SharedPools.Default<List<TextSpan>> ().GetPooledObject ()) {
+ var spansInLeftText = leftPool.Object;
+ var spansInRightText = rightPool.Object;
+
+ if (TryGetWhitespaceOnlyChanges (leftText, rightText, spansInLeftText, spansInRightText)) {
+ for (var i = 0; i < spansInLeftText.Count; i++) {
+ var spanInLeftText = spansInLeftText[i];
+ var spanInRightText = spansInRightText[i];
+ if (spanInLeftText.IsEmpty && spanInRightText.IsEmpty) {
+ continue;
+ }
+
+ var spanInOriginalText = new TextSpan (offsetInOriginalText + spanInLeftText.Start, spanInLeftText.Length);
+ if (TryGetSubTextChange (originalText, visibleSpanInOriginalText, rightText, spanInOriginalText, spanInRightText, out var textChange)) {
+ changes.Add (textChange);
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ private IEnumerable<TextChange> GetSubTextChanges (
+ SourceText originalText, TextSpan visibleSpanInOriginalText, string leftText, string rightText, int offsetInOriginalText)
+ {
+ // these are expensive. but hopefully we don't hit this as much except the boundary cases.
+ using (var leftPool = SharedPools.Default<List<ValueTuple<int, int>>> ().GetPooledObject ())
+ using (var rightPool = SharedPools.Default<List<ValueTuple<int, int>>> ().GetPooledObject ()) {
+ var leftReplacementMap = leftPool.Object;
+ var rightReplacementMap = rightPool.Object;
+ GetTextWithReplacements (leftText, rightText, leftReplacementMap, rightReplacementMap, out var leftTextWithReplacement, out var rightTextWithReplacement);
+
+ var diffResult = DiffStrings (leftTextWithReplacement, rightTextWithReplacement);
+
+ foreach (var difference in diffResult) {
+ var spanInLeftText = AdjustSpan (diffResult.LeftDecomposition.GetSpanInOriginal (difference.Left), leftReplacementMap);
+ var spanInRightText = AdjustSpan (diffResult.RightDecomposition.GetSpanInOriginal (difference.Right), rightReplacementMap);
+
+ var spanInOriginalText = new TextSpan (offsetInOriginalText + spanInLeftText.Start, spanInLeftText.Length);
+ if (TryGetSubTextChange (originalText, visibleSpanInOriginalText, rightText, spanInOriginalText, spanInRightText.ToTextSpan (), out var textChange)) {
+ yield return textChange;
+ }
+ }
+ }
+ }
+
+ private bool TryGetWhitespaceOnlyChanges (string leftText, string rightText, List<TextSpan> spansInLeftText, List<TextSpan> spansInRightText)
+ {
+ return TryGetWhitespaceGroup (leftText, spansInLeftText) && TryGetWhitespaceGroup (rightText, spansInRightText) && spansInLeftText.Count == spansInRightText.Count;
+ }
+
+ private bool TryGetWhitespaceGroup (string text, List<TextSpan> groups)
+ {
+ if (text.Length == 0) {
+ groups.Add (new TextSpan (0, 0));
+ return true;
+ }
+
+ var start = 0;
+ for (var i = 0; i < text.Length; i++) {
+ var ch = text[i];
+ switch (ch) {
+ case ' ':
+ if (!TextAt (text, i - 1, ' ')) {
+ start = i;
+ }
+
+ break;
+
+ case '\r':
+ case '\n':
+ if (i == 0) {
+ groups.Add (TextSpan.FromBounds (0, 0));
+ }
+ else if (TextAt (text, i - 1, ' ')) {
+ groups.Add (TextSpan.FromBounds (start, i));
+ }
+ else if (TextAt (text, i - 1, '\n')) {
+ groups.Add (TextSpan.FromBounds (start, i));
+ }
+
+ start = i + 1;
+ break;
+
+ default:
+ return false;
+ }
+ }
+
+ if (start <= text.Length) {
+ groups.Add (TextSpan.FromBounds (start, text.Length));
+ }
+
+ return true;
+ }
+
+ private bool TextAt (string text, int index, char ch)
+ {
+ if (index < 0 || text.Length <= index) {
+ return false;
+ }
+
+ return text[index] == ch;
+ }
+
+ private bool TryGetSubTextChange (
+ SourceText originalText, TextSpan visibleSpanInOriginalText,
+ string rightText, TextSpan spanInOriginalText, TextSpan spanInRightText, out TextChange textChange)
+ {
+ textChange = default(TextChange);
+
+ var visibleFirstLineInOriginalText = originalText.Lines.GetLineFromPosition (visibleSpanInOriginalText.Start);
+ var visibleLastLineInOriginalText = originalText.Lines.GetLineFromPosition (visibleSpanInOriginalText.End);
+
+ // skip easy case
+ // 1. things are out of visible span
+ if (!visibleSpanInOriginalText.IntersectsWith (spanInOriginalText)) {
+ return false;
+ }
+
+ // 2. there are no intersects
+ var snippetInRightText = rightText.Substring (spanInRightText.Start, spanInRightText.Length);
+ if (visibleSpanInOriginalText.Contains (spanInOriginalText) && visibleSpanInOriginalText.End != spanInOriginalText.End) {
+ textChange = new TextChange (spanInOriginalText, snippetInRightText);
+ return true;
+ }
+
+ // okay, more complex case. things are intersecting boundaries.
+ var firstLineOfRightTextSnippet = snippetInRightText.GetFirstLineText ();
+ var lastLineOfRightTextSnippet = snippetInRightText.GetLastLineText ();
+
+ // there are 4 complex cases - these are all heuristic. not sure what better way I have. and the heuristic is heavily based on
+ // text differ's behavior.
+
+ // 1. it is a single line
+ if (visibleFirstLineInOriginalText.LineNumber == visibleLastLineInOriginalText.LineNumber) {
+ // don't do anything
+ return false;
+ }
+
+ // 2. replacement contains visible spans
+ if (spanInOriginalText.Contains (visibleSpanInOriginalText)) {
+ // header
+ // don't do anything
+
+ // body
+ textChange = new TextChange (
+ TextSpan.FromBounds (visibleFirstLineInOriginalText.EndIncludingLineBreak, visibleLastLineInOriginalText.Start),
+ snippetInRightText.Substring (firstLineOfRightTextSnippet.Length, snippetInRightText.Length - firstLineOfRightTextSnippet.Length - lastLineOfRightTextSnippet.Length));
+
+ // footer
+ // don't do anything
+
+ return true;
+ }
+
+ // 3. replacement intersects with start
+ if (spanInOriginalText.Start < visibleSpanInOriginalText.Start &&
+ visibleSpanInOriginalText.Start <= spanInOriginalText.End &&
+ spanInOriginalText.End < visibleSpanInOriginalText.End) {
+ // header
+ // don't do anything
+
+ // body
+ if (visibleFirstLineInOriginalText.EndIncludingLineBreak <= spanInOriginalText.End) {
+ textChange = new TextChange (
+ TextSpan.FromBounds (visibleFirstLineInOriginalText.EndIncludingLineBreak, spanInOriginalText.End),
+ snippetInRightText.Substring (firstLineOfRightTextSnippet.Length));
+ return true;
+ }
+
+ return false;
+ }
+
+ // 4. replacement intersects with end
+ if (visibleSpanInOriginalText.Start < spanInOriginalText.Start &&
+ spanInOriginalText.Start <= visibleSpanInOriginalText.End &&
+ visibleSpanInOriginalText.End <= spanInOriginalText.End) {
+ // body
+ if (spanInOriginalText.Start <= visibleLastLineInOriginalText.Start) {
+ textChange = new TextChange (
+ TextSpan.FromBounds (spanInOriginalText.Start, visibleLastLineInOriginalText.Start),
+ snippetInRightText.Substring (0, snippetInRightText.Length - lastLineOfRightTextSnippet.Length));
+ return true;
+ }
+
+ // footer
+ // don't do anything
+
+ return false;
+ }
+
+ // if it got hit, then it means there is a missing case
+ throw ExceptionUtilities.Unreachable;
+ }
+
+ private IHierarchicalDifferenceCollection DiffStrings (string leftTextWithReplacement, string rightTextWithReplacement)
+ {
+ var document = LanguageBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges (); ;
+
+ var diffService = _differenceSelectorService.GetTextDifferencingService (
+ _workspace.Services.GetLanguageServices (document.Project.Language).GetService<IContentTypeLanguageService> ().GetDefaultContentType ());
+
+ diffService = diffService ?? _differenceSelectorService.DefaultTextDifferencingService;
+ return diffService.DiffStrings (leftTextWithReplacement, rightTextWithReplacement, s_venusEditOptions.DifferenceOptions);
+ }
+
+ private void GetTextWithReplacements (
+ string leftText, string rightText,
+ List<ValueTuple<int, int>> leftReplacementMap, List<ValueTuple<int, int>> rightReplacementMap,
+ out string leftTextWithReplacement, out string rightTextWithReplacement)
+ {
+ // to make diff works better, we choose replacement strings that don't appear in both texts.
+ var returnReplacement = GetReplacementStrings (leftText, rightText, ReturnReplacementString);
+ var newLineReplacement = GetReplacementStrings (leftText, rightText, NewLineReplacementString);
+
+ leftTextWithReplacement = GetTextWithReplacementMap (leftText, returnReplacement, newLineReplacement, leftReplacementMap);
+ rightTextWithReplacement = GetTextWithReplacementMap (rightText, returnReplacement, newLineReplacement, rightReplacementMap);
+ }
+
+ private static string GetReplacementStrings (string leftText, string rightText, string initialReplacement)
+ {
+ if (leftText.IndexOf (initialReplacement, StringComparison.Ordinal) < 0 && rightText.IndexOf (initialReplacement, StringComparison.Ordinal) < 0) {
+ return initialReplacement;
+ }
+
+ // okay, there is already one in the given text.
+ const string format = "{{|{0}|{1}|{0}|}}";
+ for (var i = 0; true; i++) {
+ var replacement = string.Format (format, i.ToString (), initialReplacement);
+ if (leftText.IndexOf (replacement, StringComparison.Ordinal) < 0 && rightText.IndexOf (replacement, StringComparison.Ordinal) < 0) {
+ return replacement;
+ }
+ }
+ }
+
+ private string GetTextWithReplacementMap (string text, string returnReplacement, string newLineReplacement, List<ValueTuple<int, int>> replacementMap)
+ {
+ var delta = 0;
+ var returnLength = returnReplacement.Length;
+ var newLineLength = newLineReplacement.Length;
+
+ var sb = StringBuilderPool.Allocate ();
+ for (var i = 0; i < text.Length; i++) {
+ var ch = text[i];
+ if (ch == '\r') {
+ sb.Append (returnReplacement);
+ delta += returnLength - 1;
+ replacementMap.Add (ValueTuple.Create (i + delta, delta));
+ continue;
+ }
+ else if (ch == '\n') {
+ sb.Append (newLineReplacement);
+ delta += newLineLength - 1;
+ replacementMap.Add (ValueTuple.Create (i + delta, delta));
+ continue;
+ }
+
+ sb.Append (ch);
+ }
+
+ return StringBuilderPool.ReturnAndFree (sb);
+ }
+
+ private Span AdjustSpan (Span span, List<ValueTuple<int, int>> replacementMap)
+ {
+ var start = span.Start;
+ var end = span.End;
+
+ for (var i = 0; i < replacementMap.Count; i++) {
+ var positionAndDelta = replacementMap[i];
+ if (positionAndDelta.Item1 <= span.Start) {
+ start = span.Start - positionAndDelta.Item2;
+ }
+
+ if (positionAndDelta.Item1 <= span.End) {
+ end = span.End - positionAndDelta.Item2;
+ }
+
+ if (positionAndDelta.Item1 > span.End) {
+ break;
+ }
+ }
+
+ return Span.FromBounds (start, end);
+ }
+
+ public IEnumerable<TextSpan> GetEditorVisibleSpans ()
+ {
+ return DataBuffer.CurrentSnapshot
+ .GetSourceSpans ()
+ .Where (ss => ss.Snapshot.TextBuffer == LanguageBuffer)
+ .Select (s => s.Span.ToTextSpan ())
+ .OrderBy (s => s.Start);
+ }
+
+ private static void ApplyChanges (
+ IProjectionBuffer subjectBuffer,
+ IEnumerable<TextChange> changes,
+ IList<TextSpan> visibleSpansInOriginal,
+ out IEnumerable<int> affectedVisibleSpansInNew)
+ {
+ using (var edit = subjectBuffer.CreateEdit (s_venusEditOptions, reiteratedVersionNumber: null, editTag: null)) {
+ var affectedSpans = SharedPools.Default<HashSet<int>> ().AllocateAndClear ();
+ affectedVisibleSpansInNew = affectedSpans;
+
+ var currentVisibleSpanIndex = 0;
+ foreach (var change in changes) {
+ // Find the next visible span that either overlaps or intersects with
+ while (currentVisibleSpanIndex < visibleSpansInOriginal.Count &&
+ visibleSpansInOriginal[currentVisibleSpanIndex].End < change.Span.Start) {
+ currentVisibleSpanIndex++;
+ }
+
+ // no more place to apply text changes
+ if (currentVisibleSpanIndex >= visibleSpansInOriginal.Count) {
+ break;
+ }
+
+ var newText = change.NewText;
+ var span = new Span(change.Span.Start, change.Span.Length);
+
+ edit.Replace (span, newText);
+
+ affectedSpans.Add (currentVisibleSpanIndex);
+ }
+
+ edit.ApplyAndLogExceptions ();
+ }
+ }
+
+ private void AdjustIndentation (IProjectionBuffer subjectBuffer, IEnumerable<int> visibleSpanIndex)
+ {
+ if (!visibleSpanIndex.Any ()) {
+ return;
+ }
+
+ var snapshot = subjectBuffer.CurrentSnapshot;
+ var document = _workspace.CurrentSolution.GetDocument (this.Id);
+ if (!document.SupportsSyntaxTree) {
+ return;
+ }
+
+ var originalText = document.GetTextAsync (CancellationToken.None).WaitAndGetResult (CancellationToken.None);
+ Contract.Requires (object.ReferenceEquals (originalText, snapshot.AsText ()));
+
+ var root = document.GetSyntaxRootSynchronously (CancellationToken.None);
+
+ var editorOptionsFactory = CompositionManager.GetExportedValue<IEditorOptionsFactoryService> ();
+ var editorOptions = editorOptionsFactory.GetOptions (DataBuffer);
+ var options = _workspace.Options
+ .WithChangedOption (FormattingOptions.UseTabs, root.Language, !editorOptions.IsConvertTabsToSpacesEnabled ())
+ .WithChangedOption (FormattingOptions.TabSize, root.Language, editorOptions.GetTabSize ())
+ .WithChangedOption (FormattingOptions.IndentationSize, root.Language, editorOptions.GetIndentSize ());
+
+ using (var pooledObject = SharedPools.Default<List<TextSpan>> ().GetPooledObject ()) {
+ var spans = pooledObject.Object;
+
+ spans.AddRange (this.GetEditorVisibleSpans ());
+ using (var edit = subjectBuffer.CreateEdit (s_venusEditOptions, reiteratedVersionNumber: null, editTag: null)) {
+ foreach (var spanIndex in visibleSpanIndex) {
+ var rule = GetBaseIndentationRule (root, originalText, spans, spanIndex);
+
+ var visibleSpan = spans[spanIndex];
+ AdjustIndentationForSpan (document, edit, visibleSpan, rule, options);
+ }
+
+ edit.Apply ();
+ }
+ }
+ }
+
+ private void AdjustIndentationForSpan (
+ Document document, ITextEdit edit, TextSpan visibleSpan, IFormattingRule baseIndentationRule, OptionSet options)
+ {
+ var root = document.GetSyntaxRootSynchronously (CancellationToken.None);
+
+ using (var rulePool = SharedPools.Default<List<IFormattingRule>> ().GetPooledObject ())
+ using (var spanPool = SharedPools.Default<List<TextSpan>> ().GetPooledObject ()) {
+ var venusFormattingRules = rulePool.Object;
+ var visibleSpans = spanPool.Object;
+
+ venusFormattingRules.Add (baseIndentationRule);
+ venusFormattingRules.Add (ContainedDocumentPreserveFormattingRule.Instance);
+
+ var formattingRules = venusFormattingRules.Concat (Formatter.GetDefaultFormattingRules (document));
+
+ var workspace = document.Project.Solution.Workspace;
+ var changes = Formatter.GetFormattedTextChanges (
+ root, new TextSpan[] { CommonFormattingHelpers.GetFormattingSpan (root, visibleSpan) },
+ workspace, options, formattingRules, CancellationToken.None);
+
+ visibleSpans.Add (visibleSpan);
+ var newChanges = FilterTextChanges (document.GetTextAsync (CancellationToken.None).WaitAndGetResult (CancellationToken.None), visibleSpans, new ReadOnlyCollection<TextChange>(changes)).Where (t => visibleSpan.Contains (t.Span));
+
+ foreach (var change in newChanges) {
+ edit.Replace (new Span(change.Span.Start, change.Span.Length), change.NewText);
+ }
+ }
+ }
+
+ internal BaseIndentationFormattingRule GetBaseIndentationRule (SyntaxNode root, SourceText text, List<TextSpan> spans, int spanIndex)
+ {
+ var currentSpanIndex = spanIndex;
+ GetVisibleAndTextSpan (text, spans, currentSpanIndex, out var visibleSpan, out var visibleTextSpan);
+
+ var end = visibleSpan.End;
+ var current = root.FindToken (visibleTextSpan.Start).Parent;
+ while (current != null) {
+ if (current.Span.Start == visibleTextSpan.Start) {
+ var blockType = GetRazorCodeBlockType (visibleSpan.Start);
+ if (blockType == RazorCodeBlockType.Explicit) {
+ var baseIndentation = GetBaseIndentation (root, text, visibleSpan);
+ return new BaseIndentationFormattingRule (root, TextSpan.FromBounds (visibleSpan.Start, end), baseIndentation);
+ }
+ }
+
+ if (current.Span.Start < visibleSpan.Start) {
+ var blockType = GetRazorCodeBlockType (visibleSpan.Start);
+ if (blockType == RazorCodeBlockType.Block || blockType == RazorCodeBlockType.Helper) {
+ var baseIndentation = GetBaseIndentation (root, text, visibleSpan);
+ return new BaseIndentationFormattingRule (root, TextSpan.FromBounds (visibleSpan.Start, end), baseIndentation);
+ }
+
+ if (currentSpanIndex == 0) {
+ break;
+ }
+
+ GetVisibleAndTextSpan (text, spans, --currentSpanIndex, out visibleSpan, out visibleTextSpan);
+ continue;
+ }
+
+ current = current.Parent;
+ }
+
+ var span = spans[spanIndex];
+ var indentation = GetBaseIndentation (root, text, span);
+ return new BaseIndentationFormattingRule (root, span, indentation);
+ }
+
+ private void GetVisibleAndTextSpan (SourceText text, List<TextSpan> spans, int spanIndex, out TextSpan visibleSpan, out TextSpan visibleTextSpan)
+ {
+ visibleSpan = spans[spanIndex];
+
+ visibleTextSpan = GetVisibleTextSpan (text, visibleSpan);
+ if (visibleTextSpan.IsEmpty) {
+ // span has no text in them
+ visibleTextSpan = visibleSpan;
+ }
+ }
+
+ private int GetBaseIndentation (SyntaxNode root, SourceText text, TextSpan span)
+ {
+ // Is this right? We should probably get this from the IVsContainedLanguageHost instead.
+ var editorOptionsFactory = CompositionManager.GetExportedValue<IEditorOptionsFactoryService> ();
+ var editorOptions = editorOptionsFactory.GetOptions (DataBuffer);
+
+ var additionalIndentation = GetAdditionalIndentation (root, text, span);
+
+ // Skip over the first line, since it's in "Venus space" anyway.
+ var startingLine = text.Lines.GetLineFromPosition (span.Start);
+ for (var line = startingLine; line.Start < span.End; line = text.Lines[line.LineNumber + 1]) {
+ ContainedLanguageHost.GetLineIndent (
+ line.LineNumber,
+ out var baseIndentationString,
+ out var parent,
+ out var indentSize,
+ out var useTabs,
+ out var tabSize);
+
+ if (!string.IsNullOrEmpty (baseIndentationString)) {
+ return baseIndentationString.GetColumnFromLineOffset (baseIndentationString.Length, editorOptions.GetTabSize ()) + additionalIndentation;
+ }
+ }
+
+ return additionalIndentation;
+ }
+
+ private TextSpan GetVisibleTextSpan (SourceText text, TextSpan visibleSpan, bool uptoFirstAndLastLine = false)
+ {
+ var start = visibleSpan.Start;
+ for (; start < visibleSpan.End; start++) {
+ if (!char.IsWhiteSpace (text[start])) {
+ break;
+ }
+ }
+
+ var end = visibleSpan.End - 1;
+ if (start <= end) {
+ for (; start <= end; end--) {
+ if (!char.IsWhiteSpace (text[end])) {
+ break;
+ }
+ }
+ }
+
+ if (uptoFirstAndLastLine) {
+ var firstLine = text.Lines.GetLineFromPosition (visibleSpan.Start);
+ var lastLine = text.Lines.GetLineFromPosition (visibleSpan.End);
+
+ if (firstLine.LineNumber < lastLine.LineNumber) {
+ start = (start < firstLine.End) ? start : firstLine.End;
+ end = (lastLine.Start < end + 1) ? end : lastLine.Start - 1;
+ }
+ }
+
+ return (start <= end) ? TextSpan.FromBounds (start, end + 1) : default (TextSpan);
+ }
+
+ private int GetAdditionalIndentation (SyntaxNode root, SourceText text, TextSpan span)
+ {
+ var type = GetRazorCodeBlockType (span.Start);
+
+ // razor block
+ if (type == RazorCodeBlockType.Block) {
+ if (_workspace == null) {
+ // this can happen if the workspace creation isn't yet completed
+ return 0;
+ }
+
+ // more workaround for csharp razor case. when } for csharp razor code block is just typed, "}" exist
+ // in both subject and surface buffer and there is no easy way to figure out who owns } just typed.
+ // in this case, we let razor owns it. later razor will remove } from subject buffer if it is something
+ // razor owns.
+ var textSpan = GetVisibleTextSpan (text, span);
+ var end = textSpan.End - 1;
+ if (end >= 0 && text[end] == '}') {
+ var token = root.FindToken (end);
+ var syntaxFact = _workspace.Services.GetLanguageServices (Language).GetService<ISyntaxFactsService> ();
+ if (token.Span.Start == end && syntaxFact != null) {
+ if (syntaxFact.TryGetCorrespondingOpenBrace (token, out var openBrace) && !textSpan.Contains (openBrace.Span)) {
+ return 0;
+ }
+ }
+ }
+
+ return _workspace.Options.GetOption (FormattingOptions.IndentationSize, Language);
+ }
+
+ return 0;
+ }
+
+ private RazorCodeBlockType GetRazorCodeBlockType (int position)
+ {
+ var subjectBuffer = (IProjectionBuffer)LanguageBuffer;
+ var subjectSnapshot = subjectBuffer.CurrentSnapshot;
+ var surfaceSnapshot = DataBuffer.CurrentSnapshot;
+
+ var surfacePoint = surfaceSnapshot.MapFromSourceSnapshot (new SnapshotPoint (subjectSnapshot, position), PositionAffinity.Predecessor);
+ if (!surfacePoint.HasValue) {
+ // how this can happen?
+ return RazorCodeBlockType.Implicit;
+ }
+
+ var ch = char.ToLower (surfaceSnapshot[Math.Max (surfacePoint.Value - 1, 0)]);
+
+ // razor block
+ if (IsCodeBlock (surfaceSnapshot, surfacePoint.Value, ch)) {
+ return RazorCodeBlockType.Block;
+ }
+
+ if (ch == RazorExplicit) {
+ return RazorCodeBlockType.Explicit;
+ }
+
+ if (CheckCode (surfaceSnapshot, surfacePoint.Value, HelperRazor)) {
+ return RazorCodeBlockType.Helper;
+ }
+
+ return RazorCodeBlockType.Implicit;
+ }
+
+ private bool IsCodeBlock (ITextSnapshot surfaceSnapshot, int position, char ch)
+ {
+ return CheckCode (surfaceSnapshot, position, ch, CSharpRazorBlock) ||
+ CheckCode (surfaceSnapshot, position, ch, FunctionsRazor, CSharpRazorBlock);
+ }
+
+ private bool CheckCode (ITextSnapshot snapshot, int position, char ch, string tag, bool checkAt = true)
+ {
+ if (ch != tag[tag.Length - 1] || position < tag.Length) {
+ return false;
+ }
+
+ var start = position - tag.Length;
+ var razorTag = snapshot.GetText (start, tag.Length);
+ return string.Equals (razorTag, tag, StringComparison.OrdinalIgnoreCase) && (!checkAt || snapshot[start - 1] == RazorExplicit);
+ }
+
+ private bool CheckCode (ITextSnapshot snapshot, int position, string tag)
+ {
+ int i = position - 1;
+ if (i < 0) {
+ return false;
+ }
+
+ for (; i >= 0; i--) {
+ if (!char.IsWhiteSpace (snapshot[i])) {
+ break;
+ }
+ }
+
+ var ch = snapshot[i];
+ position = i + 1;
+
+ return CheckCode (snapshot, position, ch, tag);
+ }
+
+ private bool CheckCode (ITextSnapshot snapshot, int position, char ch, string tag1, string tag2)
+ {
+ if (!CheckCode (snapshot, position, ch, tag2, checkAt: false)) {
+ return false;
+ }
+
+ return CheckCode (snapshot, position - tag2.Length, tag1);
+ }
+
+ private enum RazorCodeBlockType
+ {
+ Block,
+ Explicit,
+ Implicit,
+ Helper
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/Razor/MonoDevelopFormattingRuleFactoryServiceFactory.cs b/main/src/addins/CSharpBinding/Razor/MonoDevelopFormattingRuleFactoryServiceFactory.cs
new file mode 100644
index 0000000000..5c69695620
--- /dev/null
+++ b/main/src/addins/CSharpBinding/Razor/MonoDevelopFormattingRuleFactoryServiceFactory.cs
@@ -0,0 +1,91 @@
+using System;
+using System.Collections.Generic;
+using System.Composition;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Formatting.Rules;
+using Microsoft.CodeAnalysis.Host;
+using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.CodeAnalysis.Text;
+
+namespace Microsoft.VisualStudio.Platform
+{
+ [ExportWorkspaceServiceFactory (typeof (IHostDependentFormattingRuleFactoryService), ServiceLayer.Host), Shared]
+ internal sealed class MonoDevelopFormattingRuleFactoryServiceFactory : IWorkspaceServiceFactory
+ {
+ public MonoDevelopFormattingRuleFactoryServiceFactory ()
+ {
+ }
+
+ public IWorkspaceService CreateService (HostWorkspaceServices workspaceServices)
+ {
+ return new Factory ();
+ }
+
+ private sealed class Factory : IHostDependentFormattingRuleFactoryService
+ {
+ private readonly IFormattingRule _noopRule = new NoOpFormattingRule ();
+
+ public bool ShouldUseBaseIndentation (Document document)
+ {
+ return IsContainedDocument (document);
+ }
+
+ public bool ShouldNotFormatOrCommitOnPaste (Document document)
+ {
+ return IsContainedDocument (document);
+ }
+
+ private bool IsContainedDocument (Document document)
+ {
+ MonoDevelopContainedDocument containedDocument = MonoDevelopContainedDocument.FromDocument (document);
+
+ return (containedDocument != null);
+ }
+
+ public IFormattingRule CreateRule (Document document, int position)
+ {
+ MonoDevelopContainedDocument containedDocument = MonoDevelopContainedDocument.FromDocument (document);
+ if (containedDocument == null)
+ {
+ return _noopRule;
+ }
+
+ List<TextSpan> spans = new List<TextSpan>();
+
+ var root = document.GetSyntaxRootSynchronously (CancellationToken.None);
+ var text = root.SyntaxTree.GetText (CancellationToken.None);
+
+ spans.AddRange (containedDocument.GetEditorVisibleSpans ());
+
+ for (var i = 0; i < spans.Count; i++) {
+ var visibleSpan = spans[i];
+ if (visibleSpan.IntersectsWith (position) || visibleSpan.End == position) {
+ return containedDocument.GetBaseIndentationRule (root, text, spans, i);
+ }
+ }
+
+ // in razor (especially in @helper tag), it is possible for us to be asked for next line of visible span
+ var line = text.Lines.GetLineFromPosition (position);
+ if (line.LineNumber > 0) {
+ line = text.Lines[line.LineNumber - 1];
+
+ // find one that intersects with previous line
+ for (var i = 0; i < spans.Count; i++) {
+ var visibleSpan = spans[i];
+ if (visibleSpan.IntersectsWith (line.Span)) {
+ return containedDocument.GetBaseIndentationRule (root, text, spans, i);
+ }
+ }
+ }
+
+ throw new InvalidOperationException ();
+ }
+
+ public IEnumerable<TextChange> FilterFormattedChanges (Document document, TextSpan span, IList<TextChange> changes)
+ {
+ return changes;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/CSharpBinding/Razor/MyCSharpCompletionData.cs b/main/src/addins/CSharpBinding/Razor/MyCSharpCompletionData.cs
new file mode 100644
index 0000000000..2687919e92
--- /dev/null
+++ b/main/src/addins/CSharpBinding/Razor/MyCSharpCompletionData.cs
@@ -0,0 +1,114 @@
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Completion;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Utilities;
+using MonoDevelop.Core;
+using MonoDevelop.Ide.Editor;
+
+namespace MonoDevelop.Ide.CodeCompletion
+{
+ public class MyCSharpCompletionData : MyRoslynCompletionData
+ {
+ public MyCSharpCompletionData (Microsoft.CodeAnalysis.Document document, ITextSnapshot triggerSnapshot, CompletionService completionService, CompletionItem completionItem) :
+ base (document, triggerSnapshot, completionService, completionItem)
+ {
+ }
+
+ protected override string MimeType => "text/csharp";
+
+ protected override void Format (TextEditor editor, Gui.Document document, SnapshotPoint start, SnapshotPoint end)
+ {
+ return;
+ //MonoDevelop.CSharp.Formatting.OnTheFlyFormatter.Format (editor, document, start, end);
+ }
+
+ public override IconId Icon {
+ get {
+ IconId result = base.Icon;
+ if (!result.IsNull) {
+ return result;
+ }
+
+ var modifier = GetItemModifier ();
+ var type = GetItemType ();
+ return "md-" + modifier + type;
+ }
+ }
+
+ static Dictionary<string, string> roslynCompletionTypeTable = new Dictionary<string, string> {
+ { "Field", "field" },
+ { "Alias", "field" },
+ { "ArrayType", "field" },
+ { "Assembly", "field" },
+ { "DynamicType", "field" },
+ { "ErrorType", "field" },
+ { "Label", "field" },
+ { "NetModule", "field" },
+ { "PointerType", "field" },
+ { "RangeVariable", "field" },
+ { "TypeParameter", "field" },
+ { "Preprocessing", "field" },
+
+ { "Constant", "literal" },
+
+ { "Parameter", "variable" },
+ { "Local", "variable" },
+
+ { "Method", "method" },
+
+ { "Namespace", "name-space" },
+
+ { "Property", "property" },
+
+ { "Event", "event" },
+
+ { "Class", "class" },
+
+ { "Delegate", "delegate" },
+
+ { "Enum", "enum" },
+
+ { "Interface", "interface" },
+
+ { "Struct", "struct" },
+ { "Structure", "struct" },
+
+ { "Keyword", "keyword" },
+
+ { "Snippet", "template"},
+
+ { "EnumMember", "literal" },
+
+ { "NewMethod", "newmethod" }
+ };
+
+ string GetItemType ()
+ {
+ foreach (var tag in CompletionItem.Tags) {
+ if (roslynCompletionTypeTable.TryGetValue (tag, out string result))
+ return result;
+ }
+ LoggingService.LogWarning ("RoslynCompletionData: Can't find item type '" + string.Join (",", CompletionItem.Tags) + "'");
+ return "literal";
+ }
+
+ static Dictionary<string, string> modifierTypeTable = new Dictionary<string, string> {
+ { "Private", "private-" },
+ { "ProtectedAndInternal", "ProtectedOrInternal-" },
+ { "Protected", "protected-" },
+ { "Internal", "internal-" },
+ { "ProtectedOrInternal", "ProtectedOrInternal-" }
+ };
+
+ string GetItemModifier ()
+ {
+ foreach (var tag in CompletionItem.Tags) {
+ if (modifierTypeTable.TryGetValue (tag, out string result))
+ return result;
+ }
+ return "";
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/CSharpBinding/Razor/MyCSharpCompletionDataProvider.cs b/main/src/addins/CSharpBinding/Razor/MyCSharpCompletionDataProvider.cs
new file mode 100644
index 0000000000..19279eb342
--- /dev/null
+++ b/main/src/addins/CSharpBinding/Razor/MyCSharpCompletionDataProvider.cs
@@ -0,0 +1,19 @@
+using System.ComponentModel.Composition;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Completion;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Utilities;
+using MonoDevelop.Ide.Editor;
+
+namespace MonoDevelop.Ide.CodeCompletion
+{
+ [Export (typeof (IMyRoslynCompletionDataProvider))]
+ [ContentType ("CSharp")]
+ public class MyCSharpCompletionDataProvider : IMyRoslynCompletionDataProvider
+ {
+ public MyRoslynCompletionData CreateCompletionData (Document document, ITextSnapshot textSnapshtot, CompletionService completionService, CompletionItem item)
+ {
+ return new MyCSharpCompletionData (document, textSnapshtot, completionService, item);
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/CSharpBinding/Razor/MyRoslynCompletionData.cs b/main/src/addins/CSharpBinding/Razor/MyRoslynCompletionData.cs
new file mode 100644
index 0000000000..8a1a43cc88
--- /dev/null
+++ b/main/src/addins/CSharpBinding/Razor/MyRoslynCompletionData.cs
@@ -0,0 +1,188 @@
+//
+// RoslynCompletionData.cs
+//
+// Author:
+// Mike Krüger <mikkrg@microsoft.com>
+//
+// Copyright (c) 2017 Microsoft Corporation
+//
+// 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 System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Completion;
+using Microsoft.CodeAnalysis.Text;
+using MonoDevelop.Core;
+using MonoDevelop.Ide.CodeCompletion;
+using MonoDevelop.Ide.Editor;
+using MonoDevelop.Ide.Editor.Extension;
+using MonoDevelop.Ide;
+using MonoDevelop.Ide.Gui;
+using Microsoft.VisualStudio.Platform;
+using Microsoft.VisualStudio.Text;
+using MonoDevelop.Ide.CodeTemplates;
+using System.Linq;
+using System.Text;
+using MonoDevelop.Ide.Editor.Highlighting;
+using MonoDevelop.Ide.Fonts;
+
+namespace MonoDevelop.Ide.CodeCompletion
+{
+ public abstract class MyRoslynCompletionData : CompletionData
+ {
+ protected readonly Microsoft.CodeAnalysis.Document doc;
+ protected readonly ITextSnapshot triggerSnapshot;
+ protected readonly CompletionService completionService;
+
+ public CompletionItem CompletionItem { get; private set; }
+
+ public CompletionItemRules Rules { get { return CompletionItem.Rules; } }
+
+ public override string DisplayText {
+ get {
+ return CompletionItem.DisplayText;
+ }
+ }
+
+ public override string Description {
+ get {
+ var description = completionService.GetDescriptionAsync (doc, CompletionItem).Result;
+ return description.Text;
+ }
+ }
+
+ public override string CompletionText {
+ get {
+ return CompletionItem.DisplayText;
+ }
+ }
+
+ protected abstract string MimeType { get; }
+
+ public override IconId Icon {
+ get {
+ if (CompletionItem.Tags.Contains ("Snippet")) {
+ var template = CodeTemplateService.GetCodeTemplates (MimeType).FirstOrDefault (t => t.Shortcut == CompletionItem.DisplayText);
+ if (template != null)
+ return template.Icon;
+ }
+
+ return null;
+ }
+ }
+
+ public MyRoslynCompletionData (Microsoft.CodeAnalysis.Document document, ITextSnapshot triggerSnapshot, CompletionService completionService, CompletionItem completionItem)
+ {
+ this.doc = document;
+ this.triggerSnapshot = triggerSnapshot;
+ this.completionService = completionService;
+ CompletionItem = completionItem;
+ }
+
+ public override string GetDisplayDescription (bool isSelected)
+ {
+ if (CompletionItem.Properties.TryGetValue ("DescriptionMarkup", out string result))
+ return result;
+ return base.GetDisplayDescription (isSelected);
+ }
+
+ public override string GetRightSideDescription (bool isSelected)
+ {
+ if (CompletionItem.Properties.TryGetValue ("RightSideMarkup", out string result))
+ return result;
+ return null;
+ }
+
+ public override DisplayFlags DisplayFlags {
+ get {
+ DisplayFlags result = DisplayFlags.None;
+
+ if (CompletionItem.Tags.Contains ("bold")) {
+ result = DisplayFlags.MarkedBold;
+ }
+
+ return result;
+ }
+ }
+
+ public override void InsertCompletionText (CompletionListWindow window, ref KeyActions ka, KeyDescriptor descriptor)
+ {
+ var document = IdeApp.Workbench.ActiveDocument;
+ var editor = document?.Editor;
+ if (editor == null || completionService == null) {
+ base.InsertCompletionText (window, ref ka, descriptor);
+ return;
+ }
+ var completionChange = completionService.GetChangeAsync (doc, CompletionItem, null, default (CancellationToken)).WaitAndGetResult (default (CancellationToken));
+
+ var currentBuffer = editor.GetPlatformTextBuffer ();
+ var textChange = completionChange.TextChange;
+
+ var triggerSnapshotSpan = new SnapshotSpan (triggerSnapshot, new Span (textChange.Span.Start, textChange.Span.Length));
+
+ var mappedSpan = triggerSnapshotSpan.TranslateTo (triggerSnapshot.TextBuffer.CurrentSnapshot, SpanTrackingMode.EdgeInclusive);
+
+ triggerSnapshot.TextBuffer.Replace (mappedSpan, completionChange.TextChange.NewText);
+// editor.ReplaceText (mappedSpan.Start, mappedSpan.Length, completionChange.TextChange.NewText);
+
+ if (completionChange.NewPosition.HasValue)
+ editor.CaretOffset = completionChange.NewPosition.Value;
+
+ if (CompletionItem.Rules.FormatOnCommit) {
+ var endOffset = mappedSpan.Start + completionChange.TextChange.NewText.Length;
+ Format (editor, document, mappedSpan.Start, endOffset);
+ }
+ }
+
+ protected abstract void Format (TextEditor editor, Gui.Document document, SnapshotPoint start, SnapshotPoint end);
+
+ public override async Task<TooltipInformation> CreateTooltipInformation (bool smartWrap, CancellationToken cancelToken)
+ {
+ var description = await completionService.GetDescriptionAsync (doc, CompletionItem);
+ var markup = new StringBuilder ();
+ var theme = DefaultSourceEditorOptions.Instance.GetEditorTheme ();
+ var taggedParts = description.TaggedParts;
+ int i = 0;
+ while (i < taggedParts.Length) {
+ if (taggedParts[i].Tag == "LineBreak")
+ break;
+ i++;
+ }
+ if (i + 1 >= taggedParts.Length) {
+ markup.AppendTaggedText (theme, taggedParts);
+ }
+ else {
+ markup.AppendTaggedText (theme, taggedParts.Take (i));
+ markup.Append ("<span font='" + FontService.SansFontName + "' size='small'>");
+ markup.AppendLine ();
+ markup.AppendLine ();
+ markup.AppendTaggedText (theme, taggedParts.Skip (i + 1), 0, 50);
+ markup.Append ("</span>");
+ }
+ return new TooltipInformation {
+ SignatureMarkup = markup.ToString ()
+ };
+ }
+ }
+
+}
diff --git a/main/src/addins/CSharpBinding/Razor/RoslynCommandTarget.cs b/main/src/addins/CSharpBinding/Razor/RoslynCommandTarget.cs
new file mode 100644
index 0000000000..b35d73456e
--- /dev/null
+++ b/main/src/addins/CSharpBinding/Razor/RoslynCommandTarget.cs
@@ -0,0 +1,121 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+//
+
+using System;
+using Microsoft.CodeAnalysis.Editor;
+using Microsoft.CodeAnalysis.Editor.Commands;
+using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
+using Microsoft.CodeAnalysis.Utilities;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+using MonoDevelop.Ide.Composition;
+
+namespace Microsoft.VisualStudio.Platform
+{
+ public class RoslynCommandTarget
+ {
+ internal ICommandHandlerService CurrentHandlers { get; set; }
+
+ internal ITextBuffer _languageBuffer;
+ internal ITextView _textView;
+
+ static RoslynCommandTarget()
+ {
+ var defaultForegroundThreadData = ForegroundThreadData.CreateDefault(
+ defaultKind: ForegroundThreadDataKind.ForcedByPackageInitialize);
+ ForegroundThreadAffinitizedObject.CurrentForegroundThreadData = defaultForegroundThreadData;
+ }
+
+ private RoslynCommandTarget(ITextView textView, ITextBuffer languageBuffer)
+ {
+ ICommandHandlerServiceFactory commandHandlerServiceFactory = CompositionManager.GetExportedValue<ICommandHandlerServiceFactory>();
+
+ if (commandHandlerServiceFactory != null)
+ {
+ commandHandlerServiceFactory.Initialize (languageBuffer.ContentType.TypeName);
+
+ CurrentHandlers = commandHandlerServiceFactory.GetService (languageBuffer);
+ }
+
+ _languageBuffer = languageBuffer;
+ _textView = textView;
+ }
+
+ public static RoslynCommandTarget FromViewAndBuffer(ITextView textView, ITextBuffer languageBuffer)
+ {
+ return languageBuffer.Properties.GetOrCreateSingletonProperty<RoslynCommandTarget>(() => new RoslynCommandTarget(textView, languageBuffer));
+ }
+
+ public void ExecuteTypeCharacter(char typedChar, Action lastHandler)
+ {
+ CurrentHandlers?.Execute (_languageBuffer.ContentType,
+ args: new TypeCharCommandArgs (_textView, _languageBuffer, typedChar),
+ lastHandler: lastHandler);
+ }
+
+ public void ExecuteTab (Action executeNextCommandTarget)
+ {
+ CurrentHandlers.Execute (_languageBuffer.ContentType,
+ args: new TabKeyCommandArgs (_textView, _languageBuffer),
+ lastHandler: executeNextCommandTarget);
+ }
+
+ public void ExecuteBackspace(Action executeNextCommandTarget)
+ {
+ CurrentHandlers.Execute (_languageBuffer.ContentType,
+ args: new BackspaceKeyCommandArgs (_textView, _languageBuffer),
+ lastHandler: executeNextCommandTarget);
+ }
+
+ public void ExecuteDelete(Action executeNextCommandTarget)
+ {
+ CurrentHandlers.Execute (_languageBuffer.ContentType,
+ args: new DeleteKeyCommandArgs (_textView, _languageBuffer),
+ lastHandler: executeNextCommandTarget);
+ }
+
+ public void ExecuteReturn(Action executeNextCommandTarget)
+ {
+ CurrentHandlers.Execute (_languageBuffer.ContentType,
+ args: new ReturnKeyCommandArgs (_textView, _languageBuffer),
+ lastHandler: executeNextCommandTarget);
+ }
+
+ public void ExecuteUp (Action executeNextCommandTarget)
+ {
+ CurrentHandlers.Execute (_languageBuffer.ContentType,
+ args: new UpKeyCommandArgs (_textView, _languageBuffer),
+ lastHandler: executeNextCommandTarget);
+ }
+
+ public void ExecuteDown (Action executeNextCommandTarget)
+ {
+ CurrentHandlers.Execute (_languageBuffer.ContentType,
+ args: new DownKeyCommandArgs (_textView, _languageBuffer),
+ lastHandler: executeNextCommandTarget);
+ }
+
+ public void ExecuteUncommentBlock(Action executeNextCommandTarget)
+ {
+ CurrentHandlers.Execute (_languageBuffer.ContentType,
+ args: new UncommentSelectionCommandArgs (_textView, _languageBuffer),
+ lastHandler: executeNextCommandTarget);
+ }
+
+ public void ExecuteCommentBlock(Action executeNextCommandTarget)
+ {
+ CurrentHandlers.Execute (_languageBuffer.ContentType,
+ args: new CommentSelectionCommandArgs (_textView, _languageBuffer),
+ lastHandler: executeNextCommandTarget);
+ }
+
+ public void ExecuteInvokeCompletionList (Action executeNextCommandTarget)
+ {
+ CurrentHandlers.Execute (_languageBuffer.ContentType,
+ args: new InvokeCompletionListCommandArgs (_textView, _languageBuffer),
+ lastHandler: executeNextCommandTarget);
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/CSharpBinding/Razor/RoslynCompletionPresenter.cs b/main/src/addins/CSharpBinding/Razor/RoslynCompletionPresenter.cs
new file mode 100644
index 0000000000..ba934127fa
--- /dev/null
+++ b/main/src/addins/CSharpBinding/Razor/RoslynCompletionPresenter.cs
@@ -0,0 +1,81 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+//
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Completion;
+using Microsoft.CodeAnalysis.Editor;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.VisualStudio.Language.Intellisense;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Utilities;
+using MonoDevelop.Ide;
+using MonoDevelop.Ide.CodeCompletion;
+
+namespace MonoDevelop.Ide.CodeCompletion
+{
+ public interface IContentTypeMetadata
+ {
+ IEnumerable<string> ContentTypes { get; }
+ }
+
+
+ [Export (typeof (IIntelliSensePresenter<ICompletionPresenterSession, ICompletionSession>))]
+ [Name (PredefinedCompletionPresenterNames.RoslynCompletionPresenter)]
+ [ContentType (ContentTypeNames.RoslynContentType)]
+ internal sealed class RoslynCompletionPresenter : IIntelliSensePresenter<ICompletionPresenterSession, ICompletionSession>
+ {
+ [ImportMany (typeof (IMyRoslynCompletionDataProvider))]
+ internal List<Lazy<IMyRoslynCompletionDataProvider, IContentTypeMetadata>> _completionDataProviders { get; set; }
+
+ public ICompletionPresenterSession CreateSession (ITextView textView, ITextBuffer subjectBuffer, ICompletionSession sessionOpt)
+ {
+ foreach (var completionDataProviderHandle in _completionDataProviders) {
+ foreach (string contentTypeName in completionDataProviderHandle.Metadata.ContentTypes) {
+ if (string.Compare (subjectBuffer.ContentType.TypeName, contentTypeName, StringComparison.OrdinalIgnoreCase) == 0) {
+ string languageName;
+ if (TryGetLanguageNameFromContentType (subjectBuffer.ContentType, out languageName)) {
+ if (Workspace.TryGetWorkspace (subjectBuffer.AsTextContainer (), out var workspace)) {
+ CompletionService completionService = workspace.Services.GetLanguageServices (languageName).GetService<CompletionService> ();
+ return new RoslynCompletionPresenterSession (textView, subjectBuffer, completionDataProviderHandle.Value, completionService);
+ }
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ // TODO: Remove this
+ private static bool TryGetLanguageNameFromContentType (IContentType contentType, out string languageName)
+ {
+ if (contentType.IsOfType ("htmlx")) {
+ languageName = "HTML";
+ }
+ else if (contentType.IsOfType ("css")) {
+ languageName = "CSS";
+ }
+ else if (contentType.IsOfType ("JSON")) {
+ languageName = "JSON";
+ }
+ else if (contentType.IsOfType ("CSharp")) {
+ languageName = LanguageNames.CSharp;
+ }
+ else if (contentType.IsOfType ("TypeScript")) {
+ languageName = "TypeScript";
+ }
+ else {
+ languageName = null;
+ }
+
+ return languageName != null;
+ }
+
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/CSharpBinding/Razor/RoslynCompletionPresenterSession.cs b/main/src/addins/CSharpBinding/Razor/RoslynCompletionPresenterSession.cs
new file mode 100644
index 0000000000..7660ba024a
--- /dev/null
+++ b/main/src/addins/CSharpBinding/Razor/RoslynCompletionPresenterSession.cs
@@ -0,0 +1,155 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Completion;
+using Microsoft.CodeAnalysis.Editor;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Utilities;
+using MonoDevelop.Core;
+using MonoDevelop.Ide;
+using MonoDevelop.Ide.CodeCompletion;
+using MonoDevelop.Ide.CodeTemplates;
+using MonoDevelop.Ide.Editor;
+using MonoDevelop.Ide.Editor.Extension;
+using MonoDevelop.Ide.Editor.Highlighting;
+using MonoDevelop.Ide.Fonts;
+
+namespace MonoDevelop.Ide.CodeCompletion
+{
+ internal class RoslynCompletionPresenterSession : ICompletionPresenterSession
+ {
+ private ITextView _textView;
+ private ITextBuffer _subjectBuffer;
+ private Document _document;
+ private CompletionService _completionService;
+ private IMyRoslynCompletionDataProvider _completionDataProvider;
+ private bool _isAdvised = false;
+
+ public event EventHandler<CompletionItemEventArgs> ItemSelected;
+ public event EventHandler<CompletionItemEventArgs> ItemCommitted;
+ public event EventHandler<CompletionItemFilterStateChangedEventArgs> FilterStateChanged;
+ public event EventHandler<EventArgs> Dismissed;
+
+
+ public RoslynCompletionPresenterSession (ITextView textView, ITextBuffer subjectBuffer, IMyRoslynCompletionDataProvider completionDataProvider, CompletionService completionService)
+ {
+ _textView = textView;
+ _subjectBuffer = subjectBuffer;
+ _completionDataProvider = completionDataProvider;
+ _completionService = completionService;
+ }
+
+ public void Dismiss ()
+ {
+ CompletionWindowManager.HideWindow();
+ }
+
+ public void PresentItems (ITrackingSpan triggerSpan, IList<CompletionItem> items, CompletionItem selectedItem, CompletionItem suggestionModeItem, bool suggestionMode, bool isSoftSelected, ImmutableArray<CompletionItemFilter> completionItemFilters, string filterText)
+ {
+ var result = new CompletionDataList ();
+
+ foreach (var item in items) {
+ if (string.IsNullOrEmpty (item.DisplayText))
+ continue;
+ result.Add (WrapItem (item));
+ }
+
+ if (suggestionMode)
+ result.AutoSelect = false;
+ if (filterText != null)
+ result.DefaultCompletionString = filterText;
+ if (suggestionModeItem != null) {
+ result.DefaultCompletionString = suggestionModeItem.DisplayText;
+ result.AutoSelect = false;
+ }
+
+ if (selectedItem != null) {
+ result.DefaultCompletionString = selectedItem.DisplayText;
+ }
+
+ // TODO: isSoftSelected
+ // TODO: completionItemFilters
+ var editor = IdeApp.Workbench.ActiveDocument.Editor;
+ CompletionTextEditorExtension completionEditorExtension = editor.GetContent<CompletionTextEditorExtension> ();
+ completionEditorExtension.ShowCompletion (result);
+
+ if (!_isAdvised)
+ {
+ CompletionWindowManager.Wnd.SelectionChanged += OnSelectionChanged;
+ CompletionWindowManager.WordCompleted += OnWordCompleted;
+
+ CompletionWindowManager.WindowClosed += OnWindowClosed;
+
+ // TODO: Would be nice it we could better detect whether we've already advised on the completion window
+ _isAdvised = true;
+ }
+ }
+
+ private void OnWordCompleted (object sender, CodeCompletionContextEventArgs e)
+ {
+ MyRoslynCompletionData completionData = (MyRoslynCompletionData) CompletionWindowManager.Wnd.SelectedItem;
+ ItemCommitted?.Invoke (this, new CompletionItemEventArgs (completionData.CompletionItem));
+ }
+
+ private void OnWindowClosed (object sender, EventArgs e)
+ {
+ _isAdvised = false;
+
+ CompletionWindowManager.Wnd.SelectionChanged -= OnSelectionChanged;
+ CompletionWindowManager.WordCompleted -= OnWordCompleted;
+
+ CompletionWindowManager.WindowClosed -= OnWindowClosed;
+
+ Dismissed?.Invoke (this, EventArgs.Empty);
+ }
+
+ private void OnSelectionChanged (object sender, EventArgs e)
+ {
+ MyRoslynCompletionData completionData = (MyRoslynCompletionData) CompletionWindowManager.Wnd.SelectedItem;
+ ItemSelected?.Invoke (this, new CompletionItemEventArgs (completionData.CompletionItem));
+ }
+
+ public virtual MyRoslynCompletionData WrapItem (CompletionItem item)
+ {
+ Document document = _subjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges();
+ return _completionDataProvider.CreateCompletionData(document, _subjectBuffer.CurrentSnapshot, _completionService, item);
+ }
+
+ public void SelectPreviousItem ()
+ {
+ CompletionWindowManager.PreProcessKeyEvent (Editor.Extension.KeyDescriptor.Up);
+ }
+
+ public void SelectNextItem ()
+ {
+ CompletionWindowManager.PreProcessKeyEvent (Editor.Extension.KeyDescriptor.Down);
+ }
+
+ public void SelectPreviousPageItem ()
+ {
+ CompletionWindowManager.PreProcessKeyEvent (Editor.Extension.KeyDescriptor.PageUp);
+ }
+
+ public void SelectNextPageItem ()
+ {
+ CompletionWindowManager.PreProcessKeyEvent (Editor.Extension.KeyDescriptor.PageDown);
+ }
+
+ internal void OnCompletionItemCommitted(CompletionItem completionItem)
+ {
+ ItemCommitted?.Invoke(this, new CompletionItemEventArgs(completionItem));
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj
index bdfe3f4405..4a6c516637 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<Import Project="..\..\..\MonoDevelop.props" />
+ <Import Project="$(ReferencesVSEditor)" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DisassemblyView.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DisassemblyView.cs
index 789aaed73e..e62af36fe0 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DisassemblyView.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DisassemblyView.cs
@@ -383,7 +383,7 @@ namespace MonoDevelop.Debugger
void IClipboardHandler.Copy ()
{
- editor.EditorActionHost.ClipboardCopy ();
+ editor.EditorOperations.CopySelection ();
}
void IClipboardHandler.Paste ()
@@ -398,7 +398,7 @@ namespace MonoDevelop.Debugger
void IClipboardHandler.SelectAll ()
{
- editor.EditorActionHost.SelectAll ();
+ editor.EditorOperations.SelectAll ();
}
bool IClipboardHandler.EnableCut {
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Refactoring/PackageCodeDiagnosticProvider.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Refactoring/PackageCodeDiagnosticProvider.cs
index 8b87157e1a..6c0b36215b 100644
--- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Refactoring/PackageCodeDiagnosticProvider.cs
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Refactoring/PackageCodeDiagnosticProvider.cs
@@ -1,113 +1,113 @@
-//
-// PackageCodeDiagnosticProvider.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.
+////
+//// PackageCodeDiagnosticProvider.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.CodeIssues;
-using MonoDevelop.Core;
-using MonoDevelop.Projects;
-using System.Linq;
-using System.IO;
-using System.Collections.Generic;
-using System.Reflection;
-using System.Threading;
-using System.Threading.Tasks;
-using MonoDevelop.Ide.Editor;
-using MonoDevelop.Ide.TypeSystem;
-using MonoDevelop.Ide;
+//using System;
+//using MonoDevelop.CodeIssues;
+//using MonoDevelop.Core;
+//using MonoDevelop.Projects;
+//using System.Linq;
+//using System.IO;
+//using System.Collections.Generic;
+//using System.Reflection;
+//using System.Threading;
+//using System.Threading.Tasks;
+//using MonoDevelop.Ide.Editor;
+//using MonoDevelop.Ide.TypeSystem;
+//using MonoDevelop.Ide;
-namespace MonoDevelop.PackageManagement.Refactoring
-{
- sealed class PackageCodeDiagnosticProvider : CodeDiagnosticProvider
- {
- static readonly Dictionary<Project, AnalyzersFromAssembly> diagnosticCache = new Dictionary<Project, AnalyzersFromAssembly> ();
+//namespace MonoDevelop.PackageManagement.Refactoring
+//{
+// sealed class PackageCodeDiagnosticProvider : CodeDiagnosticProvider
+// {
+// static readonly Dictionary<Project, AnalyzersFromAssembly> diagnosticCache = new Dictionary<Project, AnalyzersFromAssembly> ();
- static PackageCodeDiagnosticProvider ()
- {
- IdeApp.Workspace.SolutionUnloaded += delegate {
- diagnosticCache.Clear ();
- };
- IdeApp.Workspace.ActiveConfigurationChanged += delegate {
- diagnosticCache.Clear ();
- };
- }
+// static PackageCodeDiagnosticProvider ()
+// {
+// IdeApp.Workspace.SolutionUnloaded += delegate {
+// diagnosticCache.Clear ();
+// };
+// IdeApp.Workspace.ActiveConfigurationChanged += delegate {
+// diagnosticCache.Clear ();
+// };
+// }
- public async override Task<IEnumerable<CodeDiagnosticFixDescriptor>> GetCodeFixDescriptorsAsync (DocumentContext document, string language, CancellationToken cancellationToken = default(CancellationToken))
- {
- var diags = await GetProjectDiagnosticsAsync (document.Project, language, cancellationToken).ConfigureAwait (false);
- return diags.Fixes;
- }
+// public async override Task<IEnumerable<CodeDiagnosticFixDescriptor>> GetCodeFixDescriptorsAsync (DocumentContext document, string language, CancellationToken cancellationToken = default(CancellationToken))
+// {
+// var diags = await GetProjectDiagnosticsAsync (document.Project, language, cancellationToken).ConfigureAwait (false);
+// return diags.Fixes;
+// }
- public async override Task<IEnumerable<CodeDiagnosticDescriptor>> GetCodeDiagnosticDescriptorsAsync (DocumentContext document, string language, CancellationToken cancellationToken = default(CancellationToken))
- {
- var diags = await GetProjectDiagnosticsAsync (document.Project, language, cancellationToken).ConfigureAwait (false);
- return diags.Analyzers;
- }
+// public async override Task<IEnumerable<CodeDiagnosticDescriptor>> GetCodeDiagnosticDescriptorsAsync (DocumentContext document, string language, CancellationToken cancellationToken = default(CancellationToken))
+// {
+// var diags = await GetProjectDiagnosticsAsync (document.Project, language, cancellationToken).ConfigureAwait (false);
+// return diags.Analyzers;
+// }
- public async override Task<IEnumerable<MonoDevelop.CodeActions.CodeRefactoringDescriptor>> GetCodeRefactoringDescriptorsAsync (DocumentContext document, string language, CancellationToken cancellationToken)
- {
- var diags = await GetProjectDiagnosticsAsync (document.Project, language, cancellationToken).ConfigureAwait (false);
- return diags.Refactorings;
- }
+// public async override Task<IEnumerable<MonoDevelop.CodeActions.CodeRefactoringDescriptor>> GetCodeRefactoringDescriptorsAsync (DocumentContext document, string language, CancellationToken cancellationToken)
+// {
+// var diags = await GetProjectDiagnosticsAsync (document.Project, language, cancellationToken).ConfigureAwait (false);
+// return diags.Refactorings;
+// }
- static async Task<AnalyzersFromAssembly> GetProjectDiagnosticsAsync (Project project, string language, CancellationToken cancellationToken)
- {
- if (project == null)
- return AnalyzersFromAssembly.Empty;
- AnalyzersFromAssembly result;
- if (diagnosticCache.TryGetValue(project, out result))
- return result;
+// static async Task<AnalyzersFromAssembly> GetProjectDiagnosticsAsync (Project project, string language, CancellationToken cancellationToken)
+// {
+// if (project == null)
+// return AnalyzersFromAssembly.Empty;
+// AnalyzersFromAssembly result;
+// if (diagnosticCache.TryGetValue(project, out result))
+// return result;
- result = new AnalyzersFromAssembly ();
+// result = new AnalyzersFromAssembly ();
- var dotNetProject = project as DotNetProject;
- if (dotNetProject != null) {
- var proxy = new DotNetProjectProxy (dotNetProject);
- if (proxy.HasPackages ()) {
- var packagesPath = await GetPackagesPath (proxy);
- foreach (var file in Directory.EnumerateFiles (packagesPath, "*.dll", SearchOption.AllDirectories)) {
- cancellationToken.ThrowIfCancellationRequested ();
- try {
- var asm = Assembly.LoadFrom (file);
- result.AddAssembly (asm);
- } catch (Exception) {
- }
- }
- }
- }
- diagnosticCache[project] = result;
- return result;
- }
+// var dotNetProject = project as DotNetProject;
+// if (dotNetProject != null) {
+// var proxy = new DotNetProjectProxy (dotNetProject);
+// if (proxy.HasPackages ()) {
+// var packagesPath = await GetPackagesPath (proxy);
+// foreach (var file in Directory.EnumerateFiles (packagesPath, "*.dll", SearchOption.AllDirectories)) {
+// cancellationToken.ThrowIfCancellationRequested ();
+// try {
+// var asm = Assembly.LoadFrom (file);
+// result.AddAssembly (asm);
+// } catch (Exception) {
+// }
+// }
+// }
+// }
+// diagnosticCache[project] = result;
+// return result;
+// }
- static Task<FilePath> GetPackagesPath (IDotNetProject project)
- {
- return Runtime.RunInMainThread (() => {
- var solutionManager = PackageManagementServices.Workspace.GetSolutionManager (project.ParentSolution);
- var nugetProject = solutionManager.GetNuGetProject (project);
- return nugetProject.GetPackagesFolderPath (solutionManager);
- });
- }
- }
-} \ No newline at end of file
+// static Task<FilePath> GetPackagesPath (IDotNetProject project)
+// {
+// return Runtime.RunInMainThread (() => {
+// var solutionManager = PackageManagementServices.Workspace.GetSolutionManager (project.ParentSolution);
+// var nugetProject = solutionManager.GetNuGetProject (project);
+// return nugetProject.GetPackagesFolderPath (solutionManager);
+// });
+// }
+// }
+//} \ No newline at end of file
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.AnalysisCore/AnalysisCommands.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.AnalysisCore/AnalysisCommands.cs
index 348cc789a9..3abed75c3d 100644
--- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.AnalysisCore/AnalysisCommands.cs
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.AnalysisCore/AnalysisCommands.cs
@@ -44,6 +44,9 @@ namespace MonoDevelop.AnalysisCore
class ExportRulesHandler : CommandHandler
{
+ static MonoDevelopWorkspaceDiagnosticAnalyzerProviderService.OptionsTable options =
+ ((MonoDevelopWorkspaceDiagnosticAnalyzerProviderService)Ide.Composition.CompositionManager.GetExportedValue<IWorkspaceDiagnosticAnalyzerProviderService> ()).Options;
+
protected override void Run ()
{
var lang = "text/x-csharp";
@@ -55,7 +58,8 @@ namespace MonoDevelop.AnalysisCore
Dictionary<CodeDiagnosticDescriptor, DiagnosticSeverity?> severities = new Dictionary<CodeDiagnosticDescriptor, DiagnosticSeverity?> ();
- foreach (var node in BuiltInCodeDiagnosticProvider.GetBuiltInCodeDiagnosticDescriptorsAsync (CodeRefactoringService.MimeTypeToLanguage(lang), true).Result) {
+ var language = CodeRefactoringService.MimeTypeToLanguage (lang);
+ foreach (var node in options.AllDiagnostics.Where (x => x.Languages.Contains (language))) {
severities [node] = node.DiagnosticSeverity;
// if (node.GetProvider ().SupportedDiagnostics.Length > 1) {
// foreach (var subIssue in node.GetProvider ().SupportedDiagnostics) {
@@ -90,7 +94,7 @@ namespace MonoDevelop.AnalysisCore
}
var providerStates = new Dictionary<CodeRefactoringDescriptor, bool> ();
- foreach (var node in BuiltInCodeDiagnosticProvider.GetBuiltInCodeRefactoringDescriptorsAsync (CodeRefactoringService.MimeTypeToLanguage(lang), true).Result) {
+ foreach (var node in options.AllRefactorings.Where (x => x.Language.Contains (language))) {
providerStates [node] = node.IsEnabled;
}
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.AnalysisCore/MonoDevelopWorkspaceDiagnosticAnalyzerProviderService.OptionsTable.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.AnalysisCore/MonoDevelopWorkspaceDiagnosticAnalyzerProviderService.OptionsTable.cs
new file mode 100644
index 0000000000..667665a194
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.AnalysisCore/MonoDevelopWorkspaceDiagnosticAnalyzerProviderService.OptionsTable.cs
@@ -0,0 +1,114 @@
+//
+// MonoDevelopWorkspaceDiagnosticAnalyzerProviderService.OptionsTable.cs
+//
+// Author:
+// Marius Ungureanu <maungu@microsoft.com>
+//
+// Copyright (c) 2018
+//
+// 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 System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeFixes;
+using Microsoft.CodeAnalysis.CodeRefactorings;
+using Microsoft.CodeAnalysis.Diagnostics;
+using MonoDevelop.AnalysisCore.Gui;
+using MonoDevelop.CodeActions;
+using MonoDevelop.CodeIssues;
+using MonoDevelop.Core;
+
+namespace MonoDevelop.AnalysisCore
+{
+ partial class MonoDevelopWorkspaceDiagnosticAnalyzerProviderService
+ {
+ internal class OptionsTable
+ {
+ Dictionary<string, CodeDiagnosticDescriptor> diagnosticTable = new Dictionary<string, CodeDiagnosticDescriptor> ();
+ Dictionary<Type, CodeRefactoringDescriptor> refactoringTable = new Dictionary<Type, CodeRefactoringDescriptor> ();
+ List<CodeDiagnosticDescriptor> diagnostics = new List<CodeDiagnosticDescriptor> ();
+
+ public IEnumerable<CodeDiagnosticDescriptor> AllDiagnostics => diagnostics;
+ public IEnumerable<CodeRefactoringDescriptor> AllRefactorings => refactoringTable.Values;
+
+ public bool TryGetDiagnosticDescriptor (string id, out CodeDiagnosticDescriptor desc)
+ {
+ return diagnosticTable.TryGetValue (id, out desc);
+ }
+
+ public bool TryGetRefactoringDescriptor (Type t, out CodeRefactoringDescriptor desc)
+ {
+ return refactoringTable.TryGetValue (t, out desc);
+ }
+
+ // Needed to support configuration of diagnostics and code fixes/refactorings.
+ public void ProcessAssembly (Assembly asm)
+ {
+ try {
+ foreach (var type in asm.GetTypes ()) {
+
+ //HACK: Workaround for missing UI
+ if (type == typeof (Microsoft.CodeAnalysis.GenerateOverrides.GenerateOverridesCodeRefactoringProvider))
+ continue;
+ if (type == typeof (Microsoft.CodeAnalysis.AddMissingReference.AbstractAddMissingReferenceCodeFixProvider))
+ continue;
+
+ var analyzerAttr = (DiagnosticAnalyzerAttribute)type.GetCustomAttributes (typeof (DiagnosticAnalyzerAttribute), false).FirstOrDefault ();
+ if (analyzerAttr != null) {
+ try {
+ var analyzer = (DiagnosticAnalyzer)Activator.CreateInstance (type);
+ var descriptor = new CodeDiagnosticDescriptor (analyzerAttr.Languages, type);
+
+ foreach (var diag in analyzer.SupportedDiagnostics) {
+ if (!IsDiagnosticSupported (diag))
+ continue;
+
+ diagnosticTable[diag.Id] = descriptor;
+ }
+ diagnostics.Add (descriptor);
+ } catch (Exception e) {
+ LoggingService.LogError ($"error while adding diagnostic analyzer {type} from assembly {asm.FullName}", e);
+ }
+ }
+
+ var exportAttr = type.GetCustomAttributes (typeof (ExportCodeRefactoringProviderAttribute), false).FirstOrDefault () as ExportCodeRefactoringProviderAttribute;
+ if (exportAttr != null) {
+ refactoringTable[type] = new CodeRefactoringDescriptor (type, exportAttr);
+ }
+ }
+ } catch (ReflectionTypeLoadException ex) {
+ foreach (var subException in ex.LoaderExceptions) {
+ LoggingService.LogError ("Error while loading diagnostics in " + asm.FullName, subException);
+ }
+ throw;
+ }
+ }
+
+ static bool IsDiagnosticSupported (DiagnosticDescriptor diag)
+ {
+ //filter out E&C analyzers as we don't support E&C
+ return !diag.CustomTags.Contains (WellKnownDiagnosticTags.EditAndContinue);
+ }
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.AnalysisCore/MonoDevelopWorkspaceDiagnosticAnalyzerProviderService.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.AnalysisCore/MonoDevelopWorkspaceDiagnosticAnalyzerProviderService.cs
index 901d8b4565..5f3184e237 100644
--- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.AnalysisCore/MonoDevelopWorkspaceDiagnosticAnalyzerProviderService.cs
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.AnalysisCore/MonoDevelopWorkspaceDiagnosticAnalyzerProviderService.cs
@@ -30,24 +30,37 @@ using System.ComponentModel.Composition;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
+using Mono.Addins;
using MonoDevelop.Core;
+using MonoDevelop.Core.AddIns;
namespace MonoDevelop.AnalysisCore
{
[Export(typeof(IWorkspaceDiagnosticAnalyzerProviderService))]
partial class MonoDevelopWorkspaceDiagnosticAnalyzerProviderService : IWorkspaceDiagnosticAnalyzerProviderService
{
+ static readonly AnalyzerAssemblyLoader analyzerAssemblyLoader = new AnalyzerAssemblyLoader ();
readonly static string diagnosticAnalyzerAssembly = typeof (DiagnosticAnalyzerAttribute).Assembly.GetName ().Name;
+
const bool ClrHeapEnabled = false;
- static readonly AnalyzerAssemblyLoader analyzerAssemblyLoader = new AnalyzerAssemblyLoader ();
+ internal OptionsTable Options = new OptionsTable ();
readonly ImmutableArray<HostDiagnosticAnalyzerPackage> hostDiagnosticAnalyzerInfo;
+ const string extensionPath = "/MonoDevelop/Refactoring/AnalyzerAssemblies";
+ string [] RuntimeEnabledAssemblies;
public MonoDevelopWorkspaceDiagnosticAnalyzerProviderService ()
{
+ LoadAnalyzerAssemblies ();
+ RefactoringEssentials.NRefactory6Host.GetLocalizedString = GettextCatalog.GetString;
hostDiagnosticAnalyzerInfo = CreateHostDiagnosticAnalyzerPackages ();
}
+ void LoadAnalyzerAssemblies()
+ {
+ RuntimeEnabledAssemblies = AddinManager.GetExtensionNodes<AssemblyExtensionNode> (extensionPath).Select (b => b.FileName).ToArray ();
+ }
+
public IAnalyzerAssemblyLoader GetAnalyzerAssemblyLoader ()
{
return analyzerAssemblyLoader;
@@ -58,7 +71,7 @@ namespace MonoDevelop.AnalysisCore
return hostDiagnosticAnalyzerInfo;
}
- static ImmutableArray<HostDiagnosticAnalyzerPackage> CreateHostDiagnosticAnalyzerPackages ()
+ ImmutableArray<HostDiagnosticAnalyzerPackage> CreateHostDiagnosticAnalyzerPackages ()
{
var builder = ImmutableArray.CreateBuilder<HostDiagnosticAnalyzerPackage> ();
var assemblies = ImmutableArray.CreateBuilder<string> ();
@@ -66,33 +79,27 @@ namespace MonoDevelop.AnalysisCore
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies ()) {
try {
var assemblyName = asm.GetName ().Name;
- switch (assemblyName) {
- //whitelist
- case "RefactoringEssentials":
- case "Refactoring Essentials":
- case "Microsoft.CodeAnalysis.CSharp":
- case "Microsoft.CodeAnalysis.CSharp.Features":
- case "Microsoft.CodeAnalysis.Features":
- case "Microsoft.CodeAnalysis.VisualBasic.Features":
- case "Microsoft.CodeAnalysis.VisualBasic":
- break;
- case "ClrHeapAllocationAnalyzer":
- if (!ClrHeapEnabled)
- continue;
- break;
- //blacklist
- case "FSharpBinding":
- continue;
- //addin assemblies that reference roslyn
- default:
- var refAsm = asm.GetReferencedAssemblies ();
- if (refAsm.Any (a => a.Name == diagnosticAnalyzerAssembly) && refAsm.Any (a => a.Name == "MonoDevelop.Ide"))
+ if (Array.IndexOf (RuntimeEnabledAssemblies, assemblyName) == -1) {
+ switch (assemblyName) {
+ case "ClrHeapAllocationAnalyzer":
+ if (!ClrHeapEnabled)
+ continue;
break;
- continue;
+ //blacklist
+ case "FSharpBinding":
+ continue;
+ //addin assemblies that reference roslyn
+ default:
+ var refAsm = asm.GetReferencedAssemblies ();
+ if (refAsm.Any (a => a.Name == diagnosticAnalyzerAssembly) && refAsm.Any (a => a.Name == "MonoDevelop.Ide"))
+ break;
+ continue;
+ }
}
// Figure out a way to disable E&C analyzers.
assemblies.Add (asm.Location);
+ Options.ProcessAssembly (asm);
} catch (Exception e) {
LoggingService.LogError ("Error while loading diagnostics in " + asm.FullName, e);
}
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionContainer.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionContainer.cs
index f8522866e1..28fec6bcef 100644
--- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionContainer.cs
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionContainer.cs
@@ -24,96 +24,50 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-using System.Collections.Generic;
-using Microsoft.CodeAnalysis.CodeActions;
using System;
-using MonoDevelop.CodeIssues;
using System.Linq;
+using System.Collections.Generic;
using Microsoft.CodeAnalysis;
-using System.Collections;
+using Microsoft.CodeAnalysis.CodeFixes;
+using Microsoft.CodeAnalysis.CodeRefactorings;
+using System.Collections.Immutable;
namespace MonoDevelop.CodeActions
{
class CodeActionContainer
{
- public static readonly CodeActionContainer Empty = new CodeActionContainer();
+ public static readonly CodeActionContainer Empty = new CodeActionContainer(ImmutableArray<CodeFixCollection>.Empty, ImmutableArray<CodeRefactoring>.Empty);
public bool IsEmpty {
get {
- return CodeFixActions.Count + DiagnosticsAtCaret.Count + CodeRefactoringActions.Count == 0;
+ return CodeFixActions.Length + CodeRefactoringActions.Length == 0;
}
}
- IReadOnlyList<ValidCodeDiagnosticAction> codeFixActions;
- public IReadOnlyList<ValidCodeDiagnosticAction> CodeFixActions {
+ ImmutableArray<CodeFixCollection> codeFixActions;
+ public ImmutableArray<CodeFixCollection> CodeFixActions {
get {
- return codeFixActions ?? new ValidCodeDiagnosticAction[0];
+ return codeFixActions;
}
private set {
codeFixActions = value;
}
}
- IReadOnlyList<ValidCodeAction> codeRefactoringActions;
-
- public IReadOnlyList<ValidCodeAction> CodeRefactoringActions {
+ ImmutableArray<CodeRefactoring> codeRefactoringActions;
+ public ImmutableArray<CodeRefactoring> CodeRefactoringActions {
get {
- return codeRefactoringActions ?? new ValidCodeAction[0];
+ return codeRefactoringActions;
}
private set {
codeRefactoringActions = value;
}
}
- public IEnumerable<ValidCodeAction> AllValidCodeActions {
- get {
- return CodeRefactoringActions.Concat (CodeFixActions);
- }
- }
-
- IReadOnlyList<Diagnostic> diagnosticsAtCaret;
- public IReadOnlyList<Diagnostic> DiagnosticsAtCaret {
- get {
- return diagnosticsAtCaret ?? new Diagnostic[0];
- }
- private set {
- diagnosticsAtCaret = value.Distinct (new DiagnosticComparer()).ToList ();
- }
- }
-
- class DiagnosticComparer : IEqualityComparer<Diagnostic>
- {
- bool IEqualityComparer<Diagnostic>.Equals (Diagnostic x, Diagnostic y)
- {
- if (x.Id != null && y.Id != null)
- return x.Id == y.Id;
- return x.Equals (y);
- }
-
- int IEqualityComparer<Diagnostic>.GetHashCode (Diagnostic obj)
- {
- return obj.Id != null ? obj.Id.GetHashCode () : obj.GetHashCode ();
- }
- }
-
- CodeActionContainer ()
- {
- CodeFixActions = new List<ValidCodeDiagnosticAction> ();
- CodeRefactoringActions = new List<ValidCodeAction> ();
- DiagnosticsAtCaret = new List<Diagnostic> ();
- }
-
- internal CodeActionContainer (List<ValidCodeDiagnosticAction> codeDiagnosticActions, List<ValidCodeAction> codeRefactoringActions, List<Diagnostic> diagnosticsAtCaret)
+ internal CodeActionContainer (ImmutableArray<CodeFixCollection> codeDiagnosticActions, ImmutableArray<CodeRefactoring> codeRefactoringActions)
{
- if (codeDiagnosticActions == null)
- throw new ArgumentNullException ("codeDiagnosticActions");
- if (codeRefactoringActions == null)
- throw new ArgumentNullException ("codeRefactoringActions");
- if (diagnosticsAtCaret == null)
- throw new ArgumentNullException ("diagnosticsAtCaret");
CodeFixActions = codeDiagnosticActions;
CodeRefactoringActions = codeRefactoringActions;
- DiagnosticsAtCaret = diagnosticsAtCaret;
}
}
} \ No newline at end of file
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionEditorExtension.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionEditorExtension.cs
index 941aba52af..e8fa0110be 100644
--- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionEditorExtension.cs
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionEditorExtension.cs
@@ -35,6 +35,7 @@ using System.Threading.Tasks;
using Gtk;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
+using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Text;
using MonoDevelop.AnalysisCore;
@@ -100,8 +101,6 @@ namespace MonoDevelop.CodeActions
Task<CodeActionContainer> smartTagTask;
CancellationTokenSource quickFixCancellationTokenSource = new CancellationTokenSource ();
- List<CodeDiagnosticFixDescriptor> codeFixes;
-
void HandleCaretPositionChanged (object sender, EventArgs e)
{
if (Editor.IsInAtomicUndo)
@@ -110,8 +109,8 @@ namespace MonoDevelop.CodeActions
if (AnalysisOptions.EnableFancyFeatures && DocumentContext.ParsedDocument != null) {
if (HasCurrentFixes) {
var curOffset = Editor.CaretOffset;
- foreach (var fix in smartTagTask.Result.AllValidCodeActions) {
- if (!fix.ValidSegment.Contains (curOffset)) {
+ foreach (var fix in smartTagTask.Result.CodeFixActions) {
+ if (!fix.TextSpan.Contains (curOffset)) {
RemoveWidget ();
break;
}
@@ -124,6 +123,8 @@ namespace MonoDevelop.CodeActions
}
}
+ static ICodeFixService codeFixService = Ide.Composition.CompositionManager.GetExportedValue<ICodeFixService> ();
+ static ICodeRefactoringService codeRefactoringService = Ide.Composition.CompositionManager.GetExportedValue<ICodeRefactoringService> ();
internal Task<CodeActionContainer> GetCurrentFixesAsync (CancellationToken cancellationToken)
{
var loc = Editor.CaretOffset;
@@ -132,80 +133,19 @@ namespace MonoDevelop.CodeActions
return Task.FromResult (CodeActionContainer.Empty);
}
TextSpan span;
-
if (Editor.IsSomethingSelected) {
var selectionRange = Editor.SelectionRange;
span = selectionRange.Offset >= 0 ? TextSpan.FromBounds (selectionRange.Offset, selectionRange.EndOffset) : TextSpan.FromBounds (loc, loc);
} else {
span = TextSpan.FromBounds (loc, loc);
}
- var rExt = Editor.GetContent<ResultsEditorExtension> ();
- var errorList = Editor
- .GetTextSegmentMarkersAt (Editor.CaretOffset)
- .OfType<IErrorMarker> ()
- .Where (rm => !string.IsNullOrEmpty (rm.Error.Id)).ToList ();
+
return Task.Run (async delegate {
try {
- var result = await CodeDiagnosticRunner.Check (new AnalysisDocument (Editor, DocumentContext), cancellationToken).ConfigureAwait (false);
- var diagnosticsAtCaret = result.OfType<DiagnosticResult> ().Where (d => d.Region.Contains (loc)).Select (d => d.Diagnostic).ToList ();
+ var fixes = await codeFixService.GetFixesAsync (ad, span, true, cancellationToken);
+ var refactorings = await codeRefactoringService.GetRefactoringsAsync (ad, span, cancellationToken);
- var codeIssueFixes = new List<ValidCodeDiagnosticAction> ();
- var diagnosticIds = diagnosticsAtCaret.Select (diagnostic => diagnostic.Id).Concat (errorList.Select (rm => rm.Error.Id)).ToList ();
- if (codeFixes == null) {
- codeFixes = (await CodeRefactoringService.GetCodeFixesAsync (DocumentContext, CodeRefactoringService.MimeTypeToLanguage (Editor.MimeType), cancellationToken).ConfigureAwait (false)).ToList ();
- }
- var root = await ad.GetSyntaxRootAsync (cancellationToken).ConfigureAwait (false);
- foreach (var cfp in codeFixes) {
- if (cancellationToken.IsCancellationRequested)
- return CodeActionContainer.Empty;
- var provider = cfp.GetCodeFixProvider ();
- if (!provider.FixableDiagnosticIds.Any (diagnosticIds.Contains))
- continue;
-
- // These two delegates were factored out, as using them as lambdas in the inner loop creates more captures than declaring them here.
- Func<Diagnostic, bool> providerIdsContain = d => provider.FixableDiagnosticIds.Contains (d.Id);
- Action<Microsoft.CodeAnalysis.CodeActions.CodeAction, ImmutableArray<Diagnostic>> codeFixRegistration = (ca, d) => codeIssueFixes.Add (new ValidCodeDiagnosticAction (cfp, ca, d, d [0].Location.SourceSpan));
- try {
- var groupedDiagnostics = diagnosticsAtCaret
- .Concat (errorList.Select (em => em.Error.Tag)
- .OfType<Diagnostic> ())
- .GroupBy (d => d.Location.SourceSpan);
- foreach (var g in groupedDiagnostics) {
- if (cancellationToken.IsCancellationRequested)
- return CodeActionContainer.Empty;
- var diagnosticSpan = g.Key;
- var validDiagnostics = g.Where (providerIdsContain).ToImmutableArray ();
- if (validDiagnostics.Length == 0) {
- continue;
- }
- if (diagnosticSpan.Start < 0 || diagnosticSpan.End > root.Span.End) {
- continue;
- }
- await provider.RegisterCodeFixesAsync (new CodeFixContext (ad, diagnosticSpan, validDiagnostics, codeFixRegistration, cancellationToken)).ConfigureAwait (false);
-
- // TODO: Is that right ? Currently it doesn't really make sense to run one code fix provider on several overlapping diagnostics at the same location
- // However the generate constructor one has that case and if I run it twice the same code action is generated twice. So there is a dupe check problem there.
- // Work around for now is to only take the first diagnostic batch.
- break;
- }
- } catch (OperationCanceledException) {
- return CodeActionContainer.Empty;
- } catch (AggregateException ae) {
- ae.Flatten ().Handle (aex => aex is OperationCanceledException);
- return CodeActionContainer.Empty;
- } catch (Exception ex) {
- LoggingService.LogError ("Error while getting refactorings from code fix provider " + cfp.Name, ex);
- continue;
- }
- }
- var codeActions = new List<ValidCodeAction> ();
- foreach (var action in await CodeRefactoringService.GetValidActionsAsync (Editor, DocumentContext, span, cancellationToken).ConfigureAwait (false)) {
- codeActions.Add (action);
- }
- if (cancellationToken.IsCancellationRequested)
- return CodeActionContainer.Empty;
-
- var codeActionContainer = new CodeActionContainer (codeIssueFixes, codeActions, diagnosticsAtCaret);
+ var codeActionContainer = new CodeActionContainer (fixes, refactorings);
Application.Invoke ((o, args) => {
if (cancellationToken.IsCancellationRequested)
return;
@@ -229,7 +169,6 @@ namespace MonoDevelop.CodeActions
}
}, cancellationToken);
-
}
async void PopupQuickFixMenu (Gdk.EventButton evt, Action<CodeFixMenu> menuAction)
@@ -335,8 +274,8 @@ namespace MonoDevelop.CodeActions
bool first = true;
var smartTagLocBegin = offset;
- foreach (var fix in fixes.CodeFixActions.Concat (fixes.CodeRefactoringActions)) {
- var textSpan = fix.ValidSegment;
+ foreach (var fix in fixes.CodeFixActions) {
+ var textSpan = fix.TextSpan;
if (textSpan.IsEmpty)
continue;
if (first || offset < textSpan.Start) {
@@ -356,7 +295,7 @@ namespace MonoDevelop.CodeActions
currentSmartTag.CancelPopup += CurrentSmartTag_CancelPopup;
currentSmartTag.ShowPopup += CurrentSmartTag_ShowPopup;
currentSmartTag.Tag = fixes;
- currentSmartTag.IsVisible = fixes.CodeFixActions.Count > 0;
+ currentSmartTag.IsVisible = fixes.CodeFixActions.Sum (x => x.Fixes.Length) > 0;
editor.AddMarker (currentSmartTag);
}
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionPanelWidget.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionPanelWidget.cs
index a2ba81c573..52368ba888 100644
--- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionPanelWidget.cs
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionPanelWidget.cs
@@ -34,6 +34,8 @@ using MonoDevelop.Refactoring;
using System.Collections.Generic;
using GLib;
using MonoDevelop.CodeIssues;
+using MonoDevelop.AnalysisCore;
+using Microsoft.CodeAnalysis.Diagnostics;
namespace MonoDevelop.CodeActions
{
@@ -59,10 +61,13 @@ namespace MonoDevelop.CodeActions
readonly TreeStore treeStore = new TreeStore (typeof(string), typeof(bool), typeof(CodeRefactoringDescriptor));
readonly Dictionary<CodeRefactoringDescriptor, bool> providerStates = new Dictionary<CodeRefactoringDescriptor, bool> ();
+ static MonoDevelopWorkspaceDiagnosticAnalyzerProviderService.OptionsTable options =
+ ((MonoDevelopWorkspaceDiagnosticAnalyzerProviderService)Ide.Composition.CompositionManager.GetExportedValue<IWorkspaceDiagnosticAnalyzerProviderService> ()).Options;
+
void GetAllProviderStates ()
{
var language = CodeRefactoringService.MimeTypeToLanguage (mimeType);
- foreach (var node in BuiltInCodeDiagnosticProvider.GetBuiltInCodeRefactoringDescriptorsAsync (CodeRefactoringService.MimeTypeToLanguage(language), true).Result) {
+ foreach (var node in options.AllRefactorings.Where (x => x.Language.Contains (language))) {
providerStates [node] = node.IsEnabled;
}
}
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeFixMenuService.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeFixMenuService.cs
index 3e1a3491c0..df1fb480aa 100644
--- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeFixMenuService.cs
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeFixMenuService.cs
@@ -48,6 +48,7 @@ using MonoDevelop.Ide.Composition;
using MonoDevelop.Ide.Editor;
using MonoDevelop.Refactoring;
using RefactoringEssentials;
+using MonoDevelop.AnalysisCore;
namespace MonoDevelop.CodeActions
{
@@ -76,6 +77,8 @@ namespace MonoDevelop.CodeActions
usages.Set (id, CodeActionUsages [id]);
}
+ static MonoDevelopWorkspaceDiagnosticAnalyzerProviderService.OptionsTable options =
+ ((MonoDevelopWorkspaceDiagnosticAnalyzerProviderService)Ide.Composition.CompositionManager.GetExportedValue<IWorkspaceDiagnosticAnalyzerProviderService> ()).Options;
public static async Task<CodeFixMenu> CreateFixMenu (TextEditor editor, CodeActionContainer fixes, CancellationToken cancellationToken = default(CancellationToken))
{
var menu = new CodeFixMenu ();
@@ -86,191 +89,101 @@ namespace MonoDevelop.CodeActions
int mnemonic = 1;
- foreach (var fix in fixes.CodeFixActions.OrderByDescending (i => GetUsage (i.CodeAction.EquivalenceKey))) {
- AddFixMenuItem (editor, menu, ref mnemonic, fix.CodeAction);
- }
+ var suppressLabel = GettextCatalog.GetString ("_Suppress");
+ var suppressMenu = new CodeFixMenu (suppressLabel);
- bool first = true;
- foreach (var fix in fixes.CodeRefactoringActions) {
- if (first) {
- if (menu.Items.Count > 0)
- menu.Add (CodeFixMenuEntry.Separator);
- first = false;
- }
-
- AddFixMenuItem (editor, menu, ref mnemonic, fix.CodeAction);
- }
+ var fixAllLabel = GettextCatalog.GetString ("_Fix all");
+ var fixAllMenu = new CodeFixMenu (fixAllLabel);
- var warningsAtCaret = (await editor.DocumentContext.AnalysisDocument.GetSemanticModelAsync (cancellationToken))
- .GetDiagnostics (new TextSpan (editor.CaretOffset, 0))
- .Where (diag => diag.Severity == DiagnosticSeverity.Warning).ToList ();
+ var configureLabel = GettextCatalog.GetString ("_Options");
+ var configureMenu = new CodeFixMenu (configureLabel);
- var caretSpan = new TextSpan (editor.CaretOffset, 0);
+ foreach (var cfa in fixes.CodeFixActions.OrderByDescending (x => x.Fixes.Max(y => GetUsage (y.Action.EquivalenceKey)))) {
+ var state = cfa.FixAllState;
+ var scopes = cfa.SupportedScopes;
- first = true;
- foreach (var warning in warningsAtCaret) {
- if (string.IsNullOrWhiteSpace (warning.Descriptor.Title.ToString ()))
- continue;
- var label = GettextCatalog.GetString ("_Options for \u2018{0}\u2019", warning.Descriptor.Title);
- var subMenu = new CodeFixMenu (label);
+ foreach (var fix in cfa.Fixes) {
+ var diag = fix.PrimaryDiagnostic;
- await AddSuppressionMenuItems (subMenu, editor, warning, caretSpan);
+ if (options.TryGetDiagnosticDescriptor (diag.Id, out var descriptor) && !descriptor.GetIsEnabled (diag.Descriptor))
+ continue;
+
+ bool isSuppress = fix.Action is TopLevelSuppressionCodeAction;
- if (subMenu.Items.Count > 0) {
+ if (isSuppress) {
+ AddFixMenuItem (editor, suppressMenu, ref mnemonic, fix.Action);
+ continue;
+ }
- if (first) {
- menu.Add (CodeFixMenuEntry.Separator);
- first = false;
+ AddFixMenuItem (editor, menu, ref mnemonic, fix.Action);
+
+ var configurable = !DescriptorHasTag (diag.Descriptor, WellKnownDiagnosticTags.NotConfigurable);
+ if (descriptor != null && configurable) {
+ var optionsMenuItem = new CodeFixMenuEntry (GettextCatalog.GetString ("_Configure Rule \u2018{0}\u2019", diag.Descriptor.Title),
+ delegate {
+ IdeApp.Workbench.ShowGlobalPreferencesDialog (null, "C#", dialog => {
+ var panel = dialog.GetPanel<CodeIssuePanel> ("C#");
+ if (panel == null)
+ return;
+ panel.Widget.SelectCodeIssue (fix.PrimaryDiagnostic.Descriptor.Id);
+ });
+ });
+ configureMenu.Add (optionsMenuItem);
}
- menu.Add (subMenu);
+ if (!scopes.Contains (FixAllScope.Document))
+ continue;
+
+ // FIXME: No global undo yet to support fixall in project/solution
+ var fixState = state.WithScopeAndEquivalenceKey (FixAllScope.Document, fix.Action.EquivalenceKey);
+
+ var provider = state.FixAllProvider;
+ if (provider == null)
+ continue;
+
+ // FIXME: Use a real progress tracker.
+ var fixAll = await provider.GetFixAsync (fixState.CreateFixAllContext (new RoslynProgressTracker (), cancellationToken));
+ AddFixMenuItem (editor, fixAllMenu, ref mnemonic, fixAll);
}
}
- first = true;
- foreach (var diag in fixes.DiagnosticsAtCaret) {
- if (string.IsNullOrWhiteSpace (diag.Descriptor.Title.ToString ()))
+ bool first = true;
+ foreach (var refactoring in fixes.CodeRefactoringActions) {
+ if (options.TryGetRefactoringDescriptor (refactoring.GetType (), out var descriptor) && !descriptor.IsEnabled)
continue;
- var notConfigurable = DescriptorHasTag (diag.Descriptor, WellKnownDiagnosticTags.NotConfigurable);
-
- var label = GettextCatalog.GetString ("_Options for \u2018{0}\u2019", diag.Descriptor.Title);
- var subMenu = new CodeFixMenu (label);
-
if (first) {
- menu.Add (CodeFixMenuEntry.Separator);
+ if (menu.Items.Count > 0)
+ menu.Add (CodeFixMenuEntry.Separator);
first = false;
}
- await AddSuppressionMenuItems (subMenu, editor, diag, caretSpan);
-
- var descriptor = BuiltInCodeDiagnosticProvider.GetCodeDiagnosticDescriptor (diag.Id);
-
- if (descriptor != null && IsConfigurable (diag.Descriptor)) {
- var optionsMenuItem = new CodeFixMenuEntry (GettextCatalog.GetString ("_Configure Rule"),
- delegate {
- IdeApp.Workbench.ShowGlobalPreferencesDialog (null, "C#", dialog => {
- var panel = dialog.GetPanel<CodeIssuePanel> ("C#");
- if (panel == null)
- return;
- panel.Widget.SelectCodeIssue (diag.Descriptor.Id);
- });
- });
- subMenu.Add (optionsMenuItem);
+ foreach (var action in refactoring.Actions) {
+ AddFixMenuItem (editor, menu, ref mnemonic, action);
}
+ }
- foreach (var fix in fixes.CodeFixActions.OrderByDescending (i => GetUsage (i.CodeAction.EquivalenceKey))) {
- if (cancellationToken.IsCancellationRequested)
- return null;
- var provider = fix.Diagnostic.GetCodeFixProvider ().GetFixAllProvider ();
- if (provider == null)
- continue;
-
- if (!provider.GetSupportedFixAllScopes ().Contains (FixAllScope.Document))
- continue;
-
- var language = editor.DocumentContext.AnalysisDocument.Project.Language;
- var diagnosticdDescriptor = fix.Diagnostic?.GetCodeDiagnosticDescriptor (language);
- if (diagnosticdDescriptor == null)
- continue;
-
- var subMenu2 = new CodeFixMenu (GettextCatalog.GetString ("Fix all"));
-
- var diagnosticAnalyzer = diagnosticdDescriptor.GetProvider ();
- if (!diagnosticAnalyzer.SupportedDiagnostics.Contains (diag.Descriptor))
- continue;
-
- var menuItem = new CodeFixMenuEntry (
- GettextCatalog.GetString ("In _Document"),
- async delegate { await FixAll (editor, fix, provider, diagnosticAnalyzer); }
- );
- subMenu2.Add (menuItem);
- subMenu.Add (CodeFixMenuEntry.Separator);
- subMenu.Add (subMenu2);
- }
+ first = true;
- menu.Add (subMenu);
+ if (fixAllMenu.Items.Count != 0) {
+ if (first)
+ menu.Add (CodeFixMenuEntry.Separator);
+ menu.Add (fixAllMenu);
+ first = false;
}
- return menu;
- }
-
- static async Task FixAll (TextEditor editor, ValidCodeDiagnosticAction fix, FixAllProvider provider, DiagnosticAnalyzer diagnosticAnalyzer)
- {
- var diagnosticIds = diagnosticAnalyzer.SupportedDiagnostics.Select (d => d.Id).ToImmutableHashSet ();
-
- var analyzers = new [] { diagnosticAnalyzer }.ToImmutableArray ();
-
- var dict = ImmutableDictionary<Document, ImmutableArray<Diagnostic>>.Empty;
- var doc = editor.DocumentContext.AnalysisDocument;
- var diagnostics = await GetDiagnosticsForDocument (analyzers, doc, diagnosticIds, CancellationToken.None);
-
- dict = dict.Add (doc, diagnostics);
- var fixAllDiagnosticProvider = new FixAllState.FixMultipleDiagnosticProvider (dict);
-
- var ctx = new FixAllContext (
- editor.DocumentContext.AnalysisDocument,
- fix.Diagnostic.GetCodeFixProvider (),
- FixAllScope.Document,
- fix.CodeAction.EquivalenceKey,
- diagnosticIds,
- fixAllDiagnosticProvider,
- default (CancellationToken)
- );
-
- var fixAll = await provider.GetFixAsync (ctx);
- using (var undo = editor.OpenUndoGroup ()) {
- await CodeDiagnosticDescriptor.RunAction (editor.DocumentContext, fixAll, default (CancellationToken));
+ if (suppressMenu.Items.Count != 0) {
+ if (first)
+ menu.Add (CodeFixMenuEntry.Separator);
+ menu.Add (suppressMenu);
+ first = false;
}
- }
-
- static async Task<ImmutableArray<Diagnostic>> GetDiagnosticsForDocument (ImmutableArray<DiagnosticAnalyzer> analyzers, Microsoft.CodeAnalysis.Document doc, ImmutableHashSet<string> diagnostics, CancellationToken token)
- {
- var sol = doc.Project.Solution;
- var options = new CompilationWithAnalyzersOptions (
- new WorkspaceAnalyzerOptions (
- new AnalyzerOptions (ImmutableArray<AdditionalText>.Empty),
- sol.Options,
- sol),
- delegate (Exception exception, DiagnosticAnalyzer analyzer, Diagnostic diag) {
- LoggingService.LogError ("Exception in diagnostic analyzer " + diag.Id + ":" + diag.GetMessage (), exception);
- },
- true,
- false
- );
-
- var model = await doc.GetSemanticModelAsync (token).ConfigureAwait (false);
- var compilationWithAnalyzer = model.Compilation.WithAnalyzers (analyzers, options);
-
- var diagnosticList = new List<Diagnostic> ();
- diagnosticList.AddRange (await compilationWithAnalyzer.GetAnalyzerSemanticDiagnosticsAsync (model, null, token).ConfigureAwait (false));
- diagnosticList.AddRange (await compilationWithAnalyzer.GetAnalyzerSemanticDiagnosticsAsync (model, null, token).ConfigureAwait (false));
-
- return diagnosticList.ToImmutableArray ();
- }
-
- static async Task AddSuppressionMenuItems (CodeFixMenu menu, TextEditor editor, Diagnostic diag, TextSpan span)
- {
- var workspace = editor.DocumentContext.AnalysisDocument.Project.Solution.Workspace;
- var language = editor.DocumentContext.AnalysisDocument.Project.Language;
- var mefExporter = (IMefHostExportProvider)workspace.Services.HostServices;
-
- //TODO: cache this
- var suppressionProviders = mefExporter.GetExports<ISuppressionFixProvider, CodeChangeProviderMetadata> ()
- .ToPerLanguageMapWithMultipleLanguages();
-
- foreach (var suppressionProvider in suppressionProviders[LanguageNames.CSharp].Select (lz => lz.Value)) {
- if (!suppressionProvider.CanBeSuppressedOrUnsuppressed (diag)) {
- continue;
- }
- try {
- var fixes = await suppressionProvider.GetSuppressionsAsync (editor.DocumentContext.AnalysisDocument, span, new [] { diag }, default (CancellationToken)).ConfigureAwait (false);
- foreach (var fix in fixes) {
- AddFixMenuItem (editor, menu, fix.Action);
- }
- } catch (Exception e) {
- LoggingService.LogError ("Error while adding fixes", e);
- }
+ if (configureMenu.Items.Count != 0) {
+ if (first)
+ menu.Add (CodeFixMenuEntry.Separator);
+ menu.Add (configureMenu);
+ first = false;
}
+ return menu;
}
static bool DescriptorHasTag (DiagnosticDescriptor desc, string tag)
@@ -278,11 +191,6 @@ namespace MonoDevelop.CodeActions
return desc.CustomTags.Any (c => CultureInfo.InvariantCulture.CompareInfo.Compare (c, tag) == 0);
}
- static bool IsConfigurable (DiagnosticDescriptor desc)
- {
- return !DescriptorHasTag (desc, WellKnownDiagnosticTags.NotConfigurable);
- }
-
static CodeFixMenuEntry CreateFixMenuEntry (TextEditor editor, CodeAction fix)
{
int mnemonic = -1;
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeRefactoringService.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeRefactoringService.cs
index cb53d0a708..8f480dc917 100644
--- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeRefactoringService.cs
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeRefactoringService.cs
@@ -43,117 +43,6 @@ namespace MonoDevelop.CodeActions
{
static class CodeRefactoringService
{
- readonly static List<CodeDiagnosticProvider> providers = new List<CodeDiagnosticProvider> ();
-
- static CodeRefactoringService ()
- {
- providers.Add (new BuiltInCodeDiagnosticProvider ());
-
- AddinManager.AddExtensionNodeHandler ("/MonoDevelop/Refactoring/CodeDiagnosticProvider", delegate (object sender, ExtensionNodeEventArgs args) {
- var node = (TypeExtensionNode)args.ExtensionNode;
- switch (args.Change) {
- case ExtensionChange.Add:
- providers.Add ((CodeDiagnosticProvider)node.CreateInstance ());
- break;
- }
- });
- }
-
- public async static Task<IEnumerable<CodeDiagnosticDescriptor>> GetCodeDiagnosticsAsync (DocumentContext documentContext, string language, CancellationToken cancellationToken = default (CancellationToken))
- {
- var result = new List<CodeDiagnosticDescriptor> ();
-
- foreach (var provider in providers) {
- if (cancellationToken.IsCancellationRequested)
- return Enumerable.Empty<CodeDiagnosticDescriptor> ();
- result.AddRange (await provider.GetCodeDiagnosticDescriptorsAsync (documentContext, language, cancellationToken).ConfigureAwait (false));
- }
- return result;
- }
-
- public async static Task<IEnumerable<CodeDiagnosticFixDescriptor>> GetCodeFixesAsync (DocumentContext documentContext, string language, CancellationToken cancellationToken = default (CancellationToken))
- {
- var result = new List<CodeDiagnosticFixDescriptor> ();
- foreach (var provider in providers) {
- result.AddRange (await provider.GetCodeFixDescriptorsAsync (documentContext, language, cancellationToken).ConfigureAwait (false));
- }
- return result;
- }
-
- public async static Task<IEnumerable<CodeRefactoringDescriptor>> GetCodeRefactoringsAsync (DocumentContext documentContext, string language, CancellationToken cancellationToken = default (CancellationToken))
- {
- var result = new List<CodeRefactoringDescriptor> ();
- foreach (var provider in providers) {
- result.AddRange (await provider.GetCodeRefactoringDescriptorsAsync (documentContext, language, cancellationToken).ConfigureAwait (false));
- }
- return result;
- }
-
- static List<CodeRefactoringDescriptor> codeRefactoringCache;
- public static async Task<IEnumerable<ValidCodeAction>> GetValidActionsAsync (TextEditor editor, DocumentContext doc, TextSpan span, CancellationToken cancellationToken = default (CancellationToken))
- {
- if (editor == null)
- throw new ArgumentNullException ("editor");
- if (doc == null)
- throw new ArgumentNullException ("doc");
- var parsedDocument = doc.ParsedDocument;
- var actions = new List<ValidCodeAction> ();
- if (parsedDocument == null)
- return actions;
- var analysisDocument = doc.AnalysisDocument;
- if (analysisDocument == null)
- return actions;
-
- var model = await analysisDocument.GetSemanticModelAsync (cancellationToken);
- if (model == null)
- return actions;
- var root = await model.SyntaxTree.GetRootAsync (cancellationToken).ConfigureAwait (false);
- if (span.End > root.Span.End)
- return actions;
- TextSpan tokenSegment = span;
- var token = root.FindToken (span.Start);
- if (!token.IsMissing)
- tokenSegment = token.Span;
- try {
- if (codeRefactoringCache == null) {
- codeRefactoringCache = (await GetCodeRefactoringsAsync (doc, MimeTypeToLanguage (editor.MimeType), cancellationToken).ConfigureAwait (false)).ToList ();
- }
- foreach (var descriptor in codeRefactoringCache) {
- if (!descriptor.IsEnabled)
- continue;
- if (cancellationToken.IsCancellationRequested || analysisDocument == null)
- return Enumerable.Empty<ValidCodeAction> ();
- try {
- await descriptor.GetProvider ().ComputeRefactoringsAsync (
- new CodeRefactoringContext (analysisDocument, span, delegate (CodeAction ca) {
- var nrca = ca as NRefactoryCodeAction;
- var validSegment = tokenSegment;
- if (nrca != null)
- validSegment = nrca.TextSpan;
- actions.Add (new ValidCodeAction (ca, validSegment));
- }, cancellationToken)
- ).ConfigureAwait (false);
- } catch (OperationCanceledException) {
- if (cancellationToken.IsCancellationRequested)
- return Enumerable.Empty<ValidCodeAction> ();
- } catch (AggregateException) {
- if (cancellationToken.IsCancellationRequested)
- return Enumerable.Empty<ValidCodeAction> ();
- } catch (Exception e) {
- LoggingService.LogError ("Error while getting refactorings from " + descriptor.IdString, e);
- continue;
- }
- }
- } catch (OperationCanceledException) {
- if (cancellationToken.IsCancellationRequested)
- return Enumerable.Empty<ValidCodeAction> ();
- } catch (AggregateException) {
- if (cancellationToken.IsCancellationRequested)
- return Enumerable.Empty<ValidCodeAction> ();
- }
- return actions;
- }
-
public static string MimeTypeToLanguage (string mimeType)
{
switch (mimeType) {
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/AnalyzersFromAssembly.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/AnalyzersFromAssembly.cs
deleted file mode 100644
index 9799766e12..0000000000
--- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/AnalyzersFromAssembly.cs
+++ /dev/null
@@ -1,137 +0,0 @@
-//
-// AnalyzersFromAssembly.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 System.Collections.Generic;
-using System.Reflection;
-using System.Threading;
-using MonoDevelop.CodeIssues;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Microsoft.CodeAnalysis.CodeFixes;
-using RefactoringEssentials;
-using MonoDevelop.Core;
-using System.Threading.Tasks;
-using MonoDevelop.Ide.Editor;
-using MonoDevelop.CodeActions;
-using Microsoft.CodeAnalysis.CodeRefactorings;
-using Microsoft.CodeAnalysis;
-
-namespace MonoDevelop.CodeIssues
-{
-
- class AnalyzersFromAssembly
- {
- public List<CodeDiagnosticDescriptor> Analyzers;
- public List<CodeDiagnosticFixDescriptor> Fixes;
- public List<CodeRefactoringDescriptor> Refactorings;
-
- public readonly static AnalyzersFromAssembly Empty = new AnalyzersFromAssembly ();
-
- public AnalyzersFromAssembly ()
- {
- Analyzers = new List<CodeDiagnosticDescriptor> ();
- Fixes = new List<CodeDiagnosticFixDescriptor> ();
- Refactorings = new List<CodeRefactoringDescriptor> ();
- }
-
- readonly static string diagnosticAnalyzerAssembly = typeof (DiagnosticAnalyzerAttribute).Assembly.GetName ().Name;
-
- const bool ClrHeapEnabled = false;
- internal void AddAssembly (System.Reflection.Assembly asm, bool force = false)
- {
- //FIXME; this is a really hacky arbitrary heuristic
- //we should be using proper MEF composition APIs as part of the addin scan
- if (!force) {
- var assemblyName = asm.GetName ().Name;
- switch (assemblyName) {
- //whitelist
- case "RefactoringEssentials":
- case "Refactoring Essentials":
- case "Microsoft.CodeAnalysis.Features":
- case "Microsoft.CodeAnalysis.VisualBasic.Features":
- case "Microsoft.CodeAnalysis.CSharp.Features":
- break;
- case "ClrHeapAllocationAnalyzer":
- if (!ClrHeapEnabled)
- return;
- break;
- //blacklist
- case "FSharpBinding":
- return;
- //addin assemblies that reference roslyn
- default:
- var refAsm = asm.GetReferencedAssemblies ();
- if (refAsm.Any (a => a.Name == diagnosticAnalyzerAssembly) && refAsm.Any (a => a.Name == "MonoDevelop.Ide"))
- break;
- return;
- }
- }
-
- try {
- foreach (var type in asm.GetTypes ()) {
- var analyzerAttr = (DiagnosticAnalyzerAttribute)type.GetCustomAttributes (typeof (DiagnosticAnalyzerAttribute), false).FirstOrDefault ();
- if (analyzerAttr != null) {
- try {
- var analyzer = (DiagnosticAnalyzer)Activator.CreateInstance (type);
-
- if (analyzer.SupportedDiagnostics.Any (IsDiagnosticSupported)) {
- Analyzers.Add (new CodeDiagnosticDescriptor (analyzerAttr.Languages, type));
- }
- } catch (Exception e) {
- LoggingService.LogError ($"error while adding diagnostic analyzer {type} from assembly {asm.FullName}", e);
- }
- }
-
- var codeFixAttr = (ExportCodeFixProviderAttribute)type.GetCustomAttributes (typeof(ExportCodeFixProviderAttribute), false).FirstOrDefault ();
- if (codeFixAttr != null) {
- Fixes.Add (new CodeDiagnosticFixDescriptor (type, codeFixAttr));
- }
-
- var exportAttr = type.GetCustomAttributes (typeof(ExportCodeRefactoringProviderAttribute), false).FirstOrDefault () as ExportCodeRefactoringProviderAttribute;
- if (exportAttr != null) {
- Refactorings.Add (new CodeRefactoringDescriptor (type, exportAttr));
- }
- }
- } catch (ReflectionTypeLoadException ex) {
- foreach (var subException in ex.LoaderExceptions) {
- LoggingService.LogError ("Error while loading diagnostics in " + asm.FullName, subException);
- }
- throw;
- }
- }
-
- static bool IsDiagnosticSupported (DiagnosticDescriptor diag)
- {
- //filter out E&C analyzers as we don't support E&C
- if (diag.CustomTags.Contains (WellKnownDiagnosticTags.EditAndContinue)) {
- return false;
- }
-
- return true;
- }
- }
-}
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/BuiltInCodeDiagnosticProvider.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/BuiltInCodeDiagnosticProvider.cs
deleted file mode 100644
index 08eff6b5d7..0000000000
--- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/BuiltInCodeDiagnosticProvider.cs
+++ /dev/null
@@ -1,112 +0,0 @@
-//
-// BuiltInCodeDiagnosticProvider.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 System.Collections.Generic;
-using System.Threading;
-using MonoDevelop.CodeIssues;
-using MonoDevelop.Core;
-using System.Threading.Tasks;
-using MonoDevelop.Ide.Editor;
-using MonoDevelop.CodeActions;
-
-namespace MonoDevelop.CodeIssues
-{
-
- /// <summary>
- /// Provides all IDE code diagnostics and fix provider.
- /// (Scans the app domain for providers)
- /// </summary>
- class BuiltInCodeDiagnosticProvider : CodeDiagnosticProvider
- {
- readonly static Task<AnalyzersFromAssembly> builtInDiagnostics;
-
- static BuiltInCodeDiagnosticProvider ()
- {
- RefactoringEssentials.NRefactory6Host.GetLocalizedString = GettextCatalog.GetString;
- builtInDiagnostics = Task.Run (() => {
- var result = new AnalyzersFromAssembly ();
- foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) {
- try {
- result.AddAssembly (asm);
- } catch (Exception e) {
- LoggingService.LogError ("Error while loading diagnostics in " + asm.FullName, e);
- }
- }
- return result;
- });
- }
-
- internal static CodeDiagnosticDescriptor GetCodeDiagnosticDescriptor (string diagnosticId)
- {
- foreach (var builtInDescriptor in GetBuiltInCodeDiagnosticDescriptorsAsync (null).Result) {
- if (builtInDescriptor.GetProvider ().SupportedDiagnostics.Any (diagnostic => diagnosticId == diagnostic.Id))
- return builtInDescriptor;
- }
- return null;
- }
-
- internal async static Task<IEnumerable<CodeDiagnosticDescriptor>> GetBuiltInCodeDiagnosticDescriptorsAsync (string language, bool includeDisabledNodes = false, CancellationToken cancellationToken = default (CancellationToken))
- {
- builtInDiagnostics.Wait (cancellationToken);
- var diags = await builtInDiagnostics;
- var builtInCodeDiagnostics = diags.Analyzers;
- if (string.IsNullOrEmpty (language))
- return includeDisabledNodes ? builtInCodeDiagnostics : builtInCodeDiagnostics.Where (act => act.IsEnabled);
- return includeDisabledNodes ? builtInCodeDiagnostics.Where (ca => ca.Languages.Contains (language)) : builtInCodeDiagnostics.Where (ca => ca.Languages.Contains (language) && ca.IsEnabled);
- }
-
- public async static Task<IEnumerable<CodeDiagnosticFixDescriptor>> GetBuiltInCodeFixDescriptorsAsync (string language, CancellationToken cancellationToken = default (CancellationToken))
- {
- var diags = await builtInDiagnostics.ConfigureAwait (false);
- var builtInCodeFixes = diags.Fixes;
- return string.IsNullOrEmpty (language) ? builtInCodeFixes : builtInCodeFixes.Where (cfp => cfp.Languages.Contains (language));
- }
-
- public async static Task<IEnumerable<CodeRefactoringDescriptor>> GetBuiltInCodeRefactoringDescriptorsAsync (string language, bool includeDisabledNodes = false, CancellationToken cancellationToken = default (CancellationToken))
- {
- var diags = await builtInDiagnostics.ConfigureAwait (false);
- var builtInCodeFixes = diags.Refactorings;
- return string.IsNullOrEmpty (language) ? builtInCodeFixes : builtInCodeFixes.Where (cfp => cfp.Language.Contains (language));
- }
-
- public override Task<IEnumerable<CodeDiagnosticFixDescriptor>> GetCodeFixDescriptorsAsync (DocumentContext document, string language, CancellationToken cancellationToken)
- {
- return GetBuiltInCodeFixDescriptorsAsync (language, cancellationToken);
- }
-
- public override Task<IEnumerable<CodeDiagnosticDescriptor>> GetCodeDiagnosticDescriptorsAsync (DocumentContext document, string language, CancellationToken cancellationToken)
- {
- return GetBuiltInCodeDiagnosticDescriptorsAsync (language, false, cancellationToken);
- }
-
- public override Task<IEnumerable<CodeRefactoringDescriptor>> GetCodeRefactoringDescriptorsAsync (DocumentContext document, string language, CancellationToken cancellationToken = default (CancellationToken))
- {
- return GetBuiltInCodeRefactoringDescriptorsAsync (language, false, cancellationToken);
- }
- }
-} \ No newline at end of file
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/CodeDiagnosticDescriptor.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/CodeDiagnosticDescriptor.cs
index 114a56ff2d..c0a9daca33 100644
--- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/CodeDiagnosticDescriptor.cs
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/CodeDiagnosticDescriptor.cs
@@ -124,6 +124,11 @@ namespace MonoDevelop.CodeIssues
return PropertyService.Get ("CodeIssues." + Languages + "." + IdString + "." + diagnostic.Id + ".enabled", true);
}
+ internal bool GetIsEnabled (string diagnosticId)
+ {
+ return PropertyService.Get ("CodeIssues." + Languages + "." + IdString + "." + diagnosticId + ".enabled", true);
+ }
+
internal void SetIsEnabled (DiagnosticDescriptor diagnostic, bool value)
{
PropertyService.Set ("CodeIssues." + Languages + "." + IdString + "." + diagnostic.Id + ".enabled", value);
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/CodeDiagnosticFixDescriptor.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/CodeDiagnosticFixDescriptor.cs
deleted file mode 100644
index b70fd9e0e2..0000000000
--- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/CodeDiagnosticFixDescriptor.cs
+++ /dev/null
@@ -1,89 +0,0 @@
-//
-// CodeFixDescriptor.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.CodeFixes;
-using Microsoft.CodeAnalysis.Diagnostics;
-using System.Linq;
-using MonoDevelop.Core;
-
-namespace MonoDevelop.CodeIssues
-{
- class CodeDiagnosticFixDescriptor
- {
- readonly Type codeFixProviderType;
- readonly ExportCodeFixProviderAttribute attribute;
- CodeFixProvider instance;
-
- public string Name {
- get {
- return attribute.Name;
- }
- }
-
- public string[] Languages {
- get {
- return attribute.Languages;
- }
- }
-
- internal CodeDiagnosticFixDescriptor (Type codeFixProviderType, ExportCodeFixProviderAttribute attribute)
- {
- if (codeFixProviderType == null)
- throw new ArgumentNullException ("codeFixProviderType");
- if (attribute == null)
- throw new ArgumentNullException ("attribute");
- this.codeFixProviderType = codeFixProviderType;
- this.attribute = attribute;
- }
-
- public CodeFixProvider GetCodeFixProvider ()
- {
- if (instance == null) {
- try {
- instance = (CodeFixProvider)Activator.CreateInstance (codeFixProviderType);
- } catch (InvalidCastException) {
- LoggingService.LogError (codeFixProviderType + " can't be cast to CodeFixProvider.");
- throw;
- }
- }
-
- return instance;
- }
-
- public CodeDiagnosticDescriptor GetCodeDiagnosticDescriptor (string language)
- {
- var fixableIds = GetCodeFixProvider ().FixableDiagnosticIds.ToList ();
-
- foreach (var descriptor in BuiltInCodeDiagnosticProvider.GetBuiltInCodeDiagnosticDescriptorsAsync (language).Result) {
- if (descriptor.GetProvider ().SupportedDiagnostics.Any (diagnostic => fixableIds.Contains (diagnostic.Id)))
- return descriptor;
-
- }
- return null;
-
- }
- }
-}
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/CodeDiagnosticProvider.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/CodeDiagnosticProvider.cs
deleted file mode 100644
index 479d24dffb..0000000000
--- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/CodeDiagnosticProvider.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-//
-// CodeDiagnosticProvider.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.Collections.Generic;
-using System.Threading;
-using MonoDevelop.CodeIssues;
-using System.Threading.Tasks;
-using MonoDevelop.Ide.Editor;
-using MonoDevelop.CodeActions;
-
-namespace MonoDevelop.CodeIssues
-{
- /// <summary>
- /// The code diagnostic provider gives a list of code diagnostic and fix providers from an arbitrary source.
- /// </summary>
- abstract class CodeDiagnosticProvider
- {
- public abstract Task<IEnumerable<CodeDiagnosticDescriptor>> GetCodeDiagnosticDescriptorsAsync (DocumentContext document, string language, CancellationToken cancellationToken = default (CancellationToken));
- public abstract Task<IEnumerable<CodeDiagnosticFixDescriptor>> GetCodeFixDescriptorsAsync (DocumentContext document, string language, CancellationToken cancellationToken = default (CancellationToken));
- public abstract Task<IEnumerable<CodeRefactoringDescriptor>> GetCodeRefactoringDescriptorsAsync (DocumentContext document, string language, CancellationToken cancellationToken = default (CancellationToken));
- }
-} \ No newline at end of file
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/CodeDiagnosticRunner.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/CodeDiagnosticRunner.cs
index fda8a10732..6da380030f 100644
--- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/CodeDiagnosticRunner.cs
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/CodeDiagnosticRunner.cs
@@ -48,131 +48,11 @@ namespace MonoDevelop.CodeIssues
{
static class CodeDiagnosticRunner
{
- static List<DiagnosticAnalyzer> providers = new List<DiagnosticAnalyzer> ();
- static IEnumerable<CodeDiagnosticDescriptor> diagnostics;
- static Dictionary<string, CodeDiagnosticDescriptor> diagnosticTable;
- static SemaphoreSlim diagnosticLock = new SemaphoreSlim (1, 1);
+ static MonoDevelopWorkspaceDiagnosticAnalyzerProviderService.OptionsTable options =
+ ((MonoDevelopWorkspaceDiagnosticAnalyzerProviderService)Ide.Composition.CompositionManager.GetExportedValue<IWorkspaceDiagnosticAnalyzerProviderService> ()).Options;
+
static TraceListener consoleTraceListener = new ConsoleTraceListener ();
- static bool SkipContext (DocumentContext ctx)
- {
- return (ctx.IsAdHocProject || !(ctx.Project is MonoDevelop.Projects.DotNetProject));
- }
-
- static async Task GetDescriptorTable (AnalysisDocument analysisDocument, CancellationToken cancellationToken)
- {
- if (diagnosticTable != null)
- return;
-
- bool locked = await diagnosticLock.WaitAsync (Timeout.Infinite, cancellationToken).ConfigureAwait (false);
- if (diagnosticTable != null)
- return;
-
- try {
- var language = CodeRefactoringService.MimeTypeToLanguage (analysisDocument.Editor.MimeType);
- var alreadyAdded = new HashSet<Type> ();
-
- var table = new Dictionary<string, CodeDiagnosticDescriptor> ();
-
- diagnostics = await CodeRefactoringService.GetCodeDiagnosticsAsync (analysisDocument.DocumentContext, language, cancellationToken);
- foreach (var diagnostic in diagnostics) {
- if (!alreadyAdded.Add (diagnostic.DiagnosticAnalyzerType))
- continue;
- var provider = diagnostic.GetProvider ();
- if (provider == null)
- continue;
- foreach (var diag in provider.SupportedDiagnostics)
- table [diag.Id] = diagnostic;
-
- providers.Add (provider);
- }
-
- diagnosticTable = table;
- } finally {
- if (locked)
- diagnosticLock.Release ();
- }
- }
-
- // Old code, until we get EditorFeatures into composition so we can switch code fix service.
- public static async Task<IEnumerable<Result>> Check (AnalysisDocument analysisDocument, CancellationToken cancellationToken)
- {
- var input = analysisDocument.DocumentContext;
- if (!AnalysisOptions.EnableFancyFeatures || input.Project == null || !input.IsCompileableInProject || input.AnalysisDocument == null)
- return Enumerable.Empty<Result> ();
- if (SkipContext (input))
- return Enumerable.Empty<Result> ();
- try {
- var model = await analysisDocument.DocumentContext.AnalysisDocument.GetSemanticModelAsync (cancellationToken);
- if (model == null)
- return Enumerable.Empty<Result> ();
- var compilation = model.Compilation;
- var language = CodeRefactoringService.MimeTypeToLanguage (analysisDocument.Editor.MimeType);
-
- await GetDescriptorTable (analysisDocument, cancellationToken);
-
- if (providers.Count == 0 || cancellationToken.IsCancellationRequested)
- return Enumerable.Empty<Result> ();
- #if DEBUG
- Debug.Listeners.Add (consoleTraceListener);
- #endif
-
- CompilationWithAnalyzers compilationWithAnalyzer;
- var analyzers = ImmutableArray<DiagnosticAnalyzer>.Empty.AddRange (providers);
- var diagnosticList = new List<Diagnostic> ();
- try {
- var sol = analysisDocument.DocumentContext.AnalysisDocument.Project.Solution;
- var options = new CompilationWithAnalyzersOptions (
- new WorkspaceAnalyzerOptions (
- new AnalyzerOptions (ImmutableArray<AdditionalText>.Empty),
- sol.Options,
- sol),
- delegate (Exception exception, DiagnosticAnalyzer analyzer, Diagnostic diag) {
- LoggingService.LogError ("Exception in diagnostic analyzer " + diag.Id + ":" + diag.GetMessage (), exception);
- },
- false,
- false
- );
-
- compilationWithAnalyzer = compilation.WithAnalyzers (analyzers, options);
- if (input.ParsedDocument == null || cancellationToken.IsCancellationRequested)
- return Enumerable.Empty<Result> ();
-
- diagnosticList.AddRange (await compilationWithAnalyzer.GetAnalyzerSemanticDiagnosticsAsync (model, null, cancellationToken).ConfigureAwait (false));
- diagnosticList.AddRange (await compilationWithAnalyzer.GetAnalyzerSyntaxDiagnosticsAsync (model.SyntaxTree, cancellationToken).ConfigureAwait (false));
- } catch (OperationCanceledException) {
- } catch (AggregateException ae) {
- ae.Flatten ().Handle (ix => ix is OperationCanceledException);
- } catch (Exception ex) {
- LoggingService.LogError ("Error creating analyzer compilation", ex);
- return Enumerable.Empty<Result> ();
- } finally {
- #if DEBUG
- Debug.Listeners.Remove (consoleTraceListener);
- #endif
- CompilationWithAnalyzers.ClearAnalyzerState (analyzers);
- }
-
- return diagnosticList
- .Where (d => !d.Id.StartsWith("CS", StringComparison.Ordinal))
- .Where (d => !diagnosticTable.TryGetValue (d.Id, out var desc) || desc.GetIsEnabled (d.Descriptor))
- .Select (diagnostic => {
- var res = new DiagnosticResult(diagnostic);
- // var line = analysisDocument.Editor.GetLineByOffset (res.Region.Start);
- // Console.WriteLine (diagnostic.Id + "/" + res.Region +"/" + analysisDocument.Editor.GetTextAt (line));
- return res;
- });
- } catch (OperationCanceledException) {
- return Enumerable.Empty<Result> ();
- } catch (AggregateException ae) {
- ae.Flatten ().Handle (ix => ix is OperationCanceledException);
- return Enumerable.Empty<Result> ();
- } catch (Exception e) {
- LoggingService.LogError ("Error while running diagnostics.", e);
- return Enumerable.Empty<Result> ();
- }
- }
-
public static async Task<IEnumerable<Result>> Check (AnalysisDocument analysisDocument, CancellationToken cancellationToken, ImmutableArray<DiagnosticData> results)
{
var input = analysisDocument.DocumentContext;
@@ -183,20 +63,21 @@ namespace MonoDevelop.CodeIssues
Debug.Listeners.Add (consoleTraceListener);
#endif
- await GetDescriptorTable (analysisDocument, cancellationToken);
-
var resultList = new List<Result> (results.Length);
foreach (var data in results) {
if (input.IsAdHocProject && SkipError (data.Id))
continue;
+ if (data.IsSuppressed)
+ continue;
+
if (DataHasTag (data, WellKnownDiagnosticTags.EditAndContinue))
continue;
- if (diagnosticTable.TryGetValue (data.Id, out var descriptor) && !descriptor.IsEnabled)
+ if (options.TryGetDiagnosticDescriptor (data.Id, out var desc) && !desc.GetIsEnabled (data.Id))
continue;
-
- var diagnostic = await data.ToDiagnosticAsync (analysisDocument, cancellationToken);
+
+ var diagnostic = await data.ToDiagnosticAsync (analysisDocument, cancellationToken, desc);
resultList.Add (new DiagnosticResult (diagnostic));
}
return resultList;
@@ -241,17 +122,13 @@ namespace MonoDevelop.CodeIssues
return !lexicalError.Contains (errorId);
}
- static async Task<Diagnostic> ToDiagnosticAsync (this DiagnosticData data, AnalysisDocument analysisDocument, CancellationToken cancellationToken)
+ static async Task<Diagnostic> ToDiagnosticAsync (this DiagnosticData data, AnalysisDocument analysisDocument, CancellationToken cancellationToken, CodeDiagnosticDescriptor desc)
{
var project = analysisDocument.DocumentContext.AnalysisDocument.Project;
var location = await data.DataLocation.ConvertLocationAsync (project, cancellationToken).ConfigureAwait (false);
var additionalLocations = await data.AdditionalLocations.ConvertLocationsAsync (project, cancellationToken).ConfigureAwait (false);
- DiagnosticSeverity severity;
- if (diagnosticTable.TryGetValue (data.Id, out var desc))
- severity = diagnosticTable [data.Id].GetSeverity (data.Id, data.Severity);
- else
- severity = data.Severity;
+ DiagnosticSeverity severity = desc != null ? desc.GetSeverity (data.Id, data.Severity) : data.Severity;
return Diagnostic.Create (
data.Id, data.Category, data.Message, severity, data.DefaultSeverity,
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/CodeIssuePanelWidget.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/CodeIssuePanelWidget.cs
index 59a26bc5ca..db0004a6c8 100644
--- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/CodeIssuePanelWidget.cs
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/CodeIssuePanelWidget.cs
@@ -32,6 +32,8 @@ using Gdk;
using GLib;
using Gtk;
using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+using MonoDevelop.AnalysisCore;
using MonoDevelop.CodeActions;
using MonoDevelop.Components;
using MonoDevelop.Core;
@@ -79,9 +81,16 @@ namespace MonoDevelop.CodeIssues
readonly Dictionary<Tuple<CodeDiagnosticDescriptor, DiagnosticDescriptor>, DiagnosticSeverity?> severities = new Dictionary<Tuple<CodeDiagnosticDescriptor, DiagnosticDescriptor>, DiagnosticSeverity?> ();
readonly Dictionary<Tuple<CodeDiagnosticDescriptor, DiagnosticDescriptor>, bool> enableState = new Dictionary<Tuple<CodeDiagnosticDescriptor, DiagnosticDescriptor>, bool> ();
+ static MonoDevelopWorkspaceDiagnosticAnalyzerProviderService.OptionsTable options =
+ ((MonoDevelopWorkspaceDiagnosticAnalyzerProviderService)Ide.Composition.CompositionManager.GetExportedValue<IWorkspaceDiagnosticAnalyzerProviderService> ()).Options;
+
void GetAllSeverities ()
{
- foreach (var node in BuiltInCodeDiagnosticProvider.GetBuiltInCodeDiagnosticDescriptorsAsync (CodeRefactoringService.MimeTypeToLanguage (mimeType), true).Result) {
+ var language = CodeRefactoringService.MimeTypeToLanguage (mimeType);
+ foreach (var node in options.AllDiagnostics) {
+ if (!node.Languages.Contains (language))
+ continue;
+
foreach (var subIssue in node.GetProvider ().SupportedDiagnostics.Where (IsConfigurable).ToList ()) {
var sub = new Tuple<CodeDiagnosticDescriptor, DiagnosticDescriptor> (node, subIssue);
severities [sub] = node.GetSeverity (subIssue);
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.addin.xml b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.addin.xml
index d0bb933641..1b2f6f142a 100644
--- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.addin.xml
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.addin.xml
@@ -150,4 +150,20 @@
<Assembly file="RefactoringEssentials.dll"/>
</Extension>
+ <ExtensionPoint path="/MonoDevelop/Refactoring/AnalyzerAssemblies">
+ <ExtensionNode name = "Assembly" type = "MonoDevelop.Core.AddIns.AssemblyExtensionNode" />
+ </ExtensionPoint>
+
+ <Extension path="/MonoDevelop/Refactoring/AnalyzerAssemblies">
+ <Assembly file="RefactoringEssentials" />
+ <Assembly file="Refactoring Essentials" />
+ <Assembly file="Microsoft.CodeAnalysis.CSharp" />
+ <Assembly file="Microsoft.CodeAnalysis.CSharp.Features" />
+ <Assembly file="Microsoft.CodeAnalysis.EditorFeatures" />
+ <Assembly file="Microsoft.CodeAnalysis.EditorFeatures.CSharp" />
+ <Assembly file="Microsoft.CodeAnalysis.Features" />
+ <Assembly file="Microsoft.CodeAnalysis.VisualBasic" />
+ <Assembly file="Microsoft.CodeAnalysis.VisualBasic.Features" />
+ </Extension>
+
</ExtensionModel>
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.csproj b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.csproj
index 7c2533e29e..565f8e0bb4 100644
--- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.csproj
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.csproj
@@ -174,12 +174,6 @@
<Compile Include="MonoDevelop.CodeIssues\CodeDiagnosticRunner.cs" />
<Compile Include="MonoDevelop.CodeActions\CodeActionContainer.cs" />
<Compile Include="MonoDevelop.AnalysisCore\IssueMarker.cs" />
- <Compile Include="MonoDevelop.CodeIssues\CodeDiagnosticProvider.cs" />
- <Compile Include="MonoDevelop.CodeIssues\BuiltInCodeDiagnosticProvider.cs" />
- <Compile Include="MonoDevelop.CodeIssues\AnalyzersFromAssembly.cs" />
- <Compile Include="MonoDevelop.CodeActions\ValidCodeAction.cs" />
- <Compile Include="MonoDevelop.CodeActions\ValidCodeDiagnosticAction.cs" />
- <Compile Include="MonoDevelop.CodeIssues\CodeDiagnosticFixDescriptor.cs" />
<Compile Include="MonoDevelop.Refactoring\Commands.cs" />
<Compile Include="MonoDevelop.Refactoring\RefactoringSymbolInfo.cs" />
<Compile Include="MonoDevelop.CodeIssues\CodeFix.cs" />
@@ -205,6 +199,7 @@
<Compile Include="MonoDevelop.AnalysisCore\MonoDevelopWorkspaceDiagnosticAnalyzerProviderService.AnalyzerAssemblyLoader.cs" />
<Compile Include="MonoDevelop.Refactoring.PickMembersService\PickMembersService.cs" />
<Compile Include="MonoDevelop.Refactoring.PickMembersService\PickMembersDialog.cs" />
+ <Compile Include="MonoDevelop.AnalysisCore\MonoDevelopWorkspaceDiagnosticAnalyzerProviderService.OptionsTable.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="MonoDevelop.Refactoring\" />
@@ -353,6 +348,7 @@
<InternalsVisibleTo Include="MonoDevelop.CSharpBinding" />
<InternalsVisibleTo Include="MonoDevelop.PackageManagement" />
<InternalsVisibleTo Include="MonoDevelop.MonoDroid" />
+ <InternalsVisibleTo Include="MonoDevelop.Refactoring.Tests" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring/AnalyzeWholeSolutionHandler.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring/AnalyzeWholeSolutionHandler.cs
index 8fe1b65c0e..8994b736d3 100644
--- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring/AnalyzeWholeSolutionHandler.cs
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring/AnalyzeWholeSolutionHandler.cs
@@ -28,11 +28,11 @@ using MonoDevelop.Components.Commands;
using MonoDevelop.Ide.TypeSystem;
using System.Threading.Tasks;
using System.Threading;
-using Microsoft.CodeAnalysis.Diagnostics;
using System.Collections.Generic;
using MonoDevelop.CodeActions;
using MonoDevelop.Ide.Editor;
using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Options;
using MonoDevelop.CodeIssues;
using MonoDevelop.Core;
@@ -42,23 +42,26 @@ using MonoDevelop.Ide.Gui.Pads;
using System.Collections.Immutable;
using System.Linq;
using MonoDevelop.Ide.Gui.Components;
+using MonoDevelop.AnalysisCore;
namespace MonoDevelop.Refactoring
{
class AnalyzeWholeSolutionHandler : CommandHandler
{
+ static MonoDevelopWorkspaceDiagnosticAnalyzerProviderService.OptionsTable options =
+ ((MonoDevelopWorkspaceDiagnosticAnalyzerProviderService)Ide.Composition.CompositionManager.GetExportedValue<IWorkspaceDiagnosticAnalyzerProviderService> ()).Options;
+
internal static async Task<List<DiagnosticAnalyzer>> GetProviders (Project project)
{
var providers = new List<DiagnosticAnalyzer> ();
var alreadyAdded = new HashSet<Type> ();
- var diagnostics = await CodeRefactoringService.GetCodeDiagnosticsAsync (new EmptyDocumentContext (project), LanguageNames.CSharp, default (CancellationToken));
+ var diagnostics = options.AllDiagnostics.Where (x => x.Languages.Contains (LanguageNames.CSharp));
var diagnosticTable = new Dictionary<string, CodeDiagnosticDescriptor> ();
foreach (var diagnostic in diagnostics) {
- if (alreadyAdded.Contains (diagnostic.DiagnosticAnalyzerType))
- continue;
if (!diagnostic.IsEnabled)
continue;
- alreadyAdded.Add (diagnostic.DiagnosticAnalyzerType);
+ if (!alreadyAdded.Add (diagnostic.DiagnosticAnalyzerType))
+ continue;
var provider = diagnostic.GetProvider ();
if (provider == null)
continue;
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MdTextViewLineCollection.MdTextViewLine.cs b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MdTextViewLineCollection.MdTextViewLine.cs
new file mode 100644
index 0000000000..46749c13e5
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MdTextViewLineCollection.MdTextViewLine.cs
@@ -0,0 +1,226 @@
+//
+// MdTextViewLineCollection.MdTextViewLine.cs
+//
+// Author:
+// Mike Krüger <mikkrg@microsoft.com>
+//
+// Copyright (c) 2018 Microsoft Corporation. All rights reserved.
+//
+// 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 Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Formatting;
+using Microsoft.VisualStudio.Text.Implementation;
+
+namespace Mono.TextEditor
+{
+ partial class MdTextViewLineCollection
+ {
+ sealed class MdTextViewLine : ITextViewLine
+ {
+ readonly DocumentLine line;
+ readonly TextViewMargin.LayoutWrapper layoutWrapper;
+ readonly MdTextViewLineCollection collection;
+ MonoTextEditor textEditor;
+ SnapshotSpan lineSpan;
+ int lineBreakLength;
+
+ public MdTextViewLine(MdTextViewLineCollection collection, MonoTextEditor textEditor, DocumentLine line, TextViewMargin.LayoutWrapper layoutWrapper)
+ {
+ this.collection = collection;
+ this.layoutWrapper = layoutWrapper;
+ this.textEditor = textEditor;
+ this.line = line;
+ this.lineSpan = new SnapshotSpan(textEditor.VisualSnapshot, line.Offset, line.LengthIncludingDelimiter);
+ this.lineBreakLength = line.DelimiterLength;
+ }
+
+ object indentityTag = new object ();
+ public object IdentityTag => indentityTag;
+
+ public ITextSnapshot Snapshot => lineSpan.Snapshot;
+
+ public bool IsFirstTextViewLineForSnapshotLine => collection[0] == this;
+
+ public bool IsLastTextViewLineForSnapshotLine => collection[collection.Count - 1] == this;
+
+ public double Baseline => throw new System.NotImplementedException();
+
+ public SnapshotSpan Extent => new SnapshotSpan (Snapshot, line.Offset, line.Length);
+
+ public IMappingSpan ExtentAsMappingSpan => new MappingSpan (Extent, SpanTrackingMode.EdgeInclusive, null);
+
+ public SnapshotSpan ExtentIncludingLineBreak => new SnapshotSpan (Snapshot, line.Offset, line.LengthIncludingDelimiter);
+
+ public IMappingSpan ExtentIncludingLineBreakAsMappingSpan => new MappingSpan (ExtentIncludingLineBreak, SpanTrackingMode.EdgeInclusive, null);
+
+ public SnapshotPoint Start => lineSpan.Start;
+
+ public int Length => Length - LineBreakLength;
+
+ public int LengthIncludingLineBreak => lineSpan.Length;
+
+ public SnapshotPoint End => EndIncludingLineBreak - LineBreakLength;
+
+ public SnapshotPoint EndIncludingLineBreak => lineSpan.End;
+
+ public int LineBreakLength => lineBreakLength;
+
+ public double Left => textEditor.VAdjustment.Value;
+
+ public double Top => textEditor.LocationToPoint (line.LineNumber, 0).Y;
+
+ public double Height => layoutWrapper.Height;
+
+ public double TextTop => Top;
+
+ public double TextBottom => Top + TextHeight;
+
+ public double TextHeight => textEditor.LineHeight;
+
+ public double TextLeft => Left;
+
+ public double TextRight => layoutWrapper.Width;
+
+ public double TextWidth => layoutWrapper.Width;
+
+ public double Width => textEditor.TextViewMargin.RectInParent.Width;
+
+ public double Bottom => TextBottom;
+
+ public double Right => layoutWrapper.Width;
+
+ public double EndOfLineWidth => 0;
+
+ public double VirtualSpaceWidth => 0;
+
+ public bool IsValid => true;
+
+ public LineTransform LineTransform => DefaultLineTransform;
+
+ public LineTransform DefaultLineTransform => new LineTransform ();
+
+ public VisibilityState VisibilityState => VisibilityState.FullyVisible;
+
+ public double DeltaY => 0;
+
+ public TextViewLineChange Change => TextViewLineChange.None;
+
+ SnapshotPoint FixBufferPosition(SnapshotPoint bufferPosition)
+ {
+ if (bufferPosition.Snapshot != this.lineSpan.Snapshot)
+ throw new ArgumentException("The specified SnapshotPoint is on a different ITextSnapshot than this SnapshotPoint.");
+
+ return bufferPosition;
+ }
+
+ public bool ContainsBufferPosition(SnapshotPoint bufferPosition)
+ {
+ bufferPosition = this.FixBufferPosition(bufferPosition);
+
+ return ((bufferPosition >= lineSpan.Start) &&
+ ((bufferPosition < lineSpan.End) ||
+ ((bufferPosition == lineSpan.End) &&
+ (lineBreakLength == 0) && (lineSpan.End == lineSpan.Snapshot.Length))));
+ }
+
+ public TextBounds? GetAdornmentBounds(object identityTag)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public ReadOnlyCollection<object> GetAdornmentTags(object providerTag)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public SnapshotPoint? GetBufferPositionFromXCoordinate(double xCoordinate, bool textOnly)
+ {
+ var y = textEditor.LocationToPoint(textEditor.OffsetToLocation(lineSpan.Start)).Y;
+ var loc = textEditor.PointToLocation(xCoordinate, y);
+ var pos = textEditor.LocationToOffset(loc);
+ return new SnapshotPoint(Snapshot, pos);
+ }
+
+ public SnapshotPoint? GetBufferPositionFromXCoordinate(double xCoordinate)
+ {
+ return GetBufferPositionFromXCoordinate(xCoordinate, true);
+ }
+
+ public TextBounds GetCharacterBounds(SnapshotPoint bufferPosition)
+ {
+ var point = textEditor.LocationToPoint(textEditor.OffsetToLocation(bufferPosition.Position));
+ return new TextBounds(point.X, point.Y, textEditor.TextViewMargin.CharWidth, textEditor.LineHeight, TextTop, textEditor.LineHeight);
+ }
+
+ public TextBounds GetCharacterBounds(VirtualSnapshotPoint bufferPosition)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public TextBounds GetExtendedCharacterBounds(SnapshotPoint bufferPosition)
+ {
+ return new TextBounds(textEditor.LocationToPoint(textEditor.OffsetToLocation(bufferPosition.Position)).X, Top, Width, TextHeight, TextTop, TextHeight);
+ }
+
+ public TextBounds GetExtendedCharacterBounds(VirtualSnapshotPoint bufferPosition)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public VirtualSnapshotPoint GetInsertionBufferPositionFromXCoordinate(double xCoordinate)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public Collection<TextBounds> GetNormalizedTextBounds(SnapshotSpan bufferSpan)
+ {
+ if (bufferSpan.OverlapsWith(lineSpan))
+ {
+ double leading = 0;
+ if (lineSpan.Contains(bufferSpan.Start))
+ leading = textEditor.LocationToPoint(textEditor.OffsetToLocation(bufferSpan.Start)).X;
+ var endLoc = textEditor.OffsetToLocation(lineSpan.Contains(bufferSpan.End) ? bufferSpan.End : lineSpan.End);
+ double endPos = textEditor.LocationToPoint(endLoc).X;
+ return new Collection<TextBounds>(new List<TextBounds>() { new TextBounds(leading, Top, endPos - leading, TextHeight, TextTop, TextHeight) });
+ }
+ else
+ return new Collection<TextBounds>();
+ }
+
+ public SnapshotSpan GetTextElementSpan(SnapshotPoint bufferPosition)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public VirtualSnapshotPoint GetVirtualBufferPositionFromXCoordinate(double xCoordinate)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public bool IntersectsBufferSpan(SnapshotSpan bufferSpan)
+ {
+ return lineSpan.IntersectsWith (bufferSpan);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MdTextViewLineCollection.cs b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MdTextViewLineCollection.cs
new file mode 100644
index 0000000000..789dde16ad
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MdTextViewLineCollection.cs
@@ -0,0 +1,145 @@
+//
+// MdTextViewLineCollection.cs
+//
+// Author:
+// Mike Krüger <mikkrg@microsoft.com>
+//
+// Copyright (c) 2018 Microsoft Corporation. All rights reserved.
+//
+// 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.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Formatting;
+using MonoDevelop.Core.Text;
+
+namespace Mono.TextEditor
+{
+ partial class MdTextViewLineCollection : List<ITextViewLine>, ITextViewLineCollection
+ {
+ readonly MonoTextEditor textView;
+ readonly ITextSourceVersion version;
+
+ public MdTextViewLineCollection (MonoTextEditor textEditor) : base (64)
+ {
+ this.textView = textEditor;
+ this.version = this.textView.Document.Version;
+
+ int startLine = textEditor.TextArea.YToLine (textEditor.VAdjustment.Value);
+ double startY = textEditor.TextArea.LineToY (startLine);
+ double curY = startY;
+ double lastY = textEditor.VAdjustment.Value + textEditor.Allocation.Height;
+
+ for (int visualLineNumber = textEditor.GetTextEditorData ().LogicalToVisualLine (startLine); ; visualLineNumber++) {
+ int logicalLineNumber = textEditor.GetTextEditorData ().VisualToLogicalLine (visualLineNumber);
+ var line = textEditor.GetLine (logicalLineNumber);
+
+ if (line == null)
+ break;
+
+ Add (new MdTextViewLine (this, textEditor, line, textEditor.TextViewMargin.GetLayout (line)));
+
+ curY += textEditor.TextArea.GetLineHeight (line);
+ if (curY >= lastY)
+ break;
+ }
+ }
+
+ public ITextViewLine FirstVisibleLine => this.FirstOrDefault ();
+
+ public ITextViewLine LastVisibleLine => this.LastOrDefault ();
+
+ public SnapshotSpan FormattedSpan => new SnapshotSpan (this [0].Start, this.Last ().EndIncludingLineBreak);
+
+ public bool IsValid => version.CompareAge (textView.Document.Version) == 0;
+
+ public bool ContainsBufferPosition (SnapshotPoint bufferPosition)
+ {
+ foreach (var line in this) {
+ if (line.Start <= bufferPosition && line.End < bufferPosition)
+ return true;
+ }
+ return false;
+ }
+
+ public TextBounds GetCharacterBounds (SnapshotPoint bufferPosition)
+ {
+ foreach (var line in this) {
+ if (line.Start <= bufferPosition && line.End < bufferPosition) {
+ // TODO: correct both 0 parameters
+ return new TextBounds (0, line.Top, 0, line.Height, line.TextTop, line.TextHeight);
+ }
+ }
+
+ return new TextBounds ();
+ }
+
+ public int GetIndexOfTextLine (ITextViewLine textLine)
+ {
+ return IndexOf (textLine);
+ }
+
+ public Collection<TextBounds> GetNormalizedTextBounds (SnapshotSpan bufferSpan)
+ {
+ var bounds = new Collection<TextBounds> ();
+ foreach (var line in this)
+ foreach (var bound in line.GetNormalizedTextBounds (bufferSpan))
+ bounds.Add (bound);
+ return bounds;
+ }
+
+ public SnapshotSpan GetTextElementSpan (SnapshotPoint bufferPosition)
+ {
+ return new SnapshotSpan (bufferPosition, 1);
+ }
+
+ public ITextViewLine GetTextViewLineContainingBufferPosition (SnapshotPoint bufferPosition)
+ {
+ return this.FirstOrDefault (l => l.ContainsBufferPosition (bufferPosition));
+ }
+
+ public ITextViewLine GetTextViewLineContainingYCoordinate (double y)
+ {
+ return this.FirstOrDefault (l => l.Top <= y && l.Top + l.Height >= y);
+ }
+
+ public Collection<ITextViewLine> GetTextViewLinesIntersectingSpan (SnapshotSpan bufferSpan)
+ {
+ var result = new Collection<ITextViewLine> ();
+ foreach (var line in this)
+ if (line.IntersectsBufferSpan (bufferSpan))
+ result.Add (line);
+ return result;
+ }
+
+ public bool IntersectsBufferSpan (SnapshotSpan bufferSpan)
+ {
+ foreach (var line in this)
+ if (line.IntersectsBufferSpan (bufferSpan))
+ return true;
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MonoTextEditor.ITextView.MouseHover.cs b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MonoTextEditor.ITextView.MouseHover.cs
new file mode 100644
index 0000000000..eb5a29d6cb
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MonoTextEditor.ITextView.MouseHover.cs
@@ -0,0 +1,226 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Timers;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+using Xwt;
+
+namespace Mono.TextEditor
+{
+ partial class MonoTextEditor
+ {
+ internal IList<MouseHoverEventData> _mouseHoverEvents = new List<MouseHoverEventData> ();
+
+ internal Timer _mouseHoverTimer;
+ internal int _millisecondsSinceMouseMove = 0;
+
+ internal int? _lastHoverPosition = null;
+
+ private void InitializeMouse ()
+ {
+ if (_mouseHoverTimer != null)
+ return;
+ _mouseHoverTimer = new Timer ();
+ _mouseHoverTimer.Elapsed += delegate {
+ MonoDevelop.Core.Runtime.RunInMainThread (() => {
+ this.OnHoverTimer ();
+ });
+ };
+ }
+
+ internal void OnHoverTimer ()
+ {
+ if (!isClosed) {
+ _millisecondsSinceMouseMove += (int)_mouseHoverTimer.Interval;
+
+ if (Visible && _lastHoverPosition.HasValue)
+ this.RaiseHoverEvents ();
+ }
+ }
+
+ /// <summary>
+ /// Event raised whenever the mouse has hovered over the same character
+ /// for 150 ms.
+ /// </summary>
+ /// <remarks>No hover events will be generated when the mouse is not over text in the buffer.</remarks>
+ public event EventHandler<MouseHoverEventArgs> MouseHover {
+ add {
+ lock (_mouseHoverEvents) {
+ InitializeMouse ();
+ if (_mouseHoverEvents.Count == 0) {
+ MotionNotifyEvent += OnMouseMove;
+ ButtonPressEvent += this.OnMouseDown;
+ }
+
+ _mouseHoverEvents.Add (new MouseHoverEventData (value));
+ }
+ }
+
+ remove {
+ lock (_mouseHoverEvents) {
+ for (int i = _mouseHoverEvents.Count - 1; (i >= 0); --i) {
+ if (_mouseHoverEvents [i].EventHandler == value) {
+ _mouseHoverEvents.RemoveAt (i);
+
+ if (_mouseHoverEvents.Count == 0) {
+ MotionNotifyEvent -= this.OnMouseMove;
+ ButtonPressEvent -= this.OnMouseDown;
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ internal void OnMouseMove (object o, Gtk.MotionNotifyEventArgs args)
+ {
+ if (!isClosed) {
+ MonoDevelop.Core.Runtime.AssertMainThread ();
+ var but1 = (args.Event.State & Gdk.ModifierType.Button1Mask) == Gdk.ModifierType.Button1Mask;
+ var but2 = (args.Event.State & Gdk.ModifierType.Button2Mask) == Gdk.ModifierType.Button2Mask;
+ var but3 = (args.Event.State & Gdk.ModifierType.Button3Mask) == Gdk.ModifierType.Button3Mask;
+ if (!but1 && !but2 && !but3) {
+ this.HandleMouseMove (new Point (args.Event.X, args.Event.Y));
+ }
+ }
+ }
+
+ void OnMouseDown (object o, Gtk.ButtonPressEventArgs args)
+ {
+ if (!isClosed) {
+ _mouseHoverTimer.Stop ();
+ }
+ }
+
+
+ #region Mouse Related Helpers
+
+ internal class MouseHoverEventData
+ {
+ public readonly MouseHoverAttribute Attribute;
+ public readonly EventHandler<MouseHoverEventArgs> EventHandler;
+ public bool Fired;
+
+ public MouseHoverEventData (EventHandler<MouseHoverEventArgs> eventHandler)
+ {
+ this.Attribute = GetMouseHoverAttribute (eventHandler);
+ this.EventHandler = eventHandler;
+ this.Fired = false;
+ }
+
+ private static MouseHoverAttribute GetMouseHoverAttribute (EventHandler<MouseHoverEventArgs> client)
+ {
+ object [] attributes = client.Method.GetCustomAttributes (typeof (MouseHoverAttribute), false);
+ foreach (object attribute in attributes) {
+ MouseHoverAttribute mouseHoverAttribute = attribute as MouseHoverAttribute;
+ if (mouseHoverAttribute != null) {
+ return mouseHoverAttribute;
+ }
+ }
+
+ return new MouseHoverAttribute (150);
+ }
+ }
+
+ // internal for exposure to unit tests
+ internal void RaiseHoverEvents ()
+ {
+ MonoDevelop.Core.Runtime.AssertMainThread ();
+
+ //See if there are any unfired events that are ready to fire.
+ MouseHoverEventData nextEvent = null;
+ IList<MouseHoverEventData> eventsToFire = new List<MouseHoverEventData> ();
+ lock (_mouseHoverEvents) {
+ foreach (var eventData in _mouseHoverEvents) {
+ if (!eventData.Fired) {
+ if (eventData.Attribute.Delay <= _millisecondsSinceMouseMove) {
+ eventsToFire.Add (eventData);
+ } else if ((nextEvent == null) || (eventData.Attribute.Delay < nextEvent.Attribute.Delay))
+ nextEvent = eventData;
+ }
+ }
+ }
+
+ if (eventsToFire.Count > 0) {
+ MouseHoverEventArgs args = new MouseHoverEventArgs (this, _lastHoverPosition.Value,
+ bufferGraph.CreateMappingPoint (new SnapshotPoint (Document.TextBuffer.CurrentSnapshot, _lastHoverPosition.Value), PointTrackingMode.Positive));
+ foreach (var eventData in eventsToFire) {
+ eventData.Fired = true;
+
+ try {
+ eventData.EventHandler (this, args);
+ } catch (Exception e) {
+ factoryService.GuardedOperations.HandleException (eventData.EventHandler, e);
+ }
+ }
+ }
+
+ if (nextEvent == null) {
+ //No more events to fire ... stop the timer.
+ _mouseHoverTimer.Stop ();
+ } else {
+ //Set the timer interval to match the delay to the next event.
+ int newDelay = Math.Max (50, (nextEvent.Attribute.Delay - _millisecondsSinceMouseMove));
+ _mouseHoverTimer.Interval = newDelay;
+ }
+ }
+
+ internal void HandleMouseMove (Point pt)
+ {
+ if (_mouseHoverEvents.Count > 0) {
+ int? newPosition = null;
+
+ if ((pt.X >= 0.0) && (pt.X < this.ViewportWidth) &&
+ (pt.Y >= 0.0) && (pt.Y < this.ViewportHeight)) {
+ double y = pt.Y + this.ViewportTop;
+
+ var line = TextViewLines.GetTextViewLineContainingYCoordinate (y);
+ if ((line != null) && (y >= line.TextTop) && (y <= line.TextBottom)) {
+ double x = pt.X + this.ViewportLeft;
+ newPosition = line.GetBufferPositionFromXCoordinate (x, true);
+ if ((!newPosition.HasValue) && (line.LineBreakLength == 0) && line.IsLastTextViewLineForSnapshotLine) {
+ //For purposes of hover events, pretend the last line in the buffer
+ //actually is padded by the EndOfLineWidth (even though it is not).
+ if ((line.TextRight <= x) && (x < line.TextRight + line.EndOfLineWidth))
+ newPosition = line.End;
+ }
+ }
+ }
+
+ if (newPosition != _lastHoverPosition) {
+ _lastHoverPosition = newPosition;
+
+ //The mouse moved to a different character, reset the timer.
+ _mouseHoverTimer.Stop ();
+
+ //If the mouse is over a character, reset the events & restart the timer.
+ if (newPosition.HasValue) {
+ int delay = int.MaxValue;
+ lock (_mouseHoverEvents) {
+ foreach (var eventData in _mouseHoverEvents) {
+ eventData.Fired = false;
+ if (eventData.Attribute.Delay < delay)
+ delay = eventData.Attribute.Delay;
+ }
+ }
+
+ //In theory the last event could have been removed on a background thread after we checked the count.
+ if (delay != int.MaxValue) {
+ _millisecondsSinceMouseMove = 0;
+ _mouseHoverTimer.Interval = Math.Max (50, delay);
+ _mouseHoverTimer.Start ();
+ }
+ }
+ }
+ }
+ }
+
+ #endregion //Mouse Related Helpers
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MonoTextEditor.ITextView.cs b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MonoTextEditor.ITextView.cs
new file mode 100644
index 0000000000..636046e7d8
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MonoTextEditor.ITextView.cs
@@ -0,0 +1,510 @@
+//
+// MonoTextEditor.ITextView.cs
+//
+// Author:
+// Mike Krüger <mikkrg@microsoft.com>
+//
+// Copyright (c) 2018 Microsoft Corporation. All rights reserved.
+//
+// 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 System.Text;
+using System.Threading.Tasks;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Formatting;
+using Microsoft.VisualStudio.Text.Projection;
+using Microsoft.VisualStudio.Utilities;
+using Microsoft.VisualStudio.Text.Editor.Implementation;
+using Microsoft.VisualStudio.Text.Utilities;
+using System.Diagnostics;
+using MonoDevelop.Ide;
+using Microsoft.VisualStudio.Text.Classification;
+using System.Threading;
+
+namespace Mono.TextEditor
+{
+ partial class MonoTextEditor : IMdTextView
+ {
+ #region Private Members
+
+ public MonoTextEditor VisualElement { get => this; }
+
+ ITextBuffer textBuffer;
+
+ IBufferGraph bufferGraph;
+ ITextViewRoleSet roles;
+
+ ConnectionManager connectionManager;
+
+ TextEditorFactoryService factoryService;
+ int queuedSpaceReservationStackRefresh = 0; //int so that it can be set via Interlocked.CompareExchange()
+
+ // IEditorFormatMap _editorFormatMap;
+
+ bool hasInitializeBeenCalled = false;
+
+ ITextSelection selection;
+
+ bool hasAggregateFocus;
+
+ IEditorOptions editorOptions;
+
+ List<Lazy<ITextViewCreationListener, IDeferrableContentTypeAndTextViewRoleMetadata>> deferredTextViewListeners;
+
+ bool isClosed = false;
+
+ private PropertyCollection properties = new PropertyCollection ();
+
+ //Only one view at a time will have aggregate focus, so keep track of it so that (when sending aggregate focus changed events)
+ //we give a view that had focus the chance to send its lost focus message before we claim aggregate focus.
+ [ThreadStatic]
+ static MonoTextEditor ViewWithAggregateFocus = null;
+#if DEBUG
+ [ThreadStatic]
+ static bool SettingAggregateFocus = false;
+#endif
+
+ #endregion // Private Members
+
+ /// <summary>
+ /// Text View constructor.
+ /// </summary>
+ /// <param name="textViewModel">The text view model that provides the text to visualize.</param>
+ /// <param name="roles">Roles for this view.</param>
+ /// <param name="parentOptions">Parent options for this view.</param>
+ /// <param name="factoryService">Our handy text editor factory service.</param>
+ internal void Initialize (ITextViewModel textViewModel, ITextViewRoleSet roles, IEditorOptions parentOptions, TextEditorFactoryService factoryService, bool initialize = true)
+ {
+ this.roles = roles;
+
+ this.factoryService = factoryService;
+ GuardedOperations = this.factoryService.GuardedOperations;
+ _spaceReservationStack = new SpaceReservationStack(this.factoryService.OrderedSpaceReservationManagerDefinitions, this);
+
+ this.TextDataModel = textViewModel.DataModel;
+ this.TextViewModel = textViewModel;
+
+ textBuffer = textViewModel.EditBuffer;
+ // _visualBuffer = textViewModel.VisualBuffer;
+
+ // _textSnapshot = _textBuffer.CurrentSnapshot;
+ // _visualSnapshot = _visualBuffer.CurrentSnapshot;
+
+ editorOptions = this.factoryService.EditorOptionsFactoryService.GetOptions (this);
+ editorOptions.Parent = parentOptions;
+
+ if (initialize)
+ this.Initialize ();
+ }
+
+ internal bool IsTextViewInitialized { get { return hasInitializeBeenCalled; } }
+
+ // This method should only be called once (it is normally called from the ctor unless we're using
+ // ITextEditorFactoryService2.CreateTextViewWithoutInitialization on the factory to delay initialization).
+ internal void Initialize ()
+ {
+ if (hasInitializeBeenCalled)
+ throw new InvalidOperationException ("Attempted to Initialize a WpfTextView twice");
+
+ bufferGraph = factoryService.BufferGraphFactoryService.CreateBufferGraph (this.TextViewModel.VisualBuffer);
+
+ //_editorFormatMap = _factoryService.EditorFormatMapService.GetEditorFormatMap(this);
+
+ selection = new TextSelection (this);
+
+ // this.Loaded += OnLoaded;
+
+ // TODO: *Someone* needs to call this to execute UndoHistoryRegistry.RegisterHistory -- VS does this via the ShimCompletionControllerFactory.
+ factoryService.EditorOperationsProvider.GetEditorOperations (this);
+
+ connectionManager = new ConnectionManager (this, factoryService.TextViewConnectionListeners, factoryService.GuardedOperations);
+
+ SubscribeToEvents ();
+
+ // Binding content type specific assets includes calling out to content-type
+ // specific view creation listeners. We need to do this as late as possible.
+ this.BindContentTypeSpecificAssets (null, TextViewModel.DataModel.ContentType);
+
+ //Subscribe now so that there is no chance that a layout could be forced by a text change.
+ //_visualBuffer.ChangedLowPriority += OnVisualBufferChanged;
+ //_visualBuffer.ContentTypeChanged += OnVisualBufferContentTypeChanged;
+
+ hasInitializeBeenCalled = true;
+ }
+
+ ITextCaret ITextView.Caret {
+ get {
+ return Caret;
+ }
+ }
+
+ public bool HasAggregateFocus {
+ get {
+ return hasAggregateFocus;
+ }
+ }
+
+ public bool InLayout {
+ get {
+ return false;
+ }
+ }
+
+ public bool IsClosed { get { return isClosed; } }
+
+ public bool IsMouseOverViewOrAdornments {
+ get {
+ return textArea.IsMouseTrapped;
+ }
+ }
+
+ public double MaxTextRightCoordinate {
+ get {
+ return Allocation.Width - TextViewMargin.XOffset;
+ }
+ }
+
+ IEditorOptions ITextView.Options {
+ get { return editorOptions; }
+ }
+
+ public PropertyCollection Properties {
+ get {
+ return properties;
+ }
+ }
+
+ public ITrackingSpan ProvisionalTextHighlight {
+ get {
+ throw new NotImplementedException ();
+ }
+
+ set {
+ throw new NotImplementedException ();
+ }
+ }
+
+ public ITextSelection Selection {
+ get {
+ return selection;
+ }
+ }
+
+ public ITextViewRoleSet Roles {
+ get {
+ return roles;
+ }
+ }
+
+ /// <summary>
+ /// Gets the text buffer whose text, this text editor renders
+ /// </summary>
+ public ITextBuffer TextBuffer {
+ get {
+ return textBuffer;
+ }
+ }
+
+ public IBufferGraph BufferGraph {
+ get {
+ return bufferGraph;
+ }
+ }
+
+ public ITextSnapshot TextSnapshot {
+ get {
+ // TODO: MONO: WpfTextView has a much more complex calculation of this
+ return TextBuffer.CurrentSnapshot;
+ // return _textSnapshot;
+ }
+ }
+
+ public ITextSnapshot VisualSnapshot {
+ get {
+ return TextBuffer.CurrentSnapshot;
+ // return _visualSnapshot;
+ }
+ }
+
+ public ITextDataModel TextDataModel { get; private set; }
+ public ITextViewModel TextViewModel { get; private set; }
+
+ public ITextViewLineCollection TextViewLines { get => new MdTextViewLineCollection (this); }
+
+ public double ViewportBottom {
+ get {
+ return TextViewMargin.RectInParent.Bottom;
+ }
+ }
+
+ public double ViewportHeight {
+ get {
+ return TextViewMargin.RectInParent.Height;
+ }
+ }
+
+ public double ViewportLeft {
+ get {
+ return 0;
+ }
+ set {
+ throw new NotImplementedException ();
+ }
+ }
+
+ public double ViewportRight {
+ get {
+ return TextViewMargin.RectInParent.Right + HAdjustment.Value;
+ }
+ }
+
+ public double ViewportTop {
+ get {
+ return TextViewMargin.RectInParent.Top;// + VAdjustment.Value;
+ }
+ }
+
+ public double ViewportWidth {
+ get {
+ return TextViewMargin.RectInParent.Width;
+ }
+ }
+
+ public IViewScroller ViewScroller {
+ get {
+ return this;
+ }
+ }
+
+ public event EventHandler Closed;
+ public event EventHandler GotAggregateFocus;
+ public event EventHandler LostAggregateFocus;
+#pragma warning disable CS0067
+ public event EventHandler<TextViewLayoutChangedEventArgs> LayoutChanged;
+ public event EventHandler ViewportLeftChanged;
+ public event EventHandler ViewportHeightChanged;
+ public event EventHandler ViewportWidthChanged;
+#pragma warning restore CS0067
+
+ public void Close ()
+ {
+ if (isClosed)
+ throw new InvalidOperationException ();//Strings.TextViewClosed);
+ isClosed = true;
+
+ factoryService.GuardedOperations.RaiseEvent (this, this.Closed);
+
+ if (hasAggregateFocus) {
+ //Silently lose aggregate focus (to preserve Dev11 compatibility which did not raise a focus changed event when the view was closed).
+ Debug.Assert (ViewWithAggregateFocus == this);
+ ViewWithAggregateFocus = null;
+ hasAggregateFocus = false;
+ }
+
+ UnsubscribeFromEvents ();
+
+ connectionManager.Close ();
+
+ TextViewModel.Dispose ();
+ TextViewModel = null;
+ }
+
+ public void DisplayTextLineContainingBufferPosition (SnapshotPoint bufferPosition, double verticalDistance, ViewRelativePosition relativeTo)
+ {
+ this.textArea.ScrollTo (bufferPosition.Position);
+ }
+
+ public void DisplayTextLineContainingBufferPosition (SnapshotPoint bufferPosition, double verticalDistance, ViewRelativePosition relativeTo, double? viewportWidthOverride, double? viewportHeightOverride)
+ {
+ this.textArea.ScrollTo (bufferPosition.Position);
+ }
+
+ public SnapshotSpan GetTextElementSpan (SnapshotPoint point)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public ITextViewLine GetTextViewLineContainingBufferPosition (SnapshotPoint bufferPosition)
+ {
+ return TextViewLines.GetTextViewLineContainingBufferPosition (bufferPosition);
+ }
+
+ public void QueueSpaceReservationStackRefresh ()
+ {
+ if (Interlocked.CompareExchange (ref queuedSpaceReservationStackRefresh, 1, 0) == 0) {
+ MonoDevelop.Core.Runtime.RunInMainThread (new Action (delegate {
+ Interlocked.Exchange (ref queuedSpaceReservationStackRefresh, 0);
+
+ if (!isClosed) {
+ _spaceReservationStack.Refresh ();
+ }
+ }));
+ }
+ }
+
+ /// <remarks>
+ /// If you add an event subscription to this method, be sure to add the corresponding unsubscription to
+ /// UnsubscribeFromEvents()
+ /// </remarks>
+ private void SubscribeToEvents ()
+ {
+ if (IdeApp.IsInitialized)
+ IdeApp.Workbench.ActiveDocumentChanged += Workbench_ActiveDocumentChanged;
+ }
+
+ void Workbench_ActiveDocumentChanged (object sender, EventArgs e)
+ {
+ QueueAggregateFocusCheck ();
+ }
+
+ private void UnsubscribeFromEvents ()
+ {
+ if (IdeApp.IsInitialized)
+ IdeApp.Workbench.ActiveDocumentChanged -= Workbench_ActiveDocumentChanged;
+ }
+
+ private void BindContentTypeSpecificAssets (IContentType beforeContentType, IContentType afterContentType)
+ {
+ // Notify the Text view creation listeners
+ var extensions = UIExtensionSelector.SelectMatchingExtensions (factoryService.TextViewCreationListeners, afterContentType, beforeContentType, roles);
+ foreach (var extension in extensions) {
+ string deferOptionName = extension.Metadata.OptionName;
+ if (!string.IsNullOrEmpty (deferOptionName) && ((ITextView)this).Options.IsOptionDefined (deferOptionName, false)) {
+ object value = ((ITextView)this).Options.GetOptionValue (deferOptionName);
+ if (value is bool) {
+ if (!(bool)value) {
+ if (deferredTextViewListeners == null) {
+ deferredTextViewListeners = new List<Lazy<ITextViewCreationListener, IDeferrableContentTypeAndTextViewRoleMetadata>> ();
+ }
+ deferredTextViewListeners.Add (extension);
+ continue;
+ }
+ }
+ }
+
+ var instantiatedExtension = factoryService.GuardedOperations.InstantiateExtension (extension, extension);
+ if (instantiatedExtension != null) {
+ factoryService.GuardedOperations.CallExtensionPoint (instantiatedExtension,
+ () => instantiatedExtension.TextViewCreated (this));
+ }
+ }
+ }
+
+ /// <summary>
+ /// Handles the Classification changed event that comes from the Classifier aggregator
+ /// </summary>
+ void OnClassificationChanged (object sender, ClassificationChangedEventArgs e)
+ {
+ if (!isClosed) {
+ // When classifications change, we just invalidate the lines. That invalidation will
+ // create new lines based on the new classifications.
+
+ // Map the classification change (from the edit buffer) to the visual buffer
+ Span span = Span.FromBounds (
+ TextViewModel.GetNearestPointInVisualSnapshot (e.ChangeSpan.Start, VisualSnapshot, PointTrackingMode.Negative),
+ TextViewModel.GetNearestPointInVisualSnapshot (e.ChangeSpan.End, VisualSnapshot, PointTrackingMode.Positive));
+
+ //Classifications changes invalidate only the characters contained in the span so a zero length change
+ //will have no effect.
+ if (span.Length > 0) {
+ //IsLineInvalid will invalidate a line if it intersects the end. The result is that any call to InvalidateSpan() implicitly
+ //invalidates any line that starts at the end of the invalidated span, which we do not want here. Reduce the length of the classification
+ //change span one so -- if someone invalidated an entire line including the line break -- the next line will not be invalidated.
+ span = new Span (span.Start, span.Length - 1);
+
+ // MONO: TODO: this
+
+ //lock (_invalidatedSpans)
+ //{
+ // if ((_attachedLineCache.Count > 0) || (_unattachedLineCache.Count > 0))
+ // {
+ // _reclassifiedSpans.Add(span);
+ // this.QueueLayout();
+ // }
+ //}
+ }
+ }
+ }
+
+ internal void QueueAggregateFocusCheck (bool checkForFocus = true)
+ {
+#if DEBUG
+ if (SettingAggregateFocus) {
+ Debug.Fail ("WpfTextView.SettingAggregateFocus");
+ }
+#endif
+
+ if (!isClosed) {
+ bool newHasAggregateFocus = ((IdeApp.Workbench.ActiveDocument?.Editor?.Implementation as MonoDevelop.SourceEditor.SourceEditorView)?.TextEditor == this);
+ if (newHasAggregateFocus != hasAggregateFocus) {
+ hasAggregateFocus = newHasAggregateFocus;
+
+ if (hasAggregateFocus) {
+ //Got focus so make sure that the view that had focus (which wasn't us since we didn't have focus before) raises its
+ //lost focus event before we raise our got focus event. This will potentially do bad things if someone changes focus
+ //if the lost aggregate focus handler.
+ Debug.Assert (ViewWithAggregateFocus != this);
+ if (ViewWithAggregateFocus != null) {
+ ViewWithAggregateFocus.QueueAggregateFocusCheck (checkForFocus: false);
+ }
+ Debug.Assert (ViewWithAggregateFocus == null);
+ ViewWithAggregateFocus = this;
+ } else {
+ //Lost focus (which means we were the view with focus).
+ Debug.Assert (ViewWithAggregateFocus == this);
+ ViewWithAggregateFocus = null;
+ }
+
+ EventHandler handler = hasAggregateFocus ? this.GotAggregateFocus : this.LostAggregateFocus;
+
+#if DEBUG
+ try {
+ SettingAggregateFocus = true;
+#endif
+ factoryService.GuardedOperations.RaiseEvent (this, handler);
+#if DEBUG
+ } finally {
+ SettingAggregateFocus = false;
+ }
+#endif
+ }
+ }
+ }
+
+ public IGuardedOperations GuardedOperations;
+ internal SpaceReservationStack _spaceReservationStack;
+
+ public ISpaceReservationManager GetSpaceReservationManager (string name)
+ {
+ if (name == null)
+ throw new ArgumentNullException ("name");
+
+ return _spaceReservationStack.GetOrCreateManager (name);
+ }
+
+ internal TextEditorFactoryService ComponentContext {
+ get { return factoryService; }
+ }
+
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MonoTextEditor.IViewScroller.cs b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MonoTextEditor.IViewScroller.cs
new file mode 100644
index 0000000000..480d9b26a5
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MonoTextEditor.IViewScroller.cs
@@ -0,0 +1,109 @@
+//
+// MonoTextEditor.IViewScroller.cs
+//
+// Author:
+// Mike Krüger <mikkrg@microsoft.com>
+//
+// Copyright (c) 2018 Microsoft Corporation. All rights reserved.
+//
+// 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 System.Text;
+using System.Threading.Tasks;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+
+namespace Mono.TextEditor
+{
+ partial class MonoTextEditor : IViewScroller
+ {
+ void IViewScroller.EnsureSpanVisible(SnapshotSpan span)
+ {
+ ((IViewScroller)this).EnsureSpanVisible (new VirtualSnapshotSpan (span), EnsureSpanVisibleOptions.None);
+ }
+
+ void IViewScroller.EnsureSpanVisible(SnapshotSpan span, EnsureSpanVisibleOptions options)
+ {
+ ((IViewScroller)this).EnsureSpanVisible (new VirtualSnapshotSpan (span), options);
+ }
+
+ void IViewScroller.EnsureSpanVisible (VirtualSnapshotSpan span, EnsureSpanVisibleOptions options)
+ {
+ // If the textview is closed, this should be a no-op
+ if (!IsClosed) {
+ if ((options & ~(EnsureSpanVisibleOptions.ShowStart | EnsureSpanVisibleOptions.MinimumScroll | EnsureSpanVisibleOptions.AlwaysCenter)) != 0x00)
+ throw new ArgumentOutOfRangeException ("options");
+
+ //It is possible that this call is a result of an action that was defered until the view was loaded (& if so, it is possible that the
+ //snapshot changed inbetween).
+ span = span.TranslateTo (TextSnapshot);
+
+ // TODO: handle the various options for scrolling
+ ScrollTo (span.Start.Position);
+ }
+ }
+
+ void IViewScroller.ScrollViewportHorizontallyByPixels (double distanceToScroll)
+ {
+ HAdjustment.Value += distanceToScroll;
+ }
+
+ void IViewScroller.ScrollViewportVerticallyByLine (ScrollDirection direction)
+ {
+ ((IViewScroller)this).ScrollViewportVerticallyByLines (direction, 1);
+ }
+
+ void IViewScroller.ScrollViewportVerticallyByLines (ScrollDirection direction, int count)
+ {
+ switch (direction) {
+ case ScrollDirection.Up:
+ VAdjustment.Value -= LineHeight * count;
+ break;
+ case ScrollDirection.Down:
+ VAdjustment.Value += LineHeight * count;
+ break;
+ }
+ }
+
+ bool IViewScroller.ScrollViewportVerticallyByPage (ScrollDirection direction)
+ {
+ switch (direction) {
+ case ScrollDirection.Up:
+ if (VAdjustment.Value == 0)
+ return false;
+ VAdjustment.Value -= VAdjustment.PageSize;
+ return true;
+ case ScrollDirection.Down:
+ if (VAdjustment.Value + VAdjustment.PageSize > VAdjustment.Upper)
+ return false;
+ VAdjustment.Value += VAdjustment.PageSize;
+ return true;
+ }
+ return false;
+ }
+
+ void IViewScroller.ScrollViewportVerticallyByPixels(double distanceToScroll)
+ {
+ VAdjustment.Value += distanceToScroll;
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MonoTextEditor.cs b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MonoTextEditor.cs
index 162cc2814f..66c4cafa34 100644
--- a/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MonoTextEditor.cs
+++ b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MonoTextEditor.cs
@@ -46,7 +46,7 @@ namespace Mono.TextEditor
{
[System.ComponentModel.Category("Mono.TextEditor")]
[System.ComponentModel.ToolboxItem(true)]
- class MonoTextEditor : Container
+ partial class MonoTextEditor : Container
{
readonly TextArea textArea;
@@ -912,8 +912,7 @@ namespace Mono.TextEditor
} catch (Exception e) {
if (Debugger.IsAttached)
Debugger.Break ();
- //TODO: we should really find a way to log this properly
- Console.WriteLine ("Error while executing " + action + " :" + e);
+ LoggingService.LogError ("Error while executing " + action, e);
}
}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/TextArea.cs b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/TextArea.cs
index 10a9f4bb61..bb24a36718 100644
--- a/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/TextArea.cs
+++ b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/TextArea.cs
@@ -53,7 +53,7 @@ using MonoDevelop.Ide.Editor.Highlighting;
namespace Mono.TextEditor
{
- class TextArea : Container, ITextEditorDataProvider
+ partial class TextArea : Container, ITextEditorDataProvider
{
TextEditorData textEditorData;
@@ -235,7 +235,7 @@ namespace Mono.TextEditor
value = System.Math.Round (value);
this.textEditorData.VAdjustment.Value = value;
}
- if (isMouseTrapped)
+ if (IsMouseTrapped)
FireMotionEvent (mx + textViewMargin.XOffset, my, lastState);
double delta = value - this.oldVadjustment;
@@ -1497,17 +1497,17 @@ namespace Mono.TextEditor
customText = null;
}
#endregion
- bool isMouseTrapped = false;
+ internal bool IsMouseTrapped { get; set; } = false;
protected override bool OnEnterNotifyEvent (EventCrossing evnt)
{
- isMouseTrapped = true;
+ IsMouseTrapped = true;
return base.OnEnterNotifyEvent (evnt);
}
protected override bool OnLeaveNotifyEvent (Gdk.EventCrossing e)
{
- isMouseTrapped = false;
+ IsMouseTrapped = false;
if (tipWindow != null && currentTooltipProvider != null) {
if (!currentTooltipProvider.IsInteractive (textEditorData.Parent, tipWindow))
DelayedHideTooltip ();
@@ -1831,7 +1831,7 @@ namespace Mono.TextEditor
Options.ZoomOut ();
this.QueueDraw ();
- if (isMouseTrapped)
+ if (IsMouseTrapped)
FireMotionEvent (mx + textViewMargin.XOffset, my, lastState);
return true;
}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor.csproj b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor.csproj
index b42d4007d5..31ce9d4e2e 100644
--- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor.csproj
+++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor.csproj
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<Import Project="..\..\..\MonoDevelop.props" />
<Import Project="$(ReferencesVSEditor)" />
@@ -123,6 +123,16 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
+ <Reference Include="Microsoft.VisualStudio.Threading, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\..\build\bin\Microsoft.VisualStudio.Threading.dll</HintPath>
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Text.Implementation">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\..\build\bin\Microsoft.VisualStudio.Text.Implementation.dll</HintPath>
+ <Private>False</Private>
+ </Reference>
<Reference Include="System" />
<Reference Include="System.Xml" />
<Reference Include="Mono.Posix" />
@@ -175,6 +185,11 @@
</None>
<Compile Include="AssemblyInfo.cs" />
<Compile Include="gtk-gui\generated.cs" />
+ <Compile Include="Mono.TextEditor\Gui\MdTextViewLineCollection.MdTextViewLine.cs" />
+ <Compile Include="Mono.TextEditor\Gui\MdTextViewLineCollection.cs" />
+ <Compile Include="Mono.TextEditor\Gui\MonoTextEditor.IViewScroller.cs" />
+ <Compile Include="Mono.TextEditor\Gui\MonoTextEditor.ITextView.cs" />
+ <Compile Include="Mono.TextEditor\Gui\MonoTextEditor.ITextView.MouseHover.cs" />
<Compile Include="MonoDevelop.SourceEditor\EditActions.cs" />
<Compile Include="MonoDevelop.SourceEditor\ExtensibleTextEditor.cs" />
<Compile Include="MonoDevelop.SourceEditor\LanguageItemWindow.cs" />
@@ -272,21 +287,76 @@
<Compile Include="Mono.TextEditor.Theatrics\SingleActorStage.cs" />
<Compile Include="Mono.TextEditor.Theatrics\SmartScrolledWindow.cs" />
<Compile Include="Mono.TextEditor.Theatrics\Stage.cs" />
+ <None Include="packages.config" />
+ <Compile Include="MonoDevelop.SourceEditor\TextMarker\LineSeparatorMarker.cs" />
+ <Compile Include="MonoDevelop.SourceEditor.QuickTasks\QuickTaskOverviewMode.IndicatorDrawingCache.cs" />
+ <Compile Include="MonoDevelop.SourceEditor.QuickTasks\QuickTaskOverviewMode.IdleUpdater.cs" />
+ <Compile Include="MonoDevelop.SourceEditor\SourceEditorView.IMonoDevelopEditorOperations.cs" />
<Compile Include="VSEditor\ConnectionManager.cs" />
+ <Compile Include="VSEditor\IMdTextView.cs" />
<Compile Include="VSEditor\ITagBasedSyntaxHighlightingFactory.cs" />
+ <Compile Include="VSEditor\MDUtils.cs" />
<Compile Include="VSEditor\SmartIndentationService.cs" />
<Compile Include="VSEditor\TagBasedSyntaxHighlighting.cs" />
- <Compile Include="VSEditor\TextCaret.cs" />
<Compile Include="VSEditor\TextEditorFactoryService.cs" />
<Compile Include="VSEditor\TextSelection.cs" />
- <Compile Include="VSEditor\TextView.cs" />
<Compile Include="VSEditor\TextViewRoleSet.cs" />
<Compile Include="VSEditor\VacuousTextDataModel.cs" />
- <None Include="packages.config" />
- <Compile Include="MonoDevelop.SourceEditor\TextMarker\LineSeparatorMarker.cs" />
- <Compile Include="MonoDevelop.SourceEditor.QuickTasks\QuickTaskOverviewMode.IndicatorDrawingCache.cs" />
- <Compile Include="MonoDevelop.SourceEditor.QuickTasks\QuickTaskOverviewMode.IdleUpdater.cs" />
<Compile Include="MonoDevelop.SourceEditor\TextEditorKeyPressTimings.cs" />
+ <Compile Include="VSEditor\FakeWpf\Geometry.cs" />
+ <Compile Include="VSEditor\FakeWpf\Mouse.cs" />
+ <Compile Include="VSEditor\FakeWpf\SimulatingExtensions.cs" />
+ <Compile Include="VSEditor\Language\Def\Intellisense\IPopupIntellisensePresenter.cs" />
+ <Compile Include="VSEditor\Language\Impl\Helpers.cs" />
+ <Compile Include="VSEditor\Language\Impl\Intellisense\BaseIntellisenseSession.cs" />
+ <Compile Include="VSEditor\Language\Impl\Intellisense\CurrentLineSpaceReservationAgent.cs" />
+ <Compile Include="VSEditor\Language\Impl\Intellisense\IMultiSessionIntellisensePresenter.cs" />
+ <Compile Include="VSEditor\Language\Impl\Intellisense\IntellisenseManager.cs" />
+ <Compile Include="VSEditor\Language\Impl\Intellisense\IntellisenseSession.cs" />
+ <Compile Include="VSEditor\Language\Impl\Intellisense\IntellisenseSessionStack.cs" />
+ <Compile Include="VSEditor\Language\Impl\Intellisense\IntellisenseSessionStackMapService.cs" />
+ <Compile Include="VSEditor\Language\Impl\Intellisense\IntellisenseSpaceReservationManagers.cs" />
+ <Compile Include="VSEditor\Language\Impl\Intellisense\MultiSessionIntellisensePresenterProvider.cs" />
+ <Compile Include="VSEditor\Language\Impl\Intellisense\SignatureHelp\DefaultSignatureHelpPresenter.cs" />
+ <Compile Include="VSEditor\Language\Impl\Intellisense\SignatureHelp\DefaultSignatureHelpPresenterProvider.cs" />
+ <Compile Include="VSEditor\Language\Impl\Intellisense\SignatureHelp\DefaultSignatureHelpPresenterSurfaceElement.cs" />
+ <Compile Include="VSEditor\Language\Impl\Intellisense\SignatureHelp\SignatureHelpBroker.cs" />
+ <Compile Include="VSEditor\Language\Impl\Intellisense\SignatureHelp\SignatureHelpParameterBoldingClassfier.cs" />
+ <Compile Include="VSEditor\Language\Impl\Intellisense\SignatureHelp\SignatureHelpSession.cs" />
+ <Compile Include="VSEditor\Language\Impl\Intellisense\SignatureHelp\SignatureHelpSessionView.cs" />
+ <Compile Include="VSEditor\Language\Impl\QuickInfo\AsyncQuickInfoBroker.cs" />
+ <Compile Include="VSEditor\Language\Impl\QuickInfo\AsyncQuickInfoSession.cs" />
+ <Compile Include="VSEditor\Language\Impl\QuickInfo\QuickInfoController.cs" />
+ <Compile Include="VSEditor\Language\Impl\QuickInfo\QuickInfoTextViewCreationListener.cs" />
+ <Compile Include="VSEditor\Language\Impl\QuickInfo\SquiggleQuickInfoSource.cs" />
+ <Compile Include="VSEditor\Language\Impl\QuickInfo\SquiggleQuickInfoSourceProvider.cs" />
+ <Compile Include="VSEditor\Language\Util\LanguageUtil\IntellisenseSourceCache.cs" />
+ <Compile Include="VSEditor\Language\Util\LanguageUtil\IntellisenseUtilities.cs" />
+ <Compile Include="VSEditor\Text\Def\TextUIWpf\Adornments\ToolTipPresenterStyle.cs" />
+ <Compile Include="VSEditor\Text\Def\TextUIWpf\Editor\ISpaceReservationAgent.cs" />
+ <Compile Include="VSEditor\Text\Def\TextUIWpf\Editor\ISpaceReservationManager.cs" />
+ <Compile Include="VSEditor\Text\Def\TextUIWpf\Editor\SpaceReservationAgentChangedEventArgs.cs" />
+ <Compile Include="VSEditor\Text\Def\TextUIWpf\Editor\SpaceReservationManagerDefinition.cs" />
+ <Compile Include="VSEditor\Text\Impl\ToolTipAdornment\GuardedToolTipPresenter.cs" />
+ <Compile Include="VSEditor\Text\Impl\ToolTipAdornment\IViewElementFactoryMetadata.cs" />
+ <Compile Include="VSEditor\Text\Impl\ToolTipAdornment\ToolTipService.cs" />
+ <Compile Include="VSEditor\Text\Impl\ToolTipAdornment\ViewElementFactoryService.cs" />
+ <Compile Include="VSEditor\Text\Impl\WpfToolTipAdornment\BaseWpfToolTipPresenter.cs" />
+ <Compile Include="VSEditor\Text\Impl\WpfToolTipAdornment\DefaultToolTipPresenterStyle.cs" />
+ <Compile Include="VSEditor\Text\Impl\WpfToolTipAdornment\MouseTrackingWpfToolTipPresenter.cs" />
+ <Compile Include="VSEditor\Text\Impl\WpfToolTipAdornment\SpanTrackingWpfToolTipPresenter.cs" />
+ <Compile Include="VSEditor\Text\Impl\WpfToolTipAdornment\ToolTipStyleFactory.cs" />
+ <Compile Include="VSEditor\Text\Impl\WpfToolTipAdornment\WpfToolTipPresenterProvider.cs" />
+ <Compile Include="VSEditor\Text\Impl\WpfToolTipAdornment\Legacy\ToolTipProvider.cs" />
+ <Compile Include="VSEditor\Text\Impl\WpfToolTipAdornment\Legacy\ToolTipProviderFactory.cs" />
+ <Compile Include="VSEditor\Text\Impl\WpfToolTipAdornment\ViewElementFactories\WpfClassifiedTextElementViewElementFactory.cs" />
+ <Compile Include="VSEditor\Text\Impl\WpfToolTipAdornment\ViewElementFactories\WpfContainerElementViewElementFactory.cs" />
+ <Compile Include="VSEditor\Text\Impl\WpfToolTipAdornment\ViewElementFactories\WpfImageElementViewElementFactory.cs" />
+ <Compile Include="VSEditor\Text\Impl\WpfView\PopupAgent.cs" />
+ <Compile Include="VSEditor\Text\Impl\WpfView\SpaceReservationManager.cs" />
+ <Compile Include="VSEditor\Text\Impl\WpfView\SpaceReservationStack.cs" />
+ <Compile Include="VSEditor\Text\Util\TextDataUtil\MappingHelper.cs" />
+ <Compile Include="VSEditor\VisualStudio\Impl\ViewAdapter\TipManager.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="gtk-gui\gui.stetic">
@@ -540,13 +610,5 @@
</ItemGroup>
<Import Project="..\..\core\Mono.TextEditor.Shared\Mono.TextEditor.Shared.projitems" Label="Shared" Condition="Exists('..\..\core\Mono.TextEditor.Shared\Mono.TextEditor.Shared.projitems')" />
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
- <ItemGroup>
- <Folder Include="MonoDevelop.SourceEditor\" />
- <Folder Include="MonoDevelop.SourceEditor.QuickTasks\" />
- <Folder Include="MonoDevelop.SourceEditor.Wrappers\" />
- <Folder Include="MonoDevelop.SourceEditor\TextMarker\" />
- <Folder Include="Mono.TextEditor\" />
- <Folder Include="Mono.TextEditor.PopupWindow\" />
- <Folder Include="Mono.TextEditor.Theatrics\" />
- </ItemGroup>
+ <ItemGroup />
</Project> \ No newline at end of file
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/EditorFactory.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/EditorFactory.cs
index 38fd7ada85..d0b2e80407 100644
--- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/EditorFactory.cs
+++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/EditorFactory.cs
@@ -49,20 +49,16 @@ namespace MonoDevelop.SourceEditor
ITextDocument ITextEditorFactory.CreateNewDocument (MonoDevelop.Core.Text.ITextSource textSource, string fileName, string mimeType)
{
- return new TextDocument (textSource.Text) {
+ return new TextDocument (textSource.Text, fileName, mimeType) {
Encoding = textSource.Encoding,
- MimeType = mimeType,
- FileName = fileName
};
}
IReadonlyTextDocument ITextEditorFactory.CreateNewReadonlyDocument (MonoDevelop.Core.Text.ITextSource textSource, string fileName, string mimeType)
{
- return new TextDocument (textSource.Text) {
+ return new TextDocument (textSource.Text, fileName, mimeType) {
Encoding = textSource.Encoding,
IsReadOnly = true,
- MimeType = mimeType,
- FileName = fileName
};
}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorView.IMonoDevelopEditorOperations.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorView.IMonoDevelopEditorOperations.cs
new file mode 100644
index 0000000000..35b08fd1db
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorView.IMonoDevelopEditorOperations.cs
@@ -0,0 +1,642 @@
+//
+// SourceEditorView.IEditorOperations.cs
+//
+// Author:
+// Mike Krüger <mikkrg@microsoft.com>
+//
+// Copyright (c) 2018 Microsoft Corporation. All rights reserved.
+//
+// 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.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Formatting;
+using Microsoft.VisualStudio.Text.Operations;
+using Mono.TextEditor;
+using MonoDevelop.Ide.Editor;
+
+namespace MonoDevelop.SourceEditor
+{
+ partial class SourceEditorView : IMonoDevelopEditorOperations
+ {
+ bool IEditorOperations.CanPaste => this.EnablePaste;
+
+ bool IEditorOperations.CanDelete => this.EnableDelete;
+
+ bool IEditorOperations.CanCut => this.EnableCut;
+
+ public ITextView TextView { get; set; }
+
+ IEditorOptions IEditorOperations.Options => throw new NotImplementedException ();
+
+ ITrackingSpan IEditorOperations.ProvisionalCompositionSpan => throw new NotImplementedException ();
+
+ string IEditorOperations.SelectedText => TextEditor.SelectedText;
+
+ void IEditorOperations.MoveLineDown (bool extendSelection)
+ {
+ if (extendSelection) {
+ SelectionActions.MoveDown (TextEditor.GetTextEditorData ());
+ } else {
+ CaretMoveActions.Down (TextEditor.GetTextEditorData ());
+ }
+ }
+
+ void IEditorOperations.MoveLineUp (bool extendSelection)
+ {
+ if (extendSelection) {
+ SelectionActions.MoveUp (TextEditor.GetTextEditorData ());
+ } else {
+ CaretMoveActions.Up (TextEditor.GetTextEditorData ());
+ }
+ }
+
+ void IEditorOperations.MoveToPreviousCharacter (bool extendSelection)
+ {
+ if (extendSelection) {
+ SelectionActions.MoveLeft (TextEditor.GetTextEditorData ());
+ } else {
+ CaretMoveActions.Left (TextEditor.GetTextEditorData ());
+ }
+ }
+
+ void IEditorOperations.MoveToNextCharacter (bool extendSelection)
+ {
+ if (extendSelection) {
+ SelectionActions.MoveLeft (TextEditor.GetTextEditorData ());
+ } else {
+ CaretMoveActions.Left (TextEditor.GetTextEditorData ());
+ }
+ }
+
+ void IEditorOperations.MoveToEndOfLine (bool extendSelection)
+ {
+ if (extendSelection) {
+ SelectionActions.MoveLineEnd (TextEditor.GetTextEditorData ());
+ } else {
+ CaretMoveActions.LineEnd (TextEditor.GetTextEditorData ());
+ }
+ }
+
+ void IEditorOperations.MoveToStartOfLine (bool extendSelection)
+ {
+ if (extendSelection) {
+ TextEditor.RunAction (SelectionActions.MoveLineHome);
+ } else {
+ TextEditor.RunAction (CaretMoveActions.LineHome);
+ }
+ }
+
+ void IEditorOperations.MoveToStartOfDocument (bool extendSelection)
+ {
+ if (extendSelection) {
+ TextEditor.RunAction (SelectionActions.MoveToDocumentStart);
+ } else {
+ TextEditor.RunAction (CaretMoveActions.ToDocumentStart);
+ }
+ }
+
+ void IEditorOperations.MoveToEndOfDocument (bool extendSelection)
+ {
+ if (extendSelection) {
+ TextEditor.RunAction (SelectionActions.MoveToDocumentEnd);
+ } else {
+ TextEditor.RunAction (CaretMoveActions.ToDocumentEnd);
+ }
+ }
+
+ bool IEditorOperations.Backspace ()
+ {
+ TextEditor.RunAction (DeleteActions.Backspace);
+ return true;
+ }
+
+ bool IEditorOperations.CopySelection ()
+ {
+ TextEditor.RunAction (ClipboardActions.Copy);
+ return true;
+ }
+
+ bool IEditorOperations.CutSelection ()
+ {
+ ClipboardActions.Cut (TextEditor.GetTextEditorData ());
+ return true;
+ }
+
+ bool IEditorOperations.Paste ()
+ {
+ return ClipboardActions.PasteWithResult (TextEditor.GetTextEditorData ());
+ }
+
+ bool IEditorOperations.InsertNewLine ()
+ {
+ TextEditor.RunAction (MiscActions.InsertNewLine);
+ return true;
+ }
+
+ bool IEditorOperations.Tabify ()
+ {
+ TextEditor.RunAction (MiscActions.InsertTab);
+ return true;
+ }
+
+ bool IEditorOperations.Untabify ()
+ {
+ TextEditor.RunAction (MiscActions.RemoveTab);
+ return true;
+ }
+
+ bool IEditorOperations.DeleteWordToLeft ()
+ {
+ TextEditor.RunAction (DeleteActions.PreviousWord);
+ return true;
+ }
+
+ bool IEditorOperations.DeleteWordToRight ()
+ {
+ TextEditor.RunAction (DeleteActions.NextWord);
+ return true;
+ }
+
+ void IEditorOperations.ScrollLineCenter ()
+ {
+ TextEditor.RunAction (MiscActions.RecenterEditor);
+ }
+
+
+ void IEditorOperations.MoveToNextWord (bool extendSelection)
+ {
+ if (extendSelection) {
+ TextEditor.RunAction (SelectionActions.MoveNextWord);
+ } else {
+ TextEditor.RunAction (CaretMoveActions.NextWord);
+ }
+ }
+
+ void IEditorOperations.MoveToPreviousWord (bool extendSelection)
+ {
+ if (extendSelection) {
+ TextEditor.RunAction (SelectionActions.MovePreviousWord);
+ } else {
+ TextEditor.RunAction (CaretMoveActions.PreviousWord);
+ }
+ }
+
+ void IEditorOperations.PageUp (bool extendSelection)
+ {
+ if (extendSelection) {
+ TextEditor.RunAction (SelectionActions.MovePageUp);
+ } else {
+ TextEditor.RunAction (CaretMoveActions.PageUp);
+ }
+ }
+
+ void IEditorOperations.PageDown (bool extendSelection)
+ {
+ if (extendSelection) {
+ TextEditor.RunAction (SelectionActions.MovePageDown);
+ } else {
+ TextEditor.RunAction (CaretMoveActions.PageDown);
+ }
+ }
+
+ bool IEditorOperations.DeleteFullLine ()
+ {
+ TextEditor.RunAction (DeleteActions.CaretLine);
+ return true;
+ }
+
+ bool IEditorOperations.DeleteToEndOfLine ()
+ {
+ TextEditor.RunAction (DeleteActions.CaretLineToEnd);
+ return true;
+ }
+
+ void IEditorOperations.ScrollLineTop ()
+ {
+ TextEditor.RunAction (ScrollActions.Up);
+ }
+
+ void IEditorOperations.ScrollLineBottom ()
+ {
+ TextEditor.RunAction (ScrollActions.Down);
+ }
+
+ void IEditorOperations.ScrollPageUp ()
+ {
+ TextEditor.RunAction (ScrollActions.PageUp);
+ }
+
+ void IEditorOperations.ScrollPageDown ()
+ {
+ TextEditor.RunAction (ScrollActions.PageDown);
+ }
+
+ bool IEditorOperations.Indent ()
+ {
+ if (widget.TextEditor.IsSomethingSelected) {
+ MiscActions.IndentSelection (widget.TextEditor.GetTextEditorData ());
+ } else {
+ int offset = widget.TextEditor.LocationToOffset (widget.TextEditor.Caret.Line, 1);
+ widget.TextEditor.Insert (offset, widget.TextEditor.Options.IndentationString);
+ }
+ return true;
+ }
+
+ bool IEditorOperations.Unindent ()
+ {
+ MiscActions.RemoveTab (widget.TextEditor.GetTextEditorData ());
+ return true;
+ }
+
+ void IEditorOperations.SelectAndMoveCaret (VirtualSnapshotPoint anchorPoint, VirtualSnapshotPoint activePoint)
+ {
+ throw new NotImplementedException ();
+ }
+
+ void IEditorOperations.SelectAndMoveCaret (VirtualSnapshotPoint anchorPoint, VirtualSnapshotPoint activePoint, TextSelectionMode selectionMode)
+ {
+ throw new NotImplementedException ();
+ }
+
+ void IEditorOperations.SelectAndMoveCaret (VirtualSnapshotPoint anchorPoint, VirtualSnapshotPoint activePoint, TextSelectionMode selectionMode, EnsureSpanVisibleOptions? scrollOptions)
+ {
+ throw new NotImplementedException ();
+ }
+
+ void IEditorOperations.MoveToHome (bool extendSelection)
+ {
+ if (extendSelection) {
+ TextEditor.RunAction (SelectionActions.MoveLineHome);
+ } else {
+ TextEditor.RunAction (CaretMoveActions.LineHome);
+ }
+ }
+
+ void IEditorOperations.GotoLine (int lineNumber)
+ {
+ TextEditor.Caret.Line = lineNumber;
+ TextEditor.ScrollToCaret ();
+ }
+
+ void IEditorOperations.MoveCurrentLineToTop ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ void IEditorOperations.MoveCurrentLineToBottom ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ void IEditorOperations.MoveToStartOfLineAfterWhiteSpace (bool extendSelection)
+ {
+ throw new NotImplementedException ();
+ }
+
+ void IEditorOperations.MoveToStartOfNextLineAfterWhiteSpace (bool extendSelection)
+ {
+ throw new NotImplementedException ();
+ }
+
+ void IEditorOperations.MoveToStartOfPreviousLineAfterWhiteSpace (bool extendSelection)
+ {
+ throw new NotImplementedException ();
+ }
+
+ void IEditorOperations.MoveToLastNonWhiteSpaceCharacter (bool extendSelection)
+ {
+ throw new NotImplementedException ();
+ }
+
+ void IEditorOperations.MoveToTopOfView (bool extendSelection)
+ {
+ throw new NotImplementedException ();
+ }
+
+ void IEditorOperations.MoveToBottomOfView (bool extendSelection)
+ {
+ throw new NotImplementedException ();
+ }
+
+ void IEditorOperations.SwapCaretAndAnchor ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ bool IEditorOperations.DeleteToBeginningOfLine ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ bool IEditorOperations.DeleteBlankLines ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ bool IEditorOperations.DeleteHorizontalWhiteSpace ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ bool IEditorOperations.OpenLineAbove ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ bool IEditorOperations.OpenLineBelow ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ bool IEditorOperations.IncreaseLineIndent ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ bool IEditorOperations.DecreaseLineIndent ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ bool IEditorOperations.InsertText (string text)
+ {
+ TextEditor.InsertAtCaret (text);
+ return true;
+ }
+
+ bool IEditorOperations.InsertTextAsBox (string text, out VirtualSnapshotPoint boxStart, out VirtualSnapshotPoint boxEnd)
+ {
+ throw new NotImplementedException ();
+ }
+
+ bool IEditorOperations.InsertProvisionalText (string text)
+ {
+ throw new NotImplementedException ();
+ }
+
+ bool IEditorOperations.Delete ()
+ {
+ TextEditor.RunAction (DeleteActions.Delete);
+ return true;
+ }
+
+ bool IEditorOperations.ReplaceSelection (string text)
+ {
+ throw new NotImplementedException ();
+ }
+
+ bool IEditorOperations.TransposeCharacter ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ bool IEditorOperations.TransposeLine ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ bool IEditorOperations.TransposeWord ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ bool IEditorOperations.MakeLowercase ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ bool IEditorOperations.MakeUppercase ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ bool IEditorOperations.ToggleCase ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ bool IEditorOperations.Capitalize ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ bool IEditorOperations.ReplaceText (Span replaceSpan, string text)
+ {
+ throw new NotImplementedException ();
+ }
+
+ int IEditorOperations.ReplaceAllMatches (string searchText, string replaceText, bool matchCase, bool matchWholeWord, bool useRegularExpressions)
+ {
+ throw new NotImplementedException ();
+ }
+
+ bool IEditorOperations.InsertFile (string filePath)
+ {
+ throw new NotImplementedException ();
+ }
+
+ bool IEditorOperations.ConvertSpacesToTabs ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ bool IEditorOperations.ConvertTabsToSpaces ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ bool IEditorOperations.NormalizeLineEndings (string replacement)
+ {
+ throw new NotImplementedException ();
+ }
+
+ void IEditorOperations.SelectCurrentWord ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ void IEditorOperations.SelectEnclosing ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ void IEditorOperations.SelectFirstChild ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ void IEditorOperations.SelectNextSibling (bool extendSelection)
+ {
+ throw new NotImplementedException ();
+ }
+
+ void IEditorOperations.SelectPreviousSibling (bool extendSelection)
+ {
+ throw new NotImplementedException ();
+ }
+
+ void IEditorOperations.SelectLine (ITextViewLine viewLine, bool extendSelection)
+ {
+ throw new NotImplementedException ();
+ }
+
+ void IEditorOperations.SelectAll ()
+ {
+ TextEditor.RunAction (SelectionActions.SelectAll);
+ }
+
+ void IEditorOperations.ExtendSelection (int newEnd)
+ {
+ throw new NotImplementedException ();
+ }
+
+ void IEditorOperations.MoveCaret (ITextViewLine textLine, double horizontalOffset, bool extendSelection)
+ {
+ throw new NotImplementedException ();
+ }
+
+ void IEditorOperations.ResetSelection ()
+ {
+ TextEditor.RunAction (SelectionActions.ClearSelection);
+ }
+
+ bool IEditorOperations.CutFullLine ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ void IEditorOperations.ScrollUpAndMoveCaretIfNecessary ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ void IEditorOperations.ScrollDownAndMoveCaretIfNecessary ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ void IEditorOperations.ScrollColumnLeft ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ void IEditorOperations.ScrollColumnRight ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ void IEditorOperations.AddBeforeTextBufferChangePrimitive ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ void IEditorOperations.AddAfterTextBufferChangePrimitive ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ void IEditorOperations.ZoomIn ()
+ {
+ TextEditor.Options.ZoomIn ();
+ }
+
+ void IEditorOperations.ZoomOut ()
+ {
+ TextEditor.Options.ZoomOut ();
+ }
+
+ void IEditorOperations.ZoomTo (double zoomLevel)
+ {
+ TextEditor.Options.Zoom = zoomLevel;
+ }
+
+ string IEditorOperations.GetWhitespaceForVirtualSpace (VirtualSnapshotPoint point)
+ {
+ throw new NotImplementedException ();
+ }
+
+ #region IMonoDevelopEditorOperations members
+ void IMonoDevelopEditorOperations.SwitchCaretMode ()
+ {
+ TextEditor.RunAction (MiscActions.SwitchCaretMode);
+ }
+
+ void IMonoDevelopEditorOperations.DeletePreviousSubword ()
+ {
+ TextEditor.RunAction (DeleteActions.PreviousSubword);
+ }
+
+ void IMonoDevelopEditorOperations.DeleteNextSubword ()
+ {
+ TextEditor.RunAction (DeleteActions.NextSubword);
+ }
+
+ void IMonoDevelopEditorOperations.StartCaretPulseAnimation ()
+ {
+ TextEditor.StartCaretPulseAnimation ();
+ }
+
+ void IMonoDevelopEditorOperations.JoinLines ()
+ {
+ using (var undo = Document.OpenUndoGroup ()) {
+ TextEditor.RunAction (Mono.TextEditor.Vi.ViActions.Join);
+ }
+ }
+
+ void IMonoDevelopEditorOperations.MoveToNextSubWord ()
+ {
+ TextEditor.RunAction (SelectionActions.MoveNextSubword);
+ }
+
+ void IMonoDevelopEditorOperations.MoveToPrevSubWord ()
+ {
+ TextEditor.RunAction (SelectionActions.MovePreviousSubword);
+ }
+
+ void IMonoDevelopEditorOperations.ShowQuickInfo ()
+ {
+ widget.TextEditor.TextArea.ShowQuickInfo ();
+ }
+
+ void IMonoDevelopEditorOperations.MoveBlockUp ()
+ {
+ using (var undo = TextEditor.OpenUndoGroup ()) {
+ TextEditor.RunAction (MiscActions.MoveBlockUp);
+ CorrectIndenting ();
+ }
+ }
+
+ void IMonoDevelopEditorOperations.MoveBlockDown ()
+ {
+ using (var undo = TextEditor.OpenUndoGroup ()) {
+ TextEditor.RunAction (MiscActions.MoveBlockDown);
+ CorrectIndenting ();
+ }
+ }
+
+ void IMonoDevelopEditorOperations.ToggleBlockSelectionMode ()
+ {
+ TextEditor.SelectionMode = TextEditor.SelectionMode == MonoDevelop.Ide.Editor.SelectionMode.Normal ? MonoDevelop.Ide.Editor.SelectionMode.Block : MonoDevelop.Ide.Editor.SelectionMode.Normal;
+ TextEditor.QueueDraw ();
+ }
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorView.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorView.cs
index 4fca04c857..63c7f25158 100644
--- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorView.cs
+++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorView.cs
@@ -1,4 +1,4 @@
-// SourceEditorView.cs
+// SourceEditorView.cs
//
// Author:
// Mike Krüger <mkrueger@novell.com>
@@ -69,7 +69,7 @@ namespace MonoDevelop.SourceEditor
ICompletionWidget, ISplittable, IFoldable, IToolboxDynamicProvider,
ICustomFilteringToolboxConsumer, IZoomable, ITextEditorResolver, ITextEditorDataProvider,
ICodeTemplateHandler, ICodeTemplateContextProvider, IPrintable,
- ITextEditorImpl, IEditorActionHost, ITextMarkerFactory, IUndoHandler
+ ITextEditorImpl, ITextMarkerFactory, IUndoHandler
{
readonly SourceEditorWidget widget;
bool isDisposed = false;
@@ -267,13 +267,15 @@ namespace MonoDevelop.SourceEditor
public DocumentAndLoaded (string fileName, string mimeType) {
if (AutoSave.AutoSaveExists(fileName)) {
// Don't load the document now, let Load() handle it
- this.Document = new TextDocument();
- this.Document.MimeType = mimeType;
- this.Document.FileName = fileName;
+ this.Document = new TextDocument(string.Empty, fileName, mimeType);
this.Loaded = false;
} else {
- this.Document = new TextDocument(fileName, mimeType);
+ // HACK we really need to be told explicitly that the document is for an existing file (that we should load) or a new file (which will have the given name/mime type)
+ // so we don't need to use File.Exists().
+ this.Document = File.Exists(fileName)
+ ? new TextDocument(fileName, mimeType)
+ : new TextDocument(string.Empty, fileName, mimeType);
this.Loaded = true;
}
@@ -2918,7 +2920,7 @@ namespace MonoDevelop.SourceEditor
}
}
- IEditorActionHost ITextEditorImpl.Actions {
+ IMonoDevelopEditorOperations ITextEditorImpl.Actions {
get {
return this;
}
@@ -3237,236 +3239,6 @@ namespace MonoDevelop.SourceEditor
-
- #region IEditorActionHost implementation
-
- void IEditorActionHost.MoveCaretDown ()
- {
- CaretMoveActions.Down (TextEditor.GetTextEditorData ());
- }
-
- void IEditorActionHost.MoveCaretUp ()
- {
- CaretMoveActions.Up (TextEditor.GetTextEditorData ());
- }
-
- void IEditorActionHost.MoveCaretRight ()
- {
- CaretMoveActions.Right (TextEditor.GetTextEditorData ());
- }
-
- void IEditorActionHost.MoveCaretLeft ()
- {
- CaretMoveActions.Left (TextEditor.GetTextEditorData ());
- }
-
- void IEditorActionHost.MoveCaretToLineEnd ()
- {
- CaretMoveActions.LineEnd (TextEditor.GetTextEditorData ());
- }
-
- void IEditorActionHost.MoveCaretToLineStart ()
- {
- CaretMoveActions.LineHome (TextEditor.GetTextEditorData ());
- }
-
- void IEditorActionHost.MoveCaretToDocumentStart ()
- {
- CaretMoveActions.ToDocumentStart (TextEditor.GetTextEditorData ());
- }
-
- void IEditorActionHost.MoveCaretToDocumentEnd ()
- {
- CaretMoveActions.ToDocumentEnd (TextEditor.GetTextEditorData ());
- }
-
- void IEditorActionHost.Backspace ()
- {
- DeleteActions.Backspace (TextEditor.GetTextEditorData ());
- }
-
- void IEditorActionHost.ClipboardCopy ()
- {
- ClipboardActions.Copy (TextEditor.GetTextEditorData ());
- }
-
- void IEditorActionHost.ClipboardCut ()
- {
- ClipboardActions.Cut (TextEditor.GetTextEditorData ());
- }
-
- void IEditorActionHost.ClipboardPaste ()
- {
- ClipboardActions.Paste (TextEditor.GetTextEditorData ());
- }
-
- void IEditorActionHost.NewLine ()
- {
- MiscActions.InsertNewLine (TextEditor.GetTextEditorData ());
- }
-
- void IEditorActionHost.SwitchCaretMode ()
- {
- TextEditor.RunAction (MiscActions.SwitchCaretMode);
- }
-
- void IEditorActionHost.InsertTab ()
- {
- TextEditor.RunAction (MiscActions.InsertTab);
- }
-
- void IEditorActionHost.RemoveTab ()
- {
- TextEditor.RunAction (MiscActions.RemoveTab);
- }
-
- void IEditorActionHost.InsertNewLine ()
- {
- TextEditor.RunAction (MiscActions.InsertNewLine);
- }
-
- void IEditorActionHost.DeletePreviousWord ()
- {
- TextEditor.RunAction (DeleteActions.PreviousWord);
- }
-
- void IEditorActionHost.DeleteNextWord ()
- {
- TextEditor.RunAction (DeleteActions.NextWord);
- }
-
- void IEditorActionHost.DeletePreviousSubword ()
- {
- TextEditor.RunAction (DeleteActions.PreviousSubword);
- }
-
- void IEditorActionHost.DeleteNextSubword ()
- {
- TextEditor.RunAction (DeleteActions.NextSubword);
- }
-
- void IEditorActionHost.StartCaretPulseAnimation ()
- {
- TextEditor.StartCaretPulseAnimation ();
- }
-
- void IEditorActionHost.RecenterEditor ()
- {
- TextEditor.RunAction (MiscActions.RecenterEditor);
- }
-
- void IEditorActionHost.JoinLines ()
- {
- using (var undo = Document.OpenUndoGroup ()) {
- TextEditor.RunAction (Mono.TextEditor.Vi.ViActions.Join);
- }
- }
-
- void IEditorActionHost.MoveNextSubWord ()
- {
- TextEditor.RunAction (SelectionActions.MoveNextSubword);
- }
-
- void IEditorActionHost.MovePrevSubWord ()
- {
- TextEditor.RunAction (SelectionActions.MovePreviousSubword);
- }
-
- void IEditorActionHost.MoveNextWord ()
- {
- TextEditor.RunAction (CaretMoveActions.NextWord);
- }
-
- void IEditorActionHost.MovePrevWord ()
- {
- TextEditor.RunAction (CaretMoveActions.PreviousWord);
- }
-
- void IEditorActionHost.PageUp ()
- {
- TextEditor.RunAction (CaretMoveActions.PageUp);
- }
-
- void IEditorActionHost.PageDown ()
- {
- TextEditor.RunAction (CaretMoveActions.PageDown);
- }
-
- void IEditorActionHost.DeleteCurrentLine ()
- {
- TextEditor.RunAction (DeleteActions.CaretLine);
- }
-
- void IEditorActionHost.DeleteCurrentLineToEnd ()
- {
- TextEditor.RunAction (DeleteActions.CaretLineToEnd);
- }
-
- void IEditorActionHost.ScrollLineUp ()
- {
- TextEditor.RunAction (ScrollActions.Up);
- }
-
- void IEditorActionHost.ScrollLineDown ()
- {
- TextEditor.RunAction (ScrollActions.Down);
- }
-
- void IEditorActionHost.ScrollPageUp ()
- {
- TextEditor.RunAction (ScrollActions.PageUp);
- }
-
- void IEditorActionHost.ScrollPageDown ()
- {
- TextEditor.RunAction (ScrollActions.PageDown);
- }
-
- void IEditorActionHost.MoveBlockUp ()
- {
- using (var undo = TextEditor.OpenUndoGroup ()) {
- TextEditor.RunAction (MiscActions.MoveBlockUp);
- CorrectIndenting ();
- }
- }
-
- void IEditorActionHost.MoveBlockDown ()
- {
- using (var undo = TextEditor.OpenUndoGroup ()) {
- TextEditor.RunAction (MiscActions.MoveBlockDown);
- CorrectIndenting ();
- }
- }
-
- void IEditorActionHost.ToggleBlockSelectionMode ()
- {
- TextEditor.SelectionMode = TextEditor.SelectionMode == MonoDevelop.Ide.Editor.SelectionMode.Normal ? MonoDevelop.Ide.Editor.SelectionMode.Block : MonoDevelop.Ide.Editor.SelectionMode.Normal;
- TextEditor.QueueDraw ();
- }
-
- void IEditorActionHost.IndentSelection ()
- {
- if (widget.TextEditor.IsSomethingSelected) {
- MiscActions.IndentSelection (widget.TextEditor.GetTextEditorData ());
- } else {
- int offset = widget.TextEditor.LocationToOffset (widget.TextEditor.Caret.Line, 1);
- widget.TextEditor.Insert (offset, widget.TextEditor.Options.IndentationString);
- }
- }
-
- void IEditorActionHost.UnIndentSelection ()
- {
- MiscActions.RemoveTab (widget.TextEditor.GetTextEditorData ());
- }
-
- void IEditorActionHost.ShowQuickInfo ()
- {
- widget.TextEditor.TextArea.ShowQuickInfo ();
- }
-
- #endregion
-
-
#region ISegmentMarkerHost implementation
ITextSegmentMarker ITextMarkerFactory.CreateUsageMarker (MonoDevelop.Ide.Editor.TextEditor editor, Usage usage)
@@ -3508,11 +3280,22 @@ namespace MonoDevelop.SourceEditor
{
switch (effect) {
case TextSegmentMarkerEffect.DottedLine:
- case TextSegmentMarkerEffect.WavedLine:
- var result = new GenericUnderlineMarker (new TextSegment (offset, length), effect);
- if (color.HasValue)
- result.Color = color.Value;
- return result;
+ case TextSegmentMarkerEffect.WavedLine:
+ {
+ var result = new GenericUnderlineMarker (new TextSegment (offset, length), effect);
+ if (color.HasValue)
+ result.Color = color.Value;
+ return result;
+ }
+ case TextSegmentMarkerEffect.Underline:
+ {
+ var result = new GenericUnderlineMarker (new TextSegment (offset, length), effect);
+ if (color.HasValue)
+ result.Color = color.Value;
+ result.Wave = false;
+ return result;
+ }
+
case TextSegmentMarkerEffect.GrayOut:
return new GrayOutMarker (new TextSegment (offset, length));
default:
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/TextMarker/WavedLineMarker.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/TextMarker/WavedLineMarker.cs
index 843713c7a6..5db970acc4 100644
--- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/TextMarker/WavedLineMarker.cs
+++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/TextMarker/WavedLineMarker.cs
@@ -1,4 +1,4 @@
-//
+//
// WavedLineMarker.cs
//
// Author:
@@ -34,14 +34,12 @@ namespace MonoDevelop.SourceEditor
{
class GenericUnderlineMarker : UnderlineTextSegmentMarker, MonoDevelop.Ide.Editor.IGenericTextSegmentMarker
{
- HslColor color;
-
HslColor MonoDevelop.Ide.Editor.IGenericTextSegmentMarker.Color {
get {
- return color;
+ return Color;
}
set {
- color = value;
+ Color = value;
}
}
@@ -95,7 +93,7 @@ namespace MonoDevelop.SourceEditor
return;
double height = editor.LineHeight / 5;
- cr.SetSourceColor (color);
+ cr.SetSourceColor (Color);
if (effect == MonoDevelop.Ide.Editor.TextSegmentMarkerEffect.WavedLine) {
Pango.CairoHelper.ShowErrorUnderline (cr, drawFrom, y + editor.LineHeight - height, drawTo - drawFrom, height);
} else if (effect == MonoDevelop.Ide.Editor.TextSegmentMarkerEffect.DottedLine) {
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/FakeWpf/Geometry.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/FakeWpf/Geometry.cs
new file mode 100644
index 0000000000..cea8d67b83
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/FakeWpf/Geometry.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Xwt;
+
+namespace System.Windows.Media
+{
+ public abstract class Geometry
+ {
+ public static Geometry Empty { get; } = new EmptyGeometry ();
+ public abstract Rectangle Bounds { get; }
+
+ public abstract bool IsEmpty ();
+ }
+
+ class EmptyGeometry : Geometry
+ {
+ public override Rectangle Bounds => Rectangle.Zero;
+
+ public override bool IsEmpty ()
+ {
+ return true;
+ }
+ }
+
+ public class GeometryGroup : Geometry
+ {
+ public List<Geometry> Children { get; } = new List<Geometry> ();
+
+ public override Rectangle Bounds {
+ get {
+ Rectangle union = new Rectangle ();
+ foreach (var c in Children)
+ union = union.Union (c.Bounds);
+ return union;
+ }
+ }
+ public override bool IsEmpty ()
+ {
+ return !Children.Any (c => !c.IsEmpty ());
+ }
+ }
+
+ public class RectangleGeometry : Geometry
+ {
+ public Rectangle Rectangle;
+ public RectangleGeometry (Rectangle rectangle)
+ {
+ this.Rectangle = rectangle;
+ }
+
+ public override Rectangle Bounds => Rectangle;
+
+ public override bool IsEmpty ()
+ {
+ return Rectangle.IsEmpty;
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/FakeWpf/Mouse.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/FakeWpf/Mouse.cs
new file mode 100644
index 0000000000..d854ea3934
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/FakeWpf/Mouse.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Mono.TextEditor;
+using Xwt;
+
+namespace System.Windows.Input
+{
+ class Mouse
+ {
+ internal static Point GetPosition (Gtk.Widget widget)
+ {
+ widget.GetPointer (out int x, out int y);
+ return new Point (x, y);
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/FakeWpf/SimulatingExtensions.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/FakeWpf/SimulatingExtensions.cs
new file mode 100644
index 0000000000..42917e3d29
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/FakeWpf/SimulatingExtensions.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace System.Windows.Input
+{
+ static class MouseSimulatingExtensions
+ {
+ public static bool IsMouseOver (this Gtk.Widget widget)
+ {
+ widget.GetPointer (out int x, out int y);
+ return x >= 0 && y >= 0 && x <= widget.Allocation.Width && y <= widget.Allocation.Height;
+ }
+
+ public static bool IsMouseOver (this Xwt.Widget widget)
+ {
+ var mousePosition = Xwt.Desktop.MouseLocation;
+ return widget.ScreenBounds.Contains (mousePosition);
+ }
+
+ public static bool IsMouseOver (this Xwt.Window widget)
+ {
+ var mousePosition = Xwt.Desktop.MouseLocation;
+ return widget.ScreenBounds.Contains (mousePosition);
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/IMdTextView.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/IMdTextView.cs
new file mode 100644
index 0000000000..353f69933d
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/IMdTextView.cs
@@ -0,0 +1,23 @@
+using Microsoft.VisualStudio.Text.Editor;
+
+namespace Microsoft.VisualStudio.Text.Editor
+{
+ interface IMdTextView : ITextView
+ {
+ /// <summary>
+ /// Gets a named <see cref="ISpaceReservationManager"/>.
+ /// </summary>
+ /// <param name="name">The name of the manager.</param>
+ /// <returns>An instance of the manager in this view. Not null.</returns>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="name"/> is not registered via an <see cref="SpaceReservationManagerDefinition"/>.</exception>
+ /// <remarks>
+ /// <para>Managers must be exported using <see cref="SpaceReservationManagerDefinition"/> component parts.</para>
+ /// </remarks>
+ ISpaceReservationManager GetSpaceReservationManager (string name);
+
+ Mono.TextEditor.MonoTextEditor VisualElement
+ {
+ get;
+ }
+}
+} \ No newline at end of file
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Def/Intellisense/IPopupIntellisensePresenter.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Def/Intellisense/IPopupIntellisensePresenter.cs
new file mode 100644
index 0000000000..88ff524590
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Def/Intellisense/IPopupIntellisensePresenter.cs
@@ -0,0 +1,75 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Microsoft Corporation. All rights reserved.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+using System;
+using System.Windows;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Adornments;
+
+namespace Microsoft.VisualStudio.Language.Intellisense
+{
+ /// <summary>
+ /// Defines an IntelliSense presenter that is rendered as a popup within an
+ /// <see cref="Microsoft.VisualStudio.Text.Editor.ITextView"/>.
+ /// </summary>
+ public interface IPopupIntellisensePresenter : IIntellisensePresenter
+ {
+ /// <summary>
+ /// Gets the WPF <see cref="UIElement"/> that the presenter wants to be displayed inside a
+ /// <see cref="Microsoft.VisualStudio.Text.Editor.ITextView"/> popup.
+ /// </summary>
+ Xwt.Widget SurfaceElement { get; }
+
+ /// <summary>
+ /// Occurs when the WPF SurfaceElement is changed.
+ /// </summary>
+ event EventHandler SurfaceElementChanged;
+
+ /// <summary>
+ /// Gets the <see cref="ITrackingSpan"/> to which this presenter is related.
+ /// </summary>
+ /// <remarks>
+ /// This property is used to determine where to
+ /// place the <see cref="Microsoft.VisualStudio.Text.Editor.ITextView"/> popup inside of which the presenter's
+ /// SurfaceElement is hosted.
+ /// </remarks>
+ ITrackingSpan PresentationSpan { get; }
+
+ /// <summary>
+ /// Occurs when the PresentationSpan property changes.
+ /// </summary>
+ /// <remarks>
+ /// This is the way popup presenters signal that they should be moved.
+ /// </remarks>
+ event EventHandler PresentationSpanChanged;
+
+ /// <summary>
+ /// Gets a set of flags that determine the popup style.
+ /// </summary>
+ PopupStyles PopupStyles { get; }
+
+ /// <summary>
+ /// Occurs when the PopupStyles property changes.
+ /// </summary>
+ event EventHandler<ValueChangedEventArgs<PopupStyles>> PopupStylesChanged;
+
+ /// <summary>
+ /// Gets the name of the space reservation manager that should be used to create popups for this presenter.
+ /// </summary>
+ /// <remarks>
+ /// Space reservation
+ /// managers can be ordered, thus ensuring predictable popup placement.
+ /// </remarks>
+ string SpaceReservationManagerName { get; }
+
+ /// <summary>
+ /// Gets or sets the opacity of this popup presenter.
+ /// </summary>
+ /// <remarks>
+ /// The presenter should use this property to set the
+ /// opacity of its surface element and of any other text-obscuring UI elements it has provided.
+ /// </remarks>
+ double Opacity { get; set; }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Helpers.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Helpers.cs
new file mode 100644
index 0000000000..c747a2cee0
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Helpers.cs
@@ -0,0 +1,232 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Microsoft Corporation. All rights reserved.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.Linq;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Utilities;
+using Microsoft.VisualStudio.Utilities;
+using Microsoft.VisualStudio.Language.Utilities;
+
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
+{
+ internal static class Helpers
+ {
+ private static string GetAllTextDisplayedByCompletion(Completion c)
+ {
+ string displayText = c.DisplayText;
+ string suffixText = null;
+
+ var c4 = c as Completion4;
+ if (c4 != null)
+ {
+ suffixText = c4.Suffix;
+ }
+
+ return string.IsNullOrEmpty(suffixText) ? displayText : (displayText + suffixText);
+ }
+
+#if DEBUG
+ internal static void TrackObject(List<Lazy<IObjectTracker>> objectTrackers, string bucketName, object value)
+ {
+ foreach (Lazy<IObjectTracker> trackerExport in objectTrackers)
+ {
+ IObjectTracker objectTracker = trackerExport.Value;
+ if (objectTracker != null)
+ {
+ objectTracker.TrackObject(value, bucketName);
+ }
+ }
+ }
+#endif
+
+ internal static ITrackingSpan GetEncapsulatingSpan(ITextView textView, ITrackingSpan span1, ITrackingSpan span2)
+ {
+ // If either of the spans is null, return the other one. If they're both null, we'll end up returning null
+ // (as it should be).
+ if (span1 == null)
+ {
+ var spans = textView.BufferGraph.MapUpToBuffer
+ (
+ span2.GetSpan(span2.TextBuffer.CurrentSnapshot),
+ span2.TrackingMode,
+ textView.TextBuffer
+ );
+ Debug.Assert(spans.Count == 1);
+ return spans[0].Snapshot.CreateTrackingSpan(spans[0], span2.TrackingMode);
+ }
+ if (span2 == null)
+ {
+ var spans = textView.BufferGraph.MapUpToBuffer
+ (
+ span1.GetSpan(span2.TextBuffer.CurrentSnapshot),
+ span1.TrackingMode,
+ textView.TextBuffer
+ );
+ Debug.Assert(spans.Count == 1);
+ return spans[0].Snapshot.CreateTrackingSpan(spans[0], span1.TrackingMode);
+ }
+
+ List<SnapshotSpan> surfaceSpans = new List<SnapshotSpan>();
+ surfaceSpans.AddRange
+ (
+ textView.BufferGraph.MapUpToBuffer
+ (
+ span1.GetSpan(span1.TextBuffer.CurrentSnapshot),
+ span1.TrackingMode,
+ textView.TextBuffer
+ )
+ );
+ surfaceSpans.AddRange
+ (
+ textView.BufferGraph.MapUpToBuffer
+ (
+ span2.GetSpan(span2.TextBuffer.CurrentSnapshot),
+ span2.TrackingMode,
+ textView.TextBuffer
+ )
+ );
+
+ ITrackingSpan encapsulatingSpan = null;
+ foreach (var span in surfaceSpans)
+ {
+ encapsulatingSpan = Helpers.GetEncapsulatingSpan
+ (
+ encapsulatingSpan,
+ span.Snapshot.CreateTrackingSpan(span, span1.TrackingMode)
+ );
+ }
+
+ return encapsulatingSpan;
+ }
+
+ internal static ITrackingSpan GetEncapsulatingSpan(ITrackingSpan span1, ITrackingSpan span2)
+ {
+ // If either of the spans is null, return the other one. If they're both null, we'll end up returning null
+ // (as it should be).
+ if (span1 == null)
+ {
+ return span2;
+ }
+ if (span2 == null)
+ {
+ return span1;
+ }
+
+ // In the case where the two spans exist over different buffers, just use the first span.
+ if (span1.TextBuffer != span2.TextBuffer)
+ {
+ return span1;
+ }
+
+ ITextSnapshot snapshot = span1.TextBuffer.CurrentSnapshot;
+ SnapshotSpan snapSpan1 = span1.GetSpan(snapshot);
+ SnapshotSpan snapSpan2 = span2.GetSpan(snapshot);
+
+ return snapshot.CreateTrackingSpan
+ (Span.FromBounds
+ (Math.Min(snapSpan1.Start.Position, snapSpan2.Start.Position),
+ Math.Max(snapSpan1.End.Position, snapSpan2.End.Position)),
+ span1.TrackingMode);
+ }
+
+ internal static IEnumerable<TStyle> GetMatchingPresenterStyles<TSession, TStyle>
+ (TSession session,
+ IList<Lazy<TStyle, IOrderableContentTypeMetadata>> orderedPresenterStyles,
+ GuardedOperations guardedOperations)
+ where TSession : IIntellisenseSession
+ {
+ List<TStyle> styles = new List<TStyle>();
+
+ ITextView textView = session.TextView;
+ SnapshotPoint? surfaceBufferPoint = session.GetTriggerPoint(textView.TextSnapshot);
+ if (surfaceBufferPoint == null)
+ {
+ return styles;
+ }
+
+ var buffers = Helpers.GetBuffersForTriggerPoint(session).ToList();
+
+ foreach (var styleExport in orderedPresenterStyles)
+ {
+ bool usedThisProviderAlready = false;
+ foreach (var buffer in buffers)
+ {
+ foreach (string contentType in styleExport.Metadata.ContentTypes)
+ {
+ if (buffer.ContentType.IsOfType(contentType))
+ {
+ var style = guardedOperations.InstantiateExtension(styleExport, styleExport);
+ if (!Object.Equals(style, default(TStyle)))
+ {
+ styles.Add(style);
+ }
+ usedThisProviderAlready = true;
+ break;
+ }
+ }
+ if (usedThisProviderAlready)
+ {
+ break;
+ }
+ }
+ }
+
+ return styles;
+ }
+
+ internal static Xwt.Rectangle GetScreenRect(IIntellisenseSession session)
+ {
+ return Xwt.MessageDialog.RootWindow.Screen.VisibleBounds;
+ //TODO
+ //if ((session != null) && (session.TextView != null)) {
+ // Visual sessionViewVisual = ((IWpfTextView)session.TextView).VisualElement;
+ // if ((sessionViewVisual != null) && PresentationSource.FromVisual (sessionViewVisual) != null) {
+ // Rect nativeScreenRect = WpfHelper.GetScreenRect (sessionViewVisual.PointToScreen (new Point (0, 0)));
+ // return new Rect
+ // (0,
+ // 0,
+ // nativeScreenRect.Width * WpfHelper.DeviceScaleX,
+ // nativeScreenRect.Height * WpfHelper.DeviceScaleY);
+ // }
+ //}
+
+ //return new Rect
+ // (0,
+ // 0,
+ // SystemParameters.PrimaryScreenWidth * WpfHelper.DeviceScaleX,
+ // SystemParameters.PrimaryScreenHeight * WpfHelper.DeviceScaleY);
+ }
+
+ internal static Collection<ITextBuffer> GetBuffersForTriggerPoint(IIntellisenseSession session)
+ {
+ return session.TextView.BufferGraph.GetTextBuffers(
+ buffer => session.GetTriggerPoint(buffer) != null);
+ }
+
+ public static bool CanMapDownToBuffer(ITextView textView, ITextBuffer textBuffer, ITrackingPoint triggerPoint)
+ {
+ SnapshotPoint triggerSnapshotPoint = triggerPoint.GetPoint(textView.TextSnapshot);
+ var triggerSpan = new SnapshotSpan(triggerSnapshotPoint, 0);
+
+ var mappedSpans = new FrugalList<SnapshotSpan>();
+ MappingHelper.MapDownToBufferNoTrack(triggerSpan, textBuffer, mappedSpans);
+ return mappedSpans.Count > 0;
+ }
+
+ public static IEnumerable<TSource> GetSources<TSource>(
+ IIntellisenseSession session,
+ Func<ITextBuffer, IEnumerable<TSource>> sourceCreator) where TSource : IDisposable
+ {
+ return IntellisenseSourceCache.GetSources(
+ session.TextView,
+ GetBuffersForTriggerPoint(session),
+ sourceCreator);
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/BaseIntellisenseSession.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/BaseIntellisenseSession.cs
new file mode 100644
index 0000000000..c8d2849a4d
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/BaseIntellisenseSession.cs
@@ -0,0 +1,147 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Utilities;
+using Microsoft.VisualStudio.Utilities;
+
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
+{
+ internal abstract class BaseIntellisenseSession : IIntellisenseSession
+ {
+ private PropertyCollection properties = new PropertyCollection();
+ protected readonly ITextView textView;
+ protected IIntellisensePresenter presenter;
+
+ protected BaseIntellisenseSession(ITextView textView)
+ {
+ if (textView == null)
+ {
+ throw new ArgumentNullException("textView");
+ }
+
+ this.textView = textView;
+ }
+
+ public event EventHandler PresenterChanged;
+ public event EventHandler Recalculated;
+ public event EventHandler Dismissed;
+
+ public ITextView TextView
+ {
+ get { return this.textView; }
+ }
+
+ public PropertyCollection Properties
+ {
+ get { return this.properties; }
+ }
+
+ public IIntellisensePresenter Presenter
+ {
+ get { return this.presenter; }
+ }
+
+ public virtual void Dismiss()
+ {
+ this.properties = null;
+ this.presenter = null;
+ }
+
+ public abstract void Start();
+ public abstract void Recalculate();
+ public abstract bool Match();
+ public abstract void Collapse();
+ public abstract bool IsDismissed { get; }
+ public abstract ITrackingPoint TriggerPoint { get; }
+
+ public virtual ITrackingPoint GetTriggerPoint(ITextBuffer textBuffer)
+ {
+ var mappedTriggerPoint = GetTriggerPoint(textBuffer.CurrentSnapshot);
+
+ if (!mappedTriggerPoint.HasValue)
+ {
+ return null;
+ }
+
+ return mappedTriggerPoint.Value.Snapshot.CreateTrackingPoint(mappedTriggerPoint.Value, PointTrackingMode.Negative);
+ }
+
+ public SnapshotPoint? GetTriggerPoint(ITextSnapshot textSnapshot)
+ {
+ var triggerSnapshotPoint = this.TriggerPoint.GetPoint(this.textView.TextSnapshot);
+ var triggerSpan = new SnapshotSpan(triggerSnapshotPoint, 0);
+
+ var mappedSpans = new FrugalList<SnapshotSpan>();
+ MappingHelper.MapDownToBufferNoTrack(triggerSpan, textSnapshot.TextBuffer, mappedSpans);
+
+ if (mappedSpans.Count == 0)
+ {
+ return null;
+ }
+ else
+ {
+ return mappedSpans[0].Start;
+ }
+ }
+
+ protected IIntellisensePresenter FindPresenter
+ (IIntellisenseSession session,
+ IList<Lazy<IIntellisensePresenterProvider, IOrderableContentTypeMetadata>> orderedPresenterProviders,
+ GuardedOperations guardedOperations)
+ {
+ var buffers = Helpers.GetBuffersForTriggerPoint(session);
+
+ foreach (var presenterProviderExport in orderedPresenterProviders)
+ {
+ foreach (var buffer in buffers)
+ {
+ foreach (var contentType in presenterProviderExport.Metadata.ContentTypes)
+ {
+ if (buffer.ContentType.IsOfType(contentType))
+ {
+ IIntellisensePresenter presenter = guardedOperations.InstantiateExtension(
+ session, presenterProviderExport,
+ factory => factory.TryCreateIntellisensePresenter(session));
+ if (presenter != null)
+ {
+ return presenter;
+ }
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+
+
+ protected void RaisePresenterChanged()
+ {
+ EventHandler tempHandler = this.PresenterChanged;
+ if (tempHandler != null)
+ {
+ tempHandler(this, EventArgs.Empty);
+ }
+ }
+
+ protected void RaiseRecalculated()
+ {
+ EventHandler tempHandler = this.Recalculated;
+ if (tempHandler != null)
+ {
+ tempHandler(this, EventArgs.Empty);
+ }
+ }
+
+ protected void RaiseDismissed()
+ {
+ EventHandler tempHandler = this.Dismissed;
+ if (tempHandler != null)
+ {
+ tempHandler(this, EventArgs.Empty);
+ }
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/CurrentLineSpaceReservationAgent.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/CurrentLineSpaceReservationAgent.cs
new file mode 100644
index 0000000000..f61e991312
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/CurrentLineSpaceReservationAgent.cs
@@ -0,0 +1,220 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Microsoft Corporation. All rights reserved.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.ComponentModel.Composition;
+using System.Diagnostics;
+using System.Windows;
+using System.Windows.Media;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Formatting;
+using Microsoft.VisualStudio.Text.Utilities;
+using Microsoft.VisualStudio.Utilities;
+using MonoDevelop.Components;
+using Rect = Xwt.Rectangle;
+using Point = Xwt.Point;
+
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
+{
+ internal class CurrentLineSpaceReservationAgent : ISpaceReservationAgent
+ {
+ internal const string CurrentLineSRManagerName = "currentline";
+
+ private ITextView _textView;
+ private IIntellisenseSessionStack _sessionStack;
+ private bool _isAttached = false;
+
+ [Export(typeof(ITextViewCreationListener))]
+ [ContentType("Text")]
+ [TextViewRole(PredefinedTextViewRoles.Editable)]
+ internal class CurrentLineSpaceReservationAgent_ViewCreationListener : ITextViewCreationListener
+ {
+ [Import]
+ internal IIntellisenseSessionStackMapService IntellisenseSessionStackMapService { get; set; }
+
+ public void TextViewCreated(ITextView textView)
+ {
+ var sessionStack = this.IntellisenseSessionStackMapService.GetStackForTextView(textView);
+ if (sessionStack != null)
+ {
+ var currentLineReservationAgent = new CurrentLineSpaceReservationAgent(textView, sessionStack);
+ textView.Properties.AddProperty(typeof(CurrentLineSpaceReservationAgent), currentLineReservationAgent);
+ }
+ }
+ }
+
+ internal CurrentLineSpaceReservationAgent(ITextView textView, IIntellisenseSessionStack sessionStack)
+ {
+ _textView = textView;
+ _sessionStack = sessionStack;
+
+ // We'll need to know when the collection of sessions changes.
+ ((INotifyCollectionChanged)_sessionStack.Sessions).CollectionChanged += this.OnIntellisenseSessions_CollectionChanged;
+
+ // Make sure to unsubscribe when the view closes.
+ EventHandler closedHandler = null;
+ closedHandler = delegate
+ {
+ this.Detach();
+
+ ((INotifyCollectionChanged)_sessionStack.Sessions).CollectionChanged -= this.OnIntellisenseSessions_CollectionChanged;
+ _textView.Closed -= closedHandler;
+ };
+ _textView.Closed += closedHandler;
+ }
+
+ public Geometry PositionAndDisplay(Geometry reservedSpace)
+ {
+ return this.CurrentLineGeometry;
+ }
+
+ public void Hide()
+ {
+ }
+
+ public bool IsMouseOver
+ {
+ get { return false; }
+ }
+
+ public bool HasFocus
+ {
+ get { return false; }
+ }
+
+#pragma warning disable 67
+ public event EventHandler LostFocus;
+ public event EventHandler GotFocus;
+#pragma warning restore 67
+
+ private void OnIntellisenseSessions_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ IEnumerable<IIntellisenseSession> sessions = (IEnumerable<IIntellisenseSession>)sender;
+
+ bool shouldBeAttached = false;
+ foreach (var session in sessions)
+ {
+ if ((session is ICompletionSession) || (session is ISignatureHelpSession))
+ {
+ shouldBeAttached = true;
+ break;
+ }
+ }
+
+ if (_isAttached && !shouldBeAttached)
+ {
+ this.Detach();
+ }
+ else if (!_isAttached && shouldBeAttached)
+ {
+ this.Attach();
+ }
+ }
+
+ private void OnCaret_PositionChanged(object sender, CaretPositionChangedEventArgs e)
+ {
+ if (_isAttached)
+ {
+ _textView.QueueSpaceReservationStackRefresh();
+ }
+ }
+
+ private void OnSRManager_AgentChanged(object sender, SpaceReservationAgentChangedEventArgs e)
+ {
+ if (_isAttached && (e.OldAgent == this))
+ {
+ // When our space reservation agent is removed from the view, wait for the next layout before re-adding the agent.
+ EventHandler<TextViewLayoutChangedEventArgs> layoutChangedHandler = null;
+ layoutChangedHandler = delegate(object layoutSender, TextViewLayoutChangedEventArgs layoutArgs)
+ {
+ // Make sure to always unsubscribe from the event. Do this first so we're sure not to leak this ref.
+ ITextView textView = (ITextView)layoutSender;
+ textView.LayoutChanged -= layoutChangedHandler;
+
+ if ((_textView != null) && (this.CurrentLineSRManager != null))
+ {
+ this.CurrentLineSRManager.AddAgent(this);
+ }
+ };
+ _textView.LayoutChanged += layoutChangedHandler;
+ }
+ }
+
+ private void Attach()
+ {
+ _isAttached = true;
+
+ this.CurrentLineSRManager.AgentChanged += this.OnSRManager_AgentChanged;
+ _textView.Caret.PositionChanged += this.OnCaret_PositionChanged;
+
+ if (this.CurrentLineSRManager != null)
+ {
+ this.CurrentLineSRManager.AddAgent(this);
+ }
+ }
+
+ private void Detach()
+ {
+ _isAttached = false;
+
+ this.CurrentLineSRManager.AgentChanged -= this.OnSRManager_AgentChanged;
+ _textView.Caret.PositionChanged -= this.OnCaret_PositionChanged;
+
+ if (this.CurrentLineSRManager != null)
+ {
+ this.CurrentLineSRManager.RemoveAgent(this);
+ }
+ }
+
+ private Geometry CurrentLineGeometry
+ {
+ get
+ {
+ Debug.Assert(_textView != null);
+
+ if (!_isAttached || (_textView == null))
+ {
+ return Geometry.Empty;
+ }
+
+ var caretLine = _textView.Caret.ContainingTextViewLine;
+
+ if ((caretLine != null) &&
+ (caretLine.VisibilityState != VisibilityState.Unattached) &&
+ (caretLine.VisibilityState != VisibilityState.Hidden))
+ {
+ var topLeft = ((IMdTextView)_textView).VisualElement.GetScreenCoordinates
+ (new Gdk.Point((int)_textView.ViewportLeft, (int)(caretLine.TextTop - _textView.ViewportTop)));
+ Rect screenRect = new Rect
+ (topLeft.X,
+ topLeft.Y,
+ _textView.ViewportWidth,
+ (caretLine.TextHeight) + 3); // Add some buffer to allow for the possibility of a smart
+ // tag tickler below the line.
+ return new RectangleGeometry(screenRect);
+ }
+ else
+ {
+ return Geometry.Empty;
+ }
+ }
+ }
+
+ private ISpaceReservationManager _currentLineSRManager;
+ private ISpaceReservationManager CurrentLineSRManager
+ {
+ get
+ {
+ if (_currentLineSRManager == null)
+ {
+ _currentLineSRManager = ((IMdTextView)_textView).GetSpaceReservationManager(CurrentLineSRManagerName);
+ }
+
+ return _currentLineSRManager;
+ }
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/IMultiSessionIntellisensePresenter.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/IMultiSessionIntellisensePresenter.cs
new file mode 100644
index 0000000000..581b307066
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/IMultiSessionIntellisensePresenter.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Windows;
+using Rect = Xwt.Rectangle;
+
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
+{
+ /// <summary>
+ /// An internal interface defining the basic operations for an <see cref="IIntellisensePresenter"/> that can be associated with more
+ /// than one <see cref="IIntellisenseSession"/>.
+ /// </summary>
+ internal interface IMultiSessionIntellisensePresenter<TSession> : IDisposable
+ where TSession : IIntellisenseSession
+ {
+ bool IsAttachedToSession { get; }
+
+ void AttachToSession(TSession session);
+ void DetachFromSession();
+
+ Rect? AllowableScreenSize { get; }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/IntellisenseManager.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/IntellisenseManager.cs
new file mode 100644
index 0000000000..c9b1847b37
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/IntellisenseManager.cs
@@ -0,0 +1,194 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Microsoft Corporation. All rights reserved.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Projection;
+using Microsoft.VisualStudio.Text.Utilities;
+using Microsoft.VisualStudio.Utilities;
+
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
+{
+ [PartCreationPolicy(CreationPolicy.NonShared)]
+ [Export(typeof(ITextViewConnectionListener))]
+ [ContentType("text")]
+ [TextViewRole(PredefinedTextViewRoles.Editable)]
+ [TextViewRole(PredefinedTextViewRoles.EmbeddedPeekTextView)]
+ [TextViewRole(PredefinedTextViewRoles.CodeDefinitionView)]
+ internal class IntellisenseManagerConnectionListener : ITextViewConnectionListener
+ {
+ [ImportMany]
+ internal List<Lazy<IIntellisenseControllerProvider, IContentTypeMetadata>> IntellisenseControllerFactories { get; set; }
+
+ [Import]
+ internal IGuardedOperations GuardedOperations { get; set; }
+
+ public void SubjectBuffersConnected(
+ ITextView textView,
+ ConnectionReason reason,
+ IReadOnlyCollection<ITextBuffer> subjectBuffers)
+ {
+ IntellisenseManager manager = textView.Properties.GetOrCreateSingletonProperty(
+ delegate { return new IntellisenseManager(this, textView); });
+
+ // Create the appropriate Intellisense controllers for the content types in the buffer graph. It's important that we do
+ // this after creating the brokers, as the controllers will most likely start using the brokers immediately.
+
+ for (int f = 0; f < this.IntellisenseControllerFactories.Count; ++f)
+ {
+ var factory = this.IntellisenseControllerFactories[f];
+
+ // filter subject buffers to get the ones that match the factory content types
+ FrugalList<ITextBuffer> matchingSubjectBuffers = new FrugalList<ITextBuffer>();
+ foreach (string factoryContentType in factory.Metadata.ContentTypes)
+ {
+ foreach (ITextBuffer subjectBuffer in subjectBuffers)
+ {
+ if (subjectBuffer.ContentType.IsOfType(factoryContentType) &&
+ !matchingSubjectBuffers.Contains(subjectBuffer))
+ {
+ matchingSubjectBuffers.Add(subjectBuffer);
+ }
+ }
+ }
+
+ if (matchingSubjectBuffers.Count > 0)
+ {
+ // This controller factory is registered for the content type we understand. Go ahead and create
+ // one. Note that this won't give us a handle to a controller object. We wouldn't be able to do anything
+ // with such a reference anyway.
+
+ if (manager.Controllers[f] == null)
+ {
+ manager.Controllers[f] = this.GuardedOperations.InstantiateExtension
+ (factory, factory,
+ provider => provider.TryCreateIntellisenseController(textView, matchingSubjectBuffers));
+ }
+ else
+ {
+ foreach (ITextBuffer matchingSubjectBuffer in matchingSubjectBuffers)
+ {
+ manager.Controllers[f].ConnectSubjectBuffer(matchingSubjectBuffer);
+ }
+ }
+ }
+ }
+ }
+
+ public void SubjectBuffersDisconnected(
+ ITextView textView,
+ ConnectionReason reason,
+ IReadOnlyCollection<ITextBuffer> subjectBuffers)
+ {
+ // Notify controllers that subject buffer is no longer interesting. We let the controller figure out if the
+ // buffer was interesting in the first place.
+ IntellisenseManager manager = textView.Properties.GetProperty<IntellisenseManager>(typeof(IntellisenseManager));
+
+ for (int f = 0; f < manager.Controllers.Length; ++f)
+ {
+ if (manager.Controllers[f] != null)
+ {
+ foreach (ITextBuffer subjectBuffer in subjectBuffers)
+ {
+ manager.Controllers[f].DisconnectSubjectBuffer(subjectBuffer);
+ }
+ }
+ }
+ }
+ }
+
+ internal class IntellisenseManager
+ {
+ private readonly IntellisenseManagerConnectionListener _componentContext;
+ private readonly ITextView _associatedTextView;
+ public IIntellisenseController[] Controllers { get; private set; }
+
+ internal IntellisenseManager(IntellisenseManagerConnectionListener componentContext, ITextView associatedTextView)
+ {
+ _componentContext = componentContext;
+ this.Controllers = new IIntellisenseController[_componentContext.IntellisenseControllerFactories.Count];
+ _associatedTextView = associatedTextView;
+
+ _associatedTextView.Closed += this.OnViewClosed;
+ _associatedTextView.BufferGraph.GraphBufferContentTypeChanged += this.OnGraphBufferContentTypeChange;
+ }
+
+ private void OnViewClosed(object sender, EventArgs e)
+ {
+ // Detach each of the Intellisense controllers on the associated view. We won't need them anymore
+ foreach (var controller in this.Controllers)
+ {
+ if (controller != null)
+ {
+ controller.Detach(_associatedTextView);
+ }
+ }
+
+ // Stop listening to events
+ _associatedTextView.Closed -= this.OnViewClosed;
+ _associatedTextView.BufferGraph.GraphBufferContentTypeChanged -= this.OnGraphBufferContentTypeChange;
+ }
+
+ private void OnGraphBufferContentTypeChange(object sender, GraphBufferContentTypeChangedEventArgs args)
+ {
+ if (args.BeforeContentType.IsOfType("text") && args.AfterContentType.IsOfType("text"))
+ {
+ // We won't get subject buffers connected/disconnected calls when both the before & after content
+ // types are "text", but we still need to manage intellisense controllers in this situation.
+ // The broker associated with the subjectBuffer in question remains the same.
+ ITextBuffer subjectBuffer = args.TextBuffer;
+
+ for (int f = 0; f < _componentContext.IntellisenseControllerFactories.Count; ++f)
+ {
+ var factory = _componentContext.IntellisenseControllerFactories[f];
+ bool beforeMatch = false;
+ bool afterMatch = false;
+ foreach (string factoryContentType in factory.Metadata.ContentTypes)
+ {
+ if (args.BeforeContentType.IsOfType(factoryContentType))
+ {
+ beforeMatch = true;
+ }
+ if (args.AfterContentType.IsOfType(factoryContentType))
+ {
+ afterMatch = true;
+ }
+ }
+ if (beforeMatch != afterMatch)
+ {
+ if (beforeMatch)
+ {
+ if (this.Controllers[f] != null)
+ {
+ // the controller will be null if its creation failed
+ this.Controllers[f].DisconnectSubjectBuffer(subjectBuffer);
+ // should we destroy the controller if it has no more buffers?
+ }
+ }
+
+ if (afterMatch)
+ {
+ if (this.Controllers[f] != null)
+ {
+ this.Controllers[f].ConnectSubjectBuffer(subjectBuffer);
+ }
+ else
+ {
+ this.Controllers[f] =
+ this._componentContext.GuardedOperations.InstantiateExtension(
+ factory,
+ factory,
+ provider => provider.TryCreateIntellisenseController
+ (_associatedTextView, new FrugalList<ITextBuffer>() { subjectBuffer }));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/IntellisenseSession.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/IntellisenseSession.cs
new file mode 100644
index 0000000000..756775d29c
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/IntellisenseSession.cs
@@ -0,0 +1,30 @@
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+using System;
+
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
+{
+ internal abstract class IntellisenseSession : BaseIntellisenseSession
+ {
+ protected readonly ITrackingPoint triggerPoint;
+
+ protected IntellisenseSession(ITextView textView, ITrackingPoint triggerPoint)
+ : base(textView)
+ {
+ if (triggerPoint == null)
+ {
+ throw new ArgumentNullException("triggerPoint");
+ }
+
+ this.triggerPoint = triggerPoint;
+ }
+
+ public override ITrackingPoint TriggerPoint
+ {
+ get
+ {
+ return this.triggerPoint;
+ }
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/IntellisenseSessionStack.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/IntellisenseSessionStack.cs
new file mode 100644
index 0000000000..f53c921949
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/IntellisenseSessionStack.cs
@@ -0,0 +1,550 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Microsoft Corporation. All rights reserved.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Windows.Input;
+using Microsoft.VisualStudio.Text.Editor;
+
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
+{
+ internal class StackList<T> : ObservableCollection<T>
+ {
+ internal void Push(T item)
+ {
+ this.Insert(0, item);
+ }
+
+ internal T Pop()
+ {
+ if (this.Count > 0)
+ {
+ T item = this[0];
+ this.RemoveAt(0);
+ return (item);
+ }
+ else
+ {
+ return (default(T));
+ }
+ }
+
+ internal T Peek()
+ {
+ if (this.Count > 0)
+ {
+ return (this[0]);
+ }
+ else
+ {
+ return (default(T));
+ }
+ }
+ }
+
+ internal sealed class IntellisenseSessionStack : IIntellisenseSessionStack, IIntellisenseCommandTarget
+ {
+ private readonly IObscuringTipManager _tipManager;
+ private IMdTextView _textView;
+ private StackList<IIntellisenseSession> _sessions = new StackList<IIntellisenseSession>();
+ private ReadOnlyObservableCollection<IIntellisenseSession> _readOnlySessions;
+ private Dictionary<IIntellisenseSession, ISpaceReservationAgent> _reservationAgentIndex =
+ new Dictionary<IIntellisenseSession, ISpaceReservationAgent>();
+ private Dictionary<ISpaceReservationAgent, ISpaceReservationManager> _reservationManagerIndex =
+ new Dictionary<ISpaceReservationAgent, ISpaceReservationManager>();
+ private IIntellisenseSession _keyboardSession;
+ private IIntellisenseSession _sessionBeingRehosted;
+
+ public IntellisenseSessionStack(IMdTextView textView, IObscuringTipManager tipManager)
+ {
+ _textView = textView;
+ _tipManager = tipManager; // This can be null
+ }
+
+ public IIntellisenseSession PopSession()
+ {
+ // First, remove the session at the top of the stack.
+
+ IIntellisenseSession session = _sessions.Pop();
+ if (session != null)
+ {
+ this.ReleaseKeyboard();
+ this.RemoveSession(session);
+ }
+
+ // Now, figure out once again who deserves to own the keyboard.
+
+ this.DoleOutKeyboard();
+
+ return (session);
+ }
+
+ public void PushSession(IIntellisenseSession session)
+ {
+ // make sure the session is not already dismissed
+ if (session.IsDismissed)
+ {
+ throw new ArgumentException("We cannot push a dismissed session on to the stack.");
+ }
+
+ // Tell whatever session has the keyboard to release it. After we push, we'll figure out who deserves it.
+
+ this.ReleaseKeyboard();
+
+ // Add the session to our list.
+
+ _sessions.Push(session);
+
+ // Subscribe to events on this new session
+
+ session.Dismissed += this.OnSessionDismissed;
+ session.PresenterChanged += this.OnSessionPresenterChanged;
+ IPopupIntellisensePresenter popupPresenter = session.Presenter as IPopupIntellisensePresenter;
+ if (popupPresenter != null)
+ {
+ popupPresenter.SurfaceElementChanged += this.OnPresenterSurfaceElementChanged;
+ popupPresenter.PresentationSpanChanged += this.OnPresenterPresentationSpanChanged;
+ popupPresenter.PopupStylesChanged += this.OnPresenterPopupStylesChanged;
+
+ // Since this is a popup presenter, we're responsible for drawing it. Therefore, here we'll create a popup agent
+ // that will take care of rendering this session's presenter.
+
+ this.HostSession(session, popupPresenter);
+ }
+ else
+ {
+ ICustomIntellisensePresenter customPresenter = session.Presenter as ICustomIntellisensePresenter;
+ if (customPresenter != null)
+ {
+ customPresenter.Render();
+ }
+ }
+
+ // Now, figure out once again who deserves to own the keyboard.
+
+ this.DoleOutKeyboard();
+ }
+
+ public void MoveSessionToTop(IIntellisenseSession session)
+ {
+ // Make sure this session is actually in the stack.
+ if (session == null)
+ {
+ throw new ArgumentNullException("session");
+ }
+ int sessionIndex = _sessions.IndexOf(session);
+ if (sessionIndex == -1)
+ {
+ throw new ArgumentException
+ ("IIntellisenseSessionStack.MoveSessionToTop() must be called with a session already in the stack.",
+ "session");
+ }
+
+ // Release the keyboard. We'll give it back in a minute.
+ this.ReleaseKeyboard();
+
+ // Remove the session from the stack at its old position and re-add it at the top.
+ _sessions.RemoveAt(sessionIndex);
+ _sessions.Push(session);
+
+ // Dole out the keyboard once again.
+ this.DoleOutKeyboard();
+ }
+
+ public ReadOnlyObservableCollection<IIntellisenseSession> Sessions
+ {
+ get
+ {
+ if (_readOnlySessions == null)
+ {
+ _readOnlySessions = new ReadOnlyObservableCollection<IIntellisenseSession>(_sessions);
+ }
+
+ return _readOnlySessions;
+ }
+ }
+
+ public IIntellisenseSession TopSession
+ {
+ get { return (_sessions.Peek()); }
+ }
+
+ public void CollapseAllSessions()
+ {
+ List<IIntellisenseSession> sessions = new List<IIntellisenseSession>(_sessions);
+ foreach (var session in sessions)
+ {
+ session.Collapse();
+ }
+ }
+
+ /// <summary>
+ /// Calls each of the stack's session presenters, in order, to see if they want to handle the keyboard command
+ /// </summary>
+ public bool ExecuteKeyboardCommand(IntellisenseKeyboardCommand command)
+ {
+ // We don't care if there's a keyboard session here or not. If someone has captured the keyboard, this should only get
+ // called if the capturer has decided not to handle the command.
+
+ // Run through the sessions from the topmost to the bottom-most.
+ foreach (IIntellisenseSession session in _sessions)
+ {
+ IIntellisenseCommandTarget commandTarget = session.Presenter as IIntellisenseCommandTarget;
+ if (commandTarget != null)
+ {
+ if (commandTarget.ExecuteKeyboardCommand(command))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private void OnSpaceReservationManager_AgentChanged(object sender, SpaceReservationAgentChangedEventArgs e)
+ {
+ if (e.NewAgent == null)
+ {
+ List<IIntellisenseSession> sessionsToDismiss = new List<IIntellisenseSession>();
+
+ foreach (IIntellisenseSession session in _reservationAgentIndex.Keys)
+ {
+ // Skip sessions that are being re-hosted.
+
+ if (session == _sessionBeingRehosted)
+ { continue; }
+
+ if (_reservationAgentIndex[session] == e.OldAgent)
+ {
+ // We've just noticed that one of the agents we've put up on-screen has gone away. We need to dismiss
+ // the session being displayed with this agent.
+
+ if (!session.IsDismissed)
+ {
+ sessionsToDismiss.Add(session);
+ }
+ }
+ }
+
+ // We need to dismiss these outside of the main loop, as dismissing will change the _reservationAgentIndex, which
+ // will fail the enumeration above.
+
+ foreach (IIntellisenseSession session in sessionsToDismiss)
+ {
+ session.Dismiss();
+ }
+ }
+ }
+
+ private void OnSessionDismissed(object sender, EventArgs e)
+ {
+ // Whenever a session is dismissed, we'll want to remove it from the stack, as it's no longer a "valid" session.
+
+ IIntellisenseSession session = sender as IIntellisenseSession;
+ if (session == null)
+ {
+ throw new ArgumentException("Expected 'sender' to be of type IIntellisenseSession", "sender");
+ }
+
+ if (!_sessions.Contains(session))
+ {
+ throw new ArgumentException("Expected session that is already on the stack", "sender");
+ }
+
+ // If it's the top session that was dismissed, our job is easy, we just have to pop() it off.
+
+ if ((session.TextView != null) && (_tipManager != null))
+ {
+ var tip = session.Presenter as IObscuringTip;
+ if (tip != null)
+ {
+ _tipManager.RemoveTip(session.TextView, tip);
+ }
+ }
+
+ if (session == this.TopSession)
+ {
+ this.PopSession();
+ return;
+ }
+
+ // Must not have been the top session. We'll have to remove it from the stack manually, then remove all of our
+ // event handlers, etc.
+
+ _sessions.Remove(session);
+ this.RemoveSession(session);
+
+ // If the session being removed is the one that's holding-on to the keyboard, we need to figure out who owns the
+ // keyboard once again.
+
+ if (session == _keyboardSession)
+ {
+ this.DoleOutKeyboard();
+ }
+ }
+
+ private void OnPresenterPresentationSpanChanged(object sender, EventArgs e)
+ {
+ IPopupIntellisensePresenter presenter = sender as IPopupIntellisensePresenter;
+ if (presenter == null)
+ {
+ throw new ArgumentException("Expected 'sender' to be of type IPopupIntellisensePresenter", "sender");
+ }
+
+ this.RehostPresenter(presenter);
+ }
+
+ private void OnPresenterSurfaceElementChanged(object sender, EventArgs e)
+ {
+ IPopupIntellisensePresenter presenter = sender as IPopupIntellisensePresenter;
+ if (presenter == null)
+ {
+ throw new ArgumentException("Expected 'sender' to be of type IPopupIntellisensePresenter", "sender");
+ }
+
+ this.RehostPresenter(presenter);
+ }
+
+ private void OnPresenterPopupStylesChanged(object sender, ValueChangedEventArgs<Text.Adornments.PopupStyles> e)
+ {
+ IPopupIntellisensePresenter presenter = sender as IPopupIntellisensePresenter;
+ if (presenter == null)
+ {
+ throw new ArgumentException("Expected 'sender' to be of type IPopupIntellisensePresenter", "sender");
+ }
+
+ this.RehostPresenter(presenter);
+ }
+
+ private void OnSessionPresenterChanged(object sender, EventArgs e)
+ {
+ // It could have been any of our sessions that fired this event. That means we've first got to determine which one it
+ // was.
+
+ IIntellisenseSession session = sender as IIntellisenseSession;
+ if (session == null)
+ {
+ throw new ArgumentException("Expected 'sender' to be of type IIntellisenseSession", "sender");
+ }
+
+ // Since the presenter changed, we could have a new owner for the keyboard. Figure it out.
+
+ this.ReleaseKeyboard();
+
+ // We should make sure to re-subscribe to events on this new presenter.
+
+ IPopupIntellisensePresenter popupPresenter = session.Presenter as IPopupIntellisensePresenter;
+ if (popupPresenter != null)
+ {
+ popupPresenter.PresentationSpanChanged += this.OnPresenterPresentationSpanChanged;
+ popupPresenter.SurfaceElementChanged += this.OnPresenterSurfaceElementChanged;
+ popupPresenter.PopupStylesChanged += this.OnPresenterPopupStylesChanged;
+
+ this.RehostSession(session);
+ }
+ else
+ {
+ ICustomIntellisensePresenter customPresenter = session.Presenter as ICustomIntellisensePresenter;
+ if (customPresenter != null)
+ {
+ customPresenter.Render();
+ }
+ }
+
+ if ((session.TextView != null) && (_tipManager != null))
+ {
+ var tip = session.Presenter as IObscuringTip;
+ if (tip != null)
+ {
+ _tipManager.PushTip(session.TextView, tip);
+ }
+ }
+
+ this.DoleOutKeyboard();
+ }
+
+ private void ReleaseKeyboard()
+ {
+ if (_keyboardSession != null)
+ {
+ ICustomKeyboardHandler keyboardHandler = _keyboardSession.Presenter as ICustomKeyboardHandler;
+ if (keyboardHandler != null)
+ {
+ keyboardHandler.ReleaseKeyboard();
+ }
+ }
+
+ _keyboardSession = null;
+ }
+
+ private void DoleOutKeyboard()
+ {
+ if (_keyboardSession != null)
+ {
+ this.ReleaseKeyboard();
+ }
+
+ // The idea is to walk down the stack from top to bottom, looking for the first session that has a presenter. When we
+ // find it, give it the keyboard, unless it already has it.
+
+ foreach (IIntellisenseSession session in _sessions)
+ {
+ ICustomKeyboardHandler keyboardHandler = session.Presenter as ICustomKeyboardHandler;
+ if (keyboardHandler != null)
+ {
+ if (keyboardHandler.CaptureKeyboard())
+ {
+ _keyboardSession = session;
+ break;
+ }
+ }
+ }
+ }
+
+ private void HostSession(IIntellisenseSession session, IPopupIntellisensePresenter popupPresenter)
+ {
+ // If the Popup presenter doesn't have anything to draw, don't even bother.
+
+ if (popupPresenter.SurfaceElement == null)
+ { return; }
+
+ ISpaceReservationManager manager = _textView.GetSpaceReservationManager(popupPresenter.SpaceReservationManagerName);
+ if (manager != null)
+ {
+ // If this is the first time we've seen this manager, subscribe to its AgentChanged event.
+
+ if (!_reservationManagerIndex.ContainsValue(manager))
+ {
+ manager.AgentChanged += this.OnSpaceReservationManager_AgentChanged;
+ }
+
+ ISpaceReservationAgent agent = manager.CreatePopupAgent(popupPresenter.PresentationSpan,
+ popupPresenter.PopupStyles,
+ popupPresenter.SurfaceElement);
+
+ // We'll need to hold-on to the manager and agent so that later, when we want to hide this popup, we can clear
+ // the agent.
+
+ _reservationManagerIndex[agent] = manager;
+ _reservationAgentIndex[session] = agent;
+
+ // When we add this agent to the manager's collection, the popup will become visible.
+
+ manager.AddAgent(agent);
+ }
+ }
+
+ private void RehostSession(IIntellisenseSession session)
+ {
+ IPopupIntellisensePresenter popupPresenter = session.Presenter as IPopupIntellisensePresenter;
+ if (popupPresenter == null)
+ {
+ throw new ArgumentException("Expected to rehost a session with presenter of type IPopupIntellisensePresenter",
+ "session");
+ }
+
+ // If the Popup presenter doesn't have anything to draw, don't even bother.
+
+ if (popupPresenter.PresentationSpan == null || popupPresenter.SurfaceElement == null)
+ {
+ return;
+ }
+
+ // We need to re-draw this popup. This involves tearing down the old agent and re-creating it. First, we've
+ // got to find it. We'll also want to save-off a reference to it so that we know we're re-hosting this session. The
+ // process of tearing-down and re-adding a space reservation agent is loud and messy. We don't want to accidentally
+ // dismiss this session, thinking that it went out-of-focus.
+
+ _sessionBeingRehosted = session;
+
+ try
+ {
+ ISpaceReservationAgent oldAgent = null;
+ if ((_reservationAgentIndex.TryGetValue(session, out oldAgent)) && (oldAgent != null))
+ {
+ ISpaceReservationManager manager = null;
+ if ((_reservationManagerIndex.TryGetValue(oldAgent, out manager)) && (manager != null))
+ {
+ manager.UpdatePopupAgent(oldAgent, popupPresenter.PresentationSpan, popupPresenter.PopupStyles);
+ }
+ }
+ else
+ {
+ // This presenter hasn't yet been hosted in a popup. Host it for the first time.
+
+ this.HostSession(session, popupPresenter);
+ }
+ }
+ finally
+ {
+ // Clear out our state. We're no longer re-hosting this session.
+
+ _sessionBeingRehosted = null;
+ }
+ }
+
+ private void RemoveSession(IIntellisenseSession session)
+ {
+ // A session can be added to the stack in only one way. It must be pushed onto the top of the stack. A session can
+ // come off the stack in many ways, however. It can be popped, or the session can be dismissed, etc. We'll assume that
+ // the session passed-in has already been removed from the stack. We're just interested in removing it from our indices
+ // and getting ourselves unhooked from its delegates.
+
+ // First, unsubscribe from events to which we previously subscribed
+
+ session.Dismissed -= this.OnSessionDismissed;
+ session.PresenterChanged -= this.OnSessionPresenterChanged;
+
+ IPopupIntellisensePresenter popupPresenter = session.Presenter as IPopupIntellisensePresenter;
+ if (popupPresenter != null)
+ {
+ popupPresenter.SurfaceElementChanged -= this.OnPresenterSurfaceElementChanged;
+ popupPresenter.PresentationSpanChanged -= this.OnPresenterPresentationSpanChanged;
+ popupPresenter.PopupStylesChanged -= this.OnPresenterPopupStylesChanged;
+
+ // Now, see if we have a popup agent for this presenter. If so, let's get rid of it. This is what will "hide" the
+ // popup.
+
+ ISpaceReservationAgent agent;
+ if ((_reservationAgentIndex.TryGetValue(session, out agent)) && (agent != null))
+ {
+ ISpaceReservationManager manager;
+ if ((_reservationManagerIndex.TryGetValue(agent, out manager)) && (manager != null))
+ {
+ manager.RemoveAgent(agent);
+ _reservationManagerIndex.Remove(agent);
+
+ // If this was the last reference to this particular manager, stop listening to its AgentChanged event.
+
+ if (!_reservationManagerIndex.ContainsValue(manager))
+ {
+ manager.AgentChanged -= this.OnSpaceReservationManager_AgentChanged;
+ }
+ }
+ _reservationAgentIndex.Remove(session);
+ }
+ }
+ }
+
+ private void RehostPresenter(IPopupIntellisensePresenter presenter)
+ {
+ IIntellisenseSession session = presenter.Session;
+ if (session != null)
+ {
+ // Technically, there's probably no reason to release the keyboard here, but maybe there's a presenter that
+ // conditionally holds-on to the keyboard depending on its presentation. In that case, we'll give them another
+ // chance to own the keyboard here.
+
+ this.ReleaseKeyboard();
+
+ RehostSession(session);
+
+ this.DoleOutKeyboard();
+ }
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/IntellisenseSessionStackMapService.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/IntellisenseSessionStackMapService.cs
new file mode 100644
index 0000000000..f110d2ae4b
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/IntellisenseSessionStackMapService.cs
@@ -0,0 +1,65 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Microsoft Corporation. All rights reserved.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using Microsoft.VisualStudio.Text.Adornments;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Utilities;
+
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
+{
+ [Export(typeof(IIntellisenseSessionStackMapService))]
+ internal sealed class IntellisenseSessionStackMapService : IIntellisenseSessionStackMapService
+ {
+ [Import]
+ internal IToolTipProviderFactory ToolTipProviderFactory { get; set; }
+
+ [Import(AllowDefault = true)]
+ internal IObscuringTipManager TipManager { get; set; }
+
+#if DEBUG
+ [ImportMany]
+ internal List<Lazy<IObjectTracker>> ObjectTrackers { get; set; }
+#endif
+
+ public IIntellisenseSessionStack GetStackForTextView(ITextView textView)
+ {
+ if (textView == null)
+ {
+ return (null);
+ }
+
+ IIntellisenseSessionStack stack = null;
+ if (!textView.Properties.TryGetProperty<IIntellisenseSessionStack>(typeof(IIntellisenseSessionStack), out stack))
+ {
+ var wpfTextView = textView as IMdTextView;
+ if (wpfTextView != null)
+ {
+ stack = new IntellisenseSessionStack(wpfTextView, this.TipManager);
+
+#if DEBUG
+ Helpers.TrackObject(this.ObjectTrackers, "Intellisense Session Stacks", stack);
+#endif
+
+ wpfTextView.Properties.AddProperty(typeof(IIntellisenseSessionStack), stack);
+ wpfTextView.Closed += this.OnTextViewClosed;
+ }
+ }
+
+ return (stack);
+ }
+
+ void OnTextViewClosed(object sender, EventArgs e)
+ {
+ ITextView textView = sender as ITextView;
+ if (textView != null)
+ {
+ textView.Properties.RemoveProperty(typeof(IIntellisenseSessionStack));
+ textView.Closed -= this.OnTextViewClosed;
+ }
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/IntellisenseSpaceReservationManagers.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/IntellisenseSpaceReservationManagers.cs
new file mode 100644
index 0000000000..462b256e00
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/IntellisenseSpaceReservationManagers.cs
@@ -0,0 +1,38 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Microsoft Corporation. All rights reserved
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+using System.ComponentModel.Composition;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Utilities;
+
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
+{
+ internal class IntellisenseSpaceReservationManagers
+ {
+ [Export]
+ [Name(CurrentLineSpaceReservationAgent.CurrentLineSRManagerName)]
+ [Order(Before = IntellisenseSpaceReservationManagerNames.SmartTagSpaceReservationManagerName)]
+ internal SpaceReservationManagerDefinition currentLineManager;
+
+ [Export]
+ [Name(IntellisenseSpaceReservationManagerNames.SmartTagSpaceReservationManagerName)]
+ [Order(Before = IntellisenseSpaceReservationManagerNames.QuickInfoSpaceReservationManagerName)]
+ internal SpaceReservationManagerDefinition smartTagManager;
+
+ [Export]
+ [Name(IntellisenseSpaceReservationManagerNames.QuickInfoSpaceReservationManagerName)]
+ [Order(Before = IntellisenseSpaceReservationManagerNames.SignatureHelpSpaceReservationManagerName)]
+ internal SpaceReservationManagerDefinition quickInfoManager;
+
+ [Export]
+ [Name(IntellisenseSpaceReservationManagerNames.SignatureHelpSpaceReservationManagerName)]
+ [Order(Before = IntellisenseSpaceReservationManagerNames.CompletionSpaceReservationManagerName)]
+ internal SpaceReservationManagerDefinition signatureHelpManager;
+
+ [Export]
+ [Name(IntellisenseSpaceReservationManagerNames.CompletionSpaceReservationManagerName)]
+ [Order()]
+ internal SpaceReservationManagerDefinition completionManager;
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/MultiSessionIntellisensePresenterProvider.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/MultiSessionIntellisensePresenterProvider.cs
new file mode 100644
index 0000000000..06c3acba0e
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/MultiSessionIntellisensePresenterProvider.cs
@@ -0,0 +1,101 @@
+using System;
+
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Utilities;
+
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
+{
+ /// <summary>
+ /// An intellisense presenter provider that caches the presenters it provides in the session's text view's
+ /// property bag.
+ /// </summary>
+ internal class MultiSessionIntellisensePresenterProvider<TSession, TPresenter>
+ where TSession : IIntellisenseSession
+ where TPresenter : IMultiSessionIntellisensePresenter<TSession>
+ {
+ /// <summary>
+ /// Tries to obtain a presenter from the cache (session's view's property bag). If a valid presenter was found
+ /// in the cache returns true, otherwise returns false. It always constructs a new presenter using the
+ /// provided factory.
+ /// </summary>
+ /// <returns><c>true</c> if the instance was retrieved from a cache; <c>false</c> if a new instance was created.</returns>
+ public bool GetOrCreatePresenter(TSession session, Func<TPresenter> factory, out TPresenter presenter)
+ {
+ bool addPresenterToCache = true;
+
+ // check view's property bag for a cached version of the presenter
+ if (session.TextView.Properties.TryGetProperty<TPresenter>(typeof(TPresenter), out presenter))
+ {
+ // if there is a cached presenter, make sure it's not already in a relationship with a session
+ if (!presenter.IsAttachedToSession)
+ {
+ // if there is a usable cached presenter, ensure that its screen size is compatible with the current screen size
+ // (ensure compatibility of screen resolution)
+ if (presenter.AllowableScreenSize == Helpers.GetScreenRect(session))
+ {
+ // we have a valid cached presenter, attach it to the session and return
+ presenter.AttachToSession(session);
+ return true;
+ }
+ else
+ {
+ // This one won't work. Go ahead and remove it from the cache, assuming it won't ever work again.
+ this.RemovePresenterFromCache(session);
+ }
+ }
+ else
+ {
+ // don't add the new presenter in the cache since the cache already contains a valid presenter
+ // (that happens to be attached to another session now, but we still want to re-use it for future sessions)
+ addPresenterToCache = false;
+ }
+ }
+
+ // we don't have a valid presenter or the cache is empty
+ presenter = factory.Invoke();
+
+ if (addPresenterToCache)
+ {
+ this.AddPresenterToCache(session, presenter);
+ }
+
+ // Attach our newly-created presenter to the session
+ presenter.AttachToSession(session);
+
+ // return false because we couldn't find a valid object from the cache
+ return false;
+ }
+
+ protected void AddPresenterToCache(TSession session, TPresenter presenter)
+ {
+ session.TextView.Properties.AddProperty(typeof(TPresenter), presenter);
+ session.TextView.Closed += this.OnTextViewClosed;
+ }
+
+ protected void RemovePresenterFromCache(TSession session)
+ {
+ session.TextView.Properties.RemoveProperty(typeof(TPresenter));
+ }
+
+ protected virtual void OnCacheContainerClosing(IPropertyOwner cacheContainer)
+ {
+ TPresenter presenter;
+
+ if (cacheContainer.Properties.TryGetProperty<TPresenter>(typeof(TPresenter), out presenter))
+ {
+ presenter.Dispose();
+
+ cacheContainer.Properties.RemoveProperty(typeof(TPresenter));
+ }
+ }
+
+ private void OnTextViewClosed(object sender, EventArgs args)
+ {
+ ITextView textView = (ITextView)sender;
+
+ this.OnCacheContainerClosing(textView);
+
+ textView.Closed -= this.OnTextViewClosed;
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/DefaultSignatureHelpPresenter.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/DefaultSignatureHelpPresenter.cs
new file mode 100644
index 0000000000..c153df47d9
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/DefaultSignatureHelpPresenter.cs
@@ -0,0 +1,389 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Microsoft Corporation. All rights reserved.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+using System;
+using System.Collections.Specialized;
+using System.Windows;
+using Microsoft.VisualStudio.Language.Utilities;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Adornments;
+using Microsoft.VisualStudio.Text.Editor;
+using MonoDevelop.Components;
+using Xwt;
+using Xwt.Backends;
+using Rect = Xwt.Rectangle;
+
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
+{
+ internal sealed class DefaultSignatureHelpPresenter : IPopupIntellisensePresenter, IObscuringTip, IIntellisenseCommandTarget, IMultiSessionIntellisensePresenter<ISignatureHelpSession>
+ {
+ private ISignatureHelpSession _session;
+ private ITrackingSpan _presentationSpan = null;
+ private DefaultSignatureHelpPresenterSurfaceElement _surfaceElement;
+ private Widget wrappedXwtWidget;
+ private EventHandler _surfaceElementChangedEvent;
+ private bool _isDisposed = false;
+ private SignatureHelpSessionView _sessionView;
+ private PopupStyles _popupStyles = PopupStyles.None;
+
+ public DefaultSignatureHelpPresenter(DefaultSignatureHelpPresenterProvider componentContext)
+ {
+ _surfaceElement = new DefaultSignatureHelpPresenterSurfaceElement();
+ wrappedXwtWidget = _surfaceElement.Content;
+ //TODO: _surfaceElement.MouseLeftButtonDown += this.OnSigHelpLayoutGrid_MouseLeftButtonDown;
+ _surfaceElement.UpButtonClick += this.OnSurfaceElementUpButtonClick;
+ _surfaceElement.DownButtonClick += this.OnSurfaceElementDownButtonClick;
+ _sessionView = new SignatureHelpSessionView(componentContext);
+ }
+
+ public IIntellisenseSession Session
+ {
+ get { return (_session); }
+ }
+
+ public bool ExecuteKeyboardCommand(IntellisenseKeyboardCommand command)
+ {
+ // Certain keys are important to us. We'll want to trap things like up/down/escape and make them mean something
+ // special to us.
+
+ switch (command)
+ {
+ case IntellisenseKeyboardCommand.Up:
+ return this.PreviousSignature();
+ case IntellisenseKeyboardCommand.Down:
+ return this.NextSignature();
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+ public Xwt.Widget SurfaceElement
+ {
+ get
+ {
+ return wrappedXwtWidget;
+ }
+ }
+
+ public event EventHandler SurfaceElementChanged
+ {
+ add { _surfaceElementChangedEvent += value; }
+ remove { _surfaceElementChangedEvent -= value; }
+ }
+
+ public ITrackingSpan PresentationSpan
+ {
+ get
+ {
+ return _presentationSpan;
+ }
+ }
+
+ public PopupStyles PopupStyles
+ {
+ get
+ {
+ return _popupStyles;
+ }
+ private set
+ {
+ if (value == _popupStyles)
+ {
+ return;
+ }
+
+ PopupStyles oldPopupStyles = _popupStyles;
+ _popupStyles = value;
+
+ // Raise the 'PopupStylesChanged' event.
+ EventHandler<ValueChangedEventArgs<PopupStyles>> tempHandler = this.PopupStylesChanged;
+ if (tempHandler != null)
+ {
+ tempHandler(this, new ValueChangedEventArgs<PopupStyles>(oldPopupStyles, _popupStyles));
+ }
+ }
+ }
+
+ public event EventHandler<ValueChangedEventArgs<PopupStyles>> PopupStylesChanged;
+
+ public string SpaceReservationManagerName
+ {
+ get { return IntellisenseSpaceReservationManagerNames.SignatureHelpSpaceReservationManagerName; }
+ }
+
+ public event EventHandler PresentationSpanChanged;
+
+ public double Opacity {
+ get; set;
+ //get { return (_surfaceElement.Opacity); }
+ //set { _surfaceElement.Opacity = value; }
+ }
+
+ public void Dispose()
+ {
+ // Make sure we don't double-dispose.
+ if (_isDisposed)
+ {
+ return;
+ }
+ _isDisposed = true;
+
+ _sessionView.Dispose();
+ }
+
+ public bool IsAttachedToSession
+ {
+ get { return _session != null; }
+ }
+
+ public void AttachToSession(ISignatureHelpSession session)
+ {
+ // If we're being attached to a session for the first time, set the allowable screen size for this presenter. This
+ // presenter instance should only be used for sessions on screens with this resolution.
+ if (this.AllowableScreenSize == null)
+ {
+ this.AllowableScreenSize = Helpers.GetScreenRect(session);
+ }
+
+ // If we're re-attaching, make sure to unsubscribe from events on the old session.
+ if (this.IsAttachedToSession)
+ {
+ this.DetachFromSession();
+ }
+
+ _session = session;
+ _session.Dismissed += this.OnSessionDismissed;
+ ((INotifyCollectionChanged)_session.Signatures).CollectionChanged += this.OnSessionSignaturesChanged;
+ _session.TextView.TextBuffer.Changed += this.OnSessionViewBuffer_Changed;
+
+ this.ComputePresentationSpan();
+
+ // Bind the view model to the session;
+ _sessionView.Session = session;
+
+ // Use the view model as the data context for our presenter surface element.
+ _surfaceElement.DataContext = _sessionView;
+
+ // Make sure we start off fully-opaque
+ this.Opacity = 1.0;
+ }
+
+ public void DetachFromSession()
+ {
+ // Don't use the view model as the data context anymore.
+ _surfaceElement.DataContext = null;
+
+ // Unbind the view model.
+ _sessionView.Session = null;
+
+ // Stop listening to all of the session/surface element events.
+ _session.Dismissed -= this.OnSessionDismissed;
+ ((INotifyCollectionChanged)_session.Signatures).CollectionChanged -= this.OnSessionSignaturesChanged;
+ _session.TextView.TextBuffer.Changed -= this.OnSessionViewBuffer_Changed;
+
+ _session = null;
+
+ // Reset out popup styles for the next session to which we get attached.
+ _popupStyles = PopupStyles.None;
+
+ _surfaceElement.Hide();
+ _presentationSpan = null;
+ }
+
+ public Rect? AllowableScreenSize { get; private set; }
+
+ bool IObscuringTip.Dismiss()
+ {
+ if ((_session != null) && !(_session.IsDismissed))
+ {
+ _session.Dismiss();
+ return true;
+ }
+
+ return false;
+ }
+
+ void IObscuringTip.SetOpacity(double opacity)
+ {
+ this.Opacity = opacity;
+ }
+
+ private void OnSessionDismissed(object sender, EventArgs e)
+ {
+ this.DetachFromSession();
+ }
+
+ private void OnSurfaceElementDownButtonClick(object sender, EventArgs e)
+ {
+ this.NextSignature();
+
+ // We don't want the focus to remain with this control. We want to give it back ASAP.
+ this.FocusTheSessionView();
+ }
+
+ private void OnSurfaceElementUpButtonClick(object sender, EventArgs e)
+ {
+ this.PreviousSignature();
+
+ // We don't want the focus to remain with this control. We want to give it back ASAP.
+ this.FocusTheSessionView();
+ }
+
+ private void OnSigHelpLayoutGrid_MouseLeftButtonDown()
+ {
+ this.NextSignature();
+
+ // We don't want the focus to remain with this control. We want to give it back ASAP.
+ this.FocusTheSessionView();
+ }
+
+ private void OnSessionSignaturesChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ this.ComputePresentationSpan();
+ }
+
+ private void OnSessionViewBuffer_Changed(object sender, TextContentChangedEventArgs e)
+ {
+ // When the view buffer changes, it's possible that we could want to display the presenter on-top of the signature
+ // instead-of below it.
+ if ((_session != null) && (!_session.IsDismissed))
+ {
+ foreach (var signature in _session.Signatures)
+ {
+ SnapshotSpan applicabilitySpan =
+ signature.ApplicableToSpan.GetSpan
+ (signature.ApplicableToSpan.TextBuffer.CurrentSnapshot);
+ if (applicabilitySpan.Start.GetContainingLine().LineNumber !=
+ applicabilitySpan.End.GetContainingLine().LineNumber)
+ {
+ this.PopupStyles |= PopupStyles.PreferLeftOrTopPosition;
+ }
+ }
+ }
+ }
+
+ private void FocusTheSessionView()
+ {
+ if (_session == null)
+ {
+ return;
+ }
+
+ var textView = _session.TextView as IMdTextView;
+ if (textView != null)
+ {
+ textView.VisualElement.GrabFocus ();
+ }
+ }
+
+ private void FirePresentationSpanChanged()
+ {
+ EventHandler tempHandler = this.PresentationSpanChanged;
+ if (tempHandler != null)
+ {
+ tempHandler(this, new EventArgs());
+ }
+ }
+
+ private bool NextSignature()
+ {
+ // If there are fewer than two signatures, it doesn't make any sense to go to the next one. We shouldn't be handling
+ // keyboard events in this case.
+ if ((_session == null) || (_session.Signatures.Count <= 1))
+ {
+ return false;
+ }
+
+ if (_session.SelectedSignature == null)
+ {
+ if (_session.Signatures.Count > 0)
+ {
+ _session.SelectedSignature = _session.Signatures[0];
+ }
+ }
+ else
+ {
+ int currentIndex = _session.Signatures.IndexOf(_session.SelectedSignature);
+ if (currentIndex < (_session.Signatures.Count - 1))
+ {
+ _session.SelectedSignature = _session.Signatures[++currentIndex];
+ }
+ else if (currentIndex == (_session.Signatures.Count - 1))
+ {
+ _session.SelectedSignature = _session.Signatures[0];
+ }
+ }
+
+ return true;
+ }
+
+ private bool PreviousSignature()
+ {
+ // If there are fewer than two signatures, it doesn't make any sense to go to the next one. We shouldn't be handling
+ // keyboard events in this case.
+ if ((_session == null) || (_session.Signatures.Count <= 1))
+ {
+ return false;
+ }
+
+ if (_session.SelectedSignature == null)
+ {
+ if (_session.Signatures.Count > 0)
+ {
+ _session.SelectedSignature = _session.Signatures[0];
+ }
+ }
+ else
+ {
+ int currentIndex = _session.Signatures.IndexOf(_session.SelectedSignature);
+ if (currentIndex > 0)
+ {
+ _session.SelectedSignature = _session.Signatures[--currentIndex];
+ }
+ else if (currentIndex == 0)
+ {
+ _session.SelectedSignature = _session.Signatures[_session.Signatures.Count - 1];
+ }
+ }
+
+ return true;
+ }
+
+ private void ComputePresentationSpan()
+ {
+ if ((_session == null) || (_session.IsDismissed))
+ { return; }
+
+ // Save off the old presentation span so we can tell if it changes.
+ ITrackingSpan oldPresentationSpan = _presentationSpan;
+
+ if (_session.Signatures.Count > 0)
+ {
+ _presentationSpan = null;
+ foreach (ISignature signature in _session.Signatures)
+ {
+ _presentationSpan = IntellisenseUtilities.GetEncapsulatingSpan(_session.TextView, _presentationSpan, signature.ApplicableToSpan);
+ }
+ }
+
+ // For now, we're only interested in letting consumers know about presentation span changes if the START of our
+ // presentation span changed. That's where our display position gets calculated.
+ // TODO : Develop a better story around presentation span changes in popup presenters. If the presentation location
+ // (coordinates) doesn't really change, we shouldn't have a flicker...
+ ITextSnapshot viewSnapshot = _session.TextView.TextSnapshot;
+ SnapshotSpan newSpan = _presentationSpan.GetSpan(viewSnapshot);
+ SnapshotSpan? oldSpan = null;
+ if (oldPresentationSpan != null)
+ {
+ oldSpan = oldPresentationSpan.GetSpan(viewSnapshot);
+ }
+ if (!oldSpan.HasValue || (newSpan.Start != oldSpan.Value.Start))
+ {
+ this.FirePresentationSpanChanged();
+ }
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/DefaultSignatureHelpPresenterProvider.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/DefaultSignatureHelpPresenterProvider.cs
new file mode 100644
index 0000000000..5bb292ab30
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/DefaultSignatureHelpPresenterProvider.cs
@@ -0,0 +1,91 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Microsoft Corporation. All rights reserved.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Classification;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Utilities;
+using Microsoft.VisualStudio.Utilities;
+
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
+{
+ [Export(typeof(IIntellisensePresenterProvider))]
+ [Name("Default Signature Help Presenter")]
+ [ContentType("text")]
+ [Order]
+ internal sealed class DefaultSignatureHelpPresenterProvider :
+ MultiSessionIntellisensePresenterProvider<ISignatureHelpSession, DefaultSignatureHelpPresenter>,
+ IIntellisensePresenterProvider
+ {
+ [Export]
+ [Name("intellisense")]
+ [BaseDefinition("text")]
+ internal ContentTypeDefinition intellisenseContentType;
+
+ [Export]
+ [Name("sighelp-doc")]
+ [BaseDefinition("intellisense")]
+ internal ContentTypeDefinition sigHelpDocumentationContentType;
+
+ [Export]
+ [Name("sighelp-documentation")]
+ [BaseDefinition("text")]
+ internal ClassificationTypeDefinition sigHelpDocClassificationType;
+
+ [Export]
+ [Name("sighelp")]
+ [BaseDefinition("intellisense")]
+ internal ContentTypeDefinition sigHelpContentType;
+
+ [Export]
+ [Name("currentParam")]
+ [BaseDefinition("text")]
+ internal ClassificationTypeDefinition currentParamClassificationType;
+
+ [Import]
+ internal IContentTypeRegistryService ContentTypeRegistryService { get; set; }
+
+ [Import]
+ internal ITextBufferFactoryService TextBufferFactoryService { get; set; }
+
+ [Import]
+ internal ITextEditorFactoryService TextEditorFactoryService { get; set; }
+
+ [Import]
+ internal IEditorOptionsFactoryService EditorOptionsFactoryService { get; set; }
+
+ [Import]
+ internal GuardedOperations GuardedOperations { get; set; }
+
+#if DEBUG
+ [ImportMany]
+ internal List<Lazy<IObjectTracker>> ObjectTrackers { get; set; }
+#endif
+
+ public IIntellisensePresenter TryCreateIntellisensePresenter(IIntellisenseSession session)
+ {
+ DefaultSignatureHelpPresenter presenter = null;
+
+ // We only support signature help sessions with this presenter, and we only support sessions that actually have
+ // signatures
+ ISignatureHelpSession signatureHelpSession = session as ISignatureHelpSession;
+ if ((signatureHelpSession != null) &&
+ (signatureHelpSession.Signatures != null) &&
+ (signatureHelpSession.Signatures.Count > 0))
+ {
+ if (!this.GetOrCreatePresenter(signatureHelpSession, () => new DefaultSignatureHelpPresenter(this), out presenter))
+ {
+#if DEBUG
+ Helpers.TrackObject(this.ObjectTrackers, "Default Signature Help Presenters", presenter);
+#endif
+ }
+ }
+
+ return presenter;
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/DefaultSignatureHelpPresenterSurfaceElement.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/DefaultSignatureHelpPresenterSurfaceElement.cs
new file mode 100644
index 0000000000..6a55978934
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/DefaultSignatureHelpPresenterSurfaceElement.cs
@@ -0,0 +1,181 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Gtk;
+using Microsoft.VisualStudio.Platform;
+using MonoDevelop.Components;
+using MonoDevelop.Core;
+using MonoDevelop.Ide;
+using MonoDevelop.Ide.CodeCompletion;
+using MonoDevelop.Ide.Editor.Highlighting;
+using MonoDevelop.Ide.Fonts;
+using MonoDevelop.Ide.Gui;
+
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
+{
+ class DefaultSignatureHelpPresenterSurfaceElement
+ {
+ SignatureHelpSessionView dataContext;
+ public SignatureHelpSessionView DataContext
+ {
+ get
+ {
+ return dataContext;
+ }
+ set
+ {
+ if (value == dataContext)
+ return;
+ if (dataContext != null)
+ dataContext.PropertyChanged -= DataContext_PropertyChanged;
+ dataContext = value;
+ if (dataContext != null)
+ dataContext.PropertyChanged += DataContext_PropertyChanged;
+ DataContext_PropertyChanged (this, new System.ComponentModel.PropertyChangedEventArgs (""));
+ }
+ }
+
+ private void DataContext_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+ {
+ while (vb.Children.Length > 0)
+ vb.Remove(vb.Children[0]);
+ if (DataContext != null)
+ {
+ vb.PackStart(DataContext.SignatureWpfViewVisualElement, true, true, 0);
+ vb.PackStart(descriptionBox, true, true, 0);
+ }
+ ShowTooltipInfo();
+ }
+
+ VBox descriptionBox = new VBox(false, 0);
+ VBox vb = new VBox(false, 0);
+ VBox vb2 = new VBox(false, 0);
+ Cairo.Color foreColor;
+ public Xwt.Widget Content;
+ private XwtPopupWindowTheme Theme { get => ((XwtThemedPopup)Content.ParentWindow).Theme; }
+ public DefaultSignatureHelpPresenterSurfaceElement()
+ {
+ descriptionBox.Spacing = 4;
+
+
+ HBox hb = new HBox(false, 0);
+ hb.PackStart(vb, true, true, 0);
+
+ vb2.Spacing = 4;
+ vb2.PackStart(hb, true, true, 0);
+
+ vb2.ShowAll();
+ vb2.ParentSet += Vb2_ParentSet;
+ Content = Xwt.Toolkit.CurrentEngine.WrapWidget(vb2, Xwt.NativeWidgetSizing.DefaultPreferredSize);
+
+ Styles.Changed += HandleThemeChanged;
+ IdeApp.Preferences.ColorScheme.Changed += HandleThemeChanged;
+ Content.Disposed += Content_Disposed;
+ }
+
+ private void Vb2_ParentSet(object o, ParentSetArgs args)
+ {
+ ShowTooltipInfo();
+ }
+
+ void UpdateStyle()
+ {
+ var scheme = SyntaxHighlightingService.GetEditorTheme(IdeApp.Preferences.ColorScheme);
+ if (!scheme.FitsIdeTheme(IdeApp.Preferences.UserInterfaceTheme))
+ scheme = SyntaxHighlightingService.GetDefaultColorStyle(IdeApp.Preferences.UserInterfaceTheme);
+ Theme.SetSchemeColors(scheme);
+ Theme.Font = FontService.SansFont.CopyModified(Styles.FontScale11).ToXwtFont();
+ Theme.ShadowColor = Styles.PopoverWindow.ShadowColor;
+ foreColor = Styles.PopoverWindow.DefaultTextColor.ToCairoColor();
+
+ if (DataContext != null)
+ {
+ DataContext.SignatureWpfViewVisualElement.ModifyFg(StateType.Normal, foreColor.ToGdkColor());
+ DataContext.SignatureWpfViewVisualElement.FontDescription = FontService.GetFontDescription("Editor").CopyModified(Styles.FontScale11);
+ }
+ //if (this.Visible)
+ // QueueDraw ();
+ }
+
+ void HandleThemeChanged(object sender, EventArgs e)
+ {
+ UpdateStyle();
+ }
+
+
+ private void Content_Disposed(object sender, EventArgs e)
+ {
+ if (Content == null)
+ return;
+ Styles.Changed -= HandleThemeChanged;
+ IdeApp.Preferences.ColorScheme.Changed -= HandleThemeChanged;
+ Content.Disposed -= Content_Disposed;
+ ((XwtThemedPopup)Content.ParentWindow).PagerLeftClicked -= DownButtonClick;
+ ((XwtThemedPopup)Content.ParentWindow).PagerRightClicked -= UpButtonClick;
+ Content = null;
+ }
+ bool arrowEventsRegistered;
+ void ShowTooltipInfo()
+ {
+ if (Content.ParentWindow == null)
+ return;
+ if (DataContext == null)
+ return;
+ if (!arrowEventsRegistered)
+ {
+ UpdateStyle();
+ arrowEventsRegistered = true;
+ ((XwtThemedPopup)Content.ParentWindow).PagerLeftClicked += DownButtonClick;
+ ((XwtThemedPopup)Content.ParentWindow).PagerRightClicked += UpButtonClick;
+ }
+ Theme.NumPages = DataContext.Session.Signatures.Count;
+ Theme.CurrentPage = DataContext.Session.Signatures.IndexOf(dataContext.Session.SelectedSignature);
+
+ if (Theme.NumPages > 1)
+ {
+ Theme.DrawPager = true;
+ Theme.PagerVertical = true;
+ }
+ ClearDescriptions();
+ if (Theme.DrawPager)
+ DataContext.SignatureWpfViewVisualElement.WidthRequest = DataContext.SignatureWpfViewVisualElement.RealWidth + 70;
+
+ if (!string.IsNullOrEmpty(dataContext.Session.SelectedSignature.Documentation))
+ descriptionBox.PackStart(CreateCategory(TooltipInformationWindow.GetHeaderMarkup(GettextCatalog.GetString("Summary")), dataContext.Session.SelectedSignature.Documentation), true, true, 4);
+
+ descriptionBox.ShowAll();
+ Content.QueueForReallocate();
+ }
+
+ public event EventHandler MouseLeftButtonDown;
+ public event EventHandler UpButtonClick;
+ public event EventHandler DownButtonClick;
+
+ void CurrentTooltipInformation_Changed(object sender, EventArgs e)
+ {
+ ShowTooltipInfo();
+ }
+
+ void ClearDescriptions()
+ {
+ while (descriptionBox.Children.Length > 0)
+ {
+ var child = descriptionBox.Children[0];
+ descriptionBox.Remove(child);
+ child.Destroy();
+ }
+ }
+
+ VBox CreateCategory(string categoryName, string categoryContentMarkup)
+ {
+ return TooltipInformationWindow.CreateCategory(categoryName, categoryContentMarkup, foreColor, Theme.Font.ToPangoFont());
+ }
+
+ internal void Hide()
+ {
+ //Content.Hide ();
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/SignatureHelpBroker.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/SignatureHelpBroker.cs
new file mode 100644
index 0000000000..7b42f3cdee
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/SignatureHelpBroker.cs
@@ -0,0 +1,184 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Microsoft Corporation. All rights reserved.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel.Composition;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Utilities;
+using Microsoft.VisualStudio.Utilities;
+
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
+{
+ [Export(typeof(ISignatureHelpBroker))]
+ internal class SignatureHelpBroker : ISignatureHelpBroker
+ {
+ [ImportMany]
+ internal List<Lazy<ISignatureHelpSourceProvider, IOrderableContentTypeMetadata>> UnOrderedSignatureHelpSourceProviderExports
+ { get; set; }
+
+ [ImportMany]
+ private List<Lazy<IIntellisensePresenterProvider, IOrderableContentTypeMetadata>> UnOrderedIntellisensePresenterProviderExports
+ { get; set; }
+
+ private IList<Lazy<IIntellisensePresenterProvider, IOrderableContentTypeMetadata>> _orderedIntellisensePresenterProviderExports;
+ internal IList<Lazy<IIntellisensePresenterProvider, IOrderableContentTypeMetadata>> OrderedIntellisensePresenterProviderExports
+ {
+ get
+ {
+ if (_orderedIntellisensePresenterProviderExports == null)
+ {
+ _orderedIntellisensePresenterProviderExports = Orderer.Order(this.UnOrderedIntellisensePresenterProviderExports);
+ }
+
+ return _orderedIntellisensePresenterProviderExports;
+ }
+ }
+
+ [Import]
+ internal IIntellisenseSessionStackMapService IntellisenseSessionStackMap { get; set; }
+
+ [Import]
+ internal GuardedOperations GuardedOperations { get; set; }
+
+#if DEBUG
+ [ImportMany]
+ internal List<Lazy<IObjectTracker>> ObjectTrackers { get; set; }
+#endif
+
+ public void DismissAllSessions(ITextView textView)
+ {
+ foreach (var session in this.GetSessions(textView))
+ {
+ session.Dismiss();
+ }
+ }
+
+ public bool IsSignatureHelpActive(ITextView textView)
+ {
+ return this.GetSessions(textView).Count > 0;
+ }
+
+ public ReadOnlyCollection<ISignatureHelpSession> GetSessions(ITextView textView)
+ {
+ FrugalList<ISignatureHelpSession> tempSessionList = new FrugalList<ISignatureHelpSession>();
+
+ IIntellisenseSessionStack sessionStack = this.IntellisenseSessionStackMap.GetStackForTextView(textView);
+ if (sessionStack == null)
+ {
+ return tempSessionList.AsReadOnly();
+ }
+
+ foreach (var session in sessionStack.Sessions)
+ {
+ ISignatureHelpSession sigHelpSession = session as ISignatureHelpSession;
+ if (sigHelpSession != null)
+ {
+ tempSessionList.Add(sigHelpSession);
+ }
+ }
+
+ return tempSessionList.AsReadOnly();
+ }
+
+ public ISignatureHelpSession TriggerSignatureHelp(ITextView textView)
+ {
+ var caretPosition = textView.Caret.Position.BufferPosition;
+ return this.TriggerSignatureHelp
+ (textView,
+ caretPosition.Snapshot.CreateTrackingPoint
+ (caretPosition.Position, PointTrackingMode.Negative),
+ true);
+ }
+
+ public ISignatureHelpSession TriggerSignatureHelp(ITextView textView, ITrackingPoint triggerPoint, bool trackCaret)
+ {
+ ISignatureHelpSession session = this.CreateSignatureHelpSession(textView, triggerPoint, trackCaret);
+ session.Start();
+
+ if (session.IsDismissed)
+ {
+ return (null);
+ }
+
+ return session;
+ }
+
+ public ISignatureHelpSession CreateSignatureHelpSession(ITextView textView, ITrackingPoint triggerPoint, bool trackCaret)
+ {
+ if (triggerPoint.TextBuffer != textView.TextBuffer)
+ {
+ // Map the trigger point up to the surface buffer. That's where the session will live.
+ SnapshotPoint? surfaceTriggerSnapPoint = textView.BufferGraph.MapUpToBuffer
+ (triggerPoint.GetPoint(triggerPoint.TextBuffer.CurrentSnapshot),
+ PointTrackingMode.Negative,
+ PositionAffinity.Successor,
+ textView.TextBuffer);
+ if (!surfaceTriggerSnapPoint.HasValue)
+ {
+ // We can't trigger a session when the trigger point doesn't map to the surface buffer.
+ return null;
+ }
+ triggerPoint = surfaceTriggerSnapPoint.Value.Snapshot.CreateTrackingPoint
+ (surfaceTriggerSnapPoint.Value.Position,
+ PointTrackingMode.Negative);
+ }
+
+ // Make sure we can get ahold of a session stack.
+ IIntellisenseSessionStack sessionStack = this.IntellisenseSessionStackMap.GetStackForTextView(textView);
+ if (sessionStack == null)
+ {
+ throw new InvalidOperationException("Couldn't retrieve Intellisense Session Stack for the specified ITextView.");
+ }
+
+ SignatureHelpSession session = new SignatureHelpSession(this, textView, triggerPoint, trackCaret);
+
+#if DEBUG
+ Helpers.TrackObject(this.ObjectTrackers, "Signature Help Sessions", session);
+#endif
+
+ sessionStack.PushSession(session);
+ return session;
+ }
+
+ internal IEnumerable<ISignatureHelpSource> GetSignatureHelpSources(IIntellisenseSession session)
+ {
+ return Helpers.GetSources(session, this.CreateSourcesForBuffer);
+ }
+
+ private IList<Lazy<ISignatureHelpSourceProvider, IOrderableContentTypeMetadata>> _orderedSignatureHelpSourceProviderExports;
+ private IList<Lazy<ISignatureHelpSourceProvider, IOrderableContentTypeMetadata>> OrderedSignatureHelpSourceProviderExports
+ {
+ get
+ {
+ if (_orderedSignatureHelpSourceProviderExports == null)
+ {
+ _orderedSignatureHelpSourceProviderExports = Orderer.Order(this.UnOrderedSignatureHelpSourceProviderExports);
+ }
+
+ return _orderedSignatureHelpSourceProviderExports;
+ }
+ }
+
+ private IEnumerable<ISignatureHelpSource> CreateSourcesForBuffer(ITextBuffer buffer)
+ {
+ FrugalList<ISignatureHelpSource> sources = new FrugalList<ISignatureHelpSource>();
+ foreach(var source in this.GuardedOperations.InvokeMatchingFactories
+ (this.OrderedSignatureHelpSourceProviderExports,
+ provider => provider.TryCreateSignatureHelpSource(buffer),
+ buffer.ContentType,
+ this))
+ {
+#if DEBUG
+ Helpers.TrackObject(this.ObjectTrackers, "SignatureHelp Sources", source);
+#endif
+ sources.Add(source);
+ }
+
+ return sources;
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/SignatureHelpParameterBoldingClassfier.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/SignatureHelpParameterBoldingClassfier.cs
new file mode 100644
index 0000000000..1e1b606f15
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/SignatureHelpParameterBoldingClassfier.cs
@@ -0,0 +1,146 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Microsoft Corporation. All rights reserved.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Classification;
+using Microsoft.VisualStudio.Text.Utilities;
+
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
+{
+ internal class SignatureHelpParameterBoldingClassfier : IClassifier
+ {
+ private ITextBuffer _textBuffer;
+ private event EventHandler<ClassificationChangedEventArgs> _classificationChanged;
+ private ISignatureHelpSession _session;
+ private IClassificationTypeRegistryService _classificationTypeRegistry;
+
+ public const string UsePrettyPrintedContentKey = "UsePrettyPrintedContent";
+
+ public SignatureHelpParameterBoldingClassfier(ITextBuffer textBuffer,
+ IClassificationTypeRegistryService classificationTypeRegistry)
+ {
+ _textBuffer = textBuffer;
+ _classificationTypeRegistry = classificationTypeRegistry;
+
+ // Find the signature help session that created this text buffer.
+
+ if ((!_textBuffer.Properties.TryGetProperty<ISignatureHelpSession>(typeof(ISignatureHelpSession), out _session)) ||
+ (_session == null))
+ {
+ throw new ArgumentException
+ ("Invalid text buffer. The specified buffer wasn't created by the default signature help presenter",
+ "textBuffer");
+ }
+
+ _session.Dismissed += this.OnSession_Dismissed;
+ ((INotifyCollectionChanged)_session.Signatures).CollectionChanged += this.OnSignaturesChanged;
+ _session.SelectedSignatureChanged += this.OnSession_SelectedSignatureChanged;
+
+ if (_session.SelectedSignature != null)
+ {
+ _session.SelectedSignature.CurrentParameterChanged += this.OnSelectedSignature_CurrentParameterChanged;
+ }
+ }
+
+ public event EventHandler<ClassificationChangedEventArgs> ClassificationChanged
+ {
+ add { _classificationChanged += value; }
+ remove { _classificationChanged -= value; }
+ }
+
+ public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan trackingSpan)
+ {
+ IClassificationType currentParamType = _classificationTypeRegistry.GetClassificationType("currentParam");
+ if (currentParamType == null)
+ {
+ throw (new InvalidOperationException("Unable to retrieve 'current parameter' classification type"));
+ }
+
+ bool usePrettyPrintedContent;
+ if (!_textBuffer.Properties.TryGetProperty<bool>(SignatureHelpParameterBoldingClassfier.UsePrettyPrintedContentKey, out usePrettyPrintedContent))
+ {
+ usePrettyPrintedContent = false;
+ }
+
+ FrugalList<ClassificationSpan> classSpans = new FrugalList<ClassificationSpan>();
+ if ((_session != null) &&
+ (_session.SelectedSignature != null) &&
+ (_session.SelectedSignature.CurrentParameter != null))
+ {
+ Span span = usePrettyPrintedContent ?
+ _session.SelectedSignature.CurrentParameter.PrettyPrintedLocus :
+ _session.SelectedSignature.CurrentParameter.Locus;
+
+ if ((trackingSpan.IntersectsWith(span)) &&
+ (span.End <= _textBuffer.CurrentSnapshot.Length))
+ {
+ // We only have one classification span to add, and that's the classification span for the current parameter.
+
+ classSpans.Add
+ (new ClassificationSpan
+ (new SnapshotSpan(_textBuffer.CurrentSnapshot, span),
+ currentParamType));
+ }
+ }
+
+ return classSpans;
+ }
+
+ private void OnSignaturesChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ this.FireClassificationChanged
+ (new SnapshotSpan(_textBuffer.CurrentSnapshot, 0, _textBuffer.CurrentSnapshot.Length));
+ }
+
+ private void OnSession_SelectedSignatureChanged(object sender, SelectedSignatureChangedEventArgs e)
+ {
+ // If the selected signature changed, we should subscribe to current parameter change notification on this new
+ // selected signature.
+
+ if (e.PreviousSelectedSignature != null)
+ {
+ e.PreviousSelectedSignature.CurrentParameterChanged -= this.OnSelectedSignature_CurrentParameterChanged;
+ }
+
+ if (e.NewSelectedSignature != null)
+ {
+ e.NewSelectedSignature.CurrentParameterChanged += this.OnSelectedSignature_CurrentParameterChanged;
+ }
+
+ this.FireClassificationChanged
+ (new SnapshotSpan(_textBuffer.CurrentSnapshot, 0, _textBuffer.CurrentSnapshot.Length));
+ }
+
+ private void OnSelectedSignature_CurrentParameterChanged(object sender, CurrentParameterChangedEventArgs e)
+ {
+ this.FireClassificationChanged
+ (new SnapshotSpan(_textBuffer.CurrentSnapshot, 0, _textBuffer.CurrentSnapshot.Length));
+ }
+
+ private void OnSession_Dismissed(object sender, EventArgs e)
+ {
+ _session.Dismissed -= this.OnSession_Dismissed;
+ ((INotifyCollectionChanged)_session.Signatures).CollectionChanged -= this.OnSignaturesChanged;
+ _session.SelectedSignatureChanged -= this.OnSession_SelectedSignatureChanged;
+
+ if (_session.SelectedSignature != null)
+ {
+ _session.SelectedSignature.CurrentParameterChanged -= this.OnSelectedSignature_CurrentParameterChanged;
+ }
+ _session = null;
+ }
+
+ private void FireClassificationChanged(SnapshotSpan changeSpan)
+ {
+ EventHandler<ClassificationChangedEventArgs> tempHandler = _classificationChanged;
+ if (tempHandler != null)
+ {
+ tempHandler(this, new ClassificationChangedEventArgs(changeSpan));
+ }
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/SignatureHelpSession.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/SignatureHelpSession.cs
new file mode 100644
index 0000000000..57f305af61
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/SignatureHelpSession.cs
@@ -0,0 +1,255 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Microsoft Corporation. All rights reserved.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
+{
+ internal class SignatureHelpSession : IntellisenseSession, ISignatureHelpSession
+ {
+ private SignatureHelpBroker _componentContext;
+ private bool _trackCaret;
+
+ private BulkObservableCollection<ISignature> _activeSignatures = new BulkObservableCollection<ISignature>();
+ private ReadOnlyObservableCollection<ISignature> _readOnlySignatures;
+ private bool _isDismissed = false;
+ private ISignature _selectedSignature;
+
+
+ public SignatureHelpSession(SignatureHelpBroker componentContext,
+ ITextView textView,
+ ITrackingPoint triggerPoint,
+ bool trackCaret)
+ : base(textView, triggerPoint)
+ {
+ _componentContext = componentContext;
+
+ // If we were asked to track the caret, go ahead and subscribe to the event that tracks its movement.
+
+ _trackCaret = trackCaret;
+ if (_trackCaret)
+ {
+ base.textView.Caret.PositionChanged += new EventHandler<CaretPositionChangedEventArgs>(OnCaretPositionChanged);
+ }
+ }
+
+ public ReadOnlyObservableCollection<ISignature> Signatures
+ {
+ get
+ {
+ if (_readOnlySignatures == null)
+ {
+ _readOnlySignatures = new ReadOnlyObservableCollection<ISignature>(_activeSignatures);
+ }
+
+ return _readOnlySignatures;
+ }
+ }
+
+ public ISignature SelectedSignature
+ {
+ get
+ {
+ return (_selectedSignature);
+ }
+ set
+ {
+ if (value != _selectedSignature)
+ {
+ if (_activeSignatures.Contains(value))
+ {
+ ISignature prevSelectedSignature = _selectedSignature;
+ _selectedSignature = value;
+ this.RaiseSelectedSignatureChanged(prevSelectedSignature, _selectedSignature);
+ return;
+ }
+
+ throw (new ArgumentException("Invalid signature. Cannot set selection to signature that doesn't exist in the set of signatures for this session."));
+ }
+ }
+ }
+
+ public event EventHandler<SelectedSignatureChangedEventArgs> SelectedSignatureChanged;
+
+ public override bool Match()
+ {
+ foreach (ISignatureHelpSource provider in _componentContext.GetSignatureHelpSources(this))
+ {
+ ISignature bestMatch = provider.GetBestMatch(this);
+ if (bestMatch != null)
+ {
+ this.SelectedSignature = bestMatch;
+ return (true);
+ }
+ }
+
+ return (false);
+ }
+
+ public override void Start()
+ {
+ this.Recalculate();
+ }
+
+ public override void Recalculate()
+ {
+ if (_isDismissed)
+ {
+ throw (new InvalidOperationException("Cannot recalculate. The session is dismissed."));
+ }
+
+ // Clear out the active set of signatures.
+ _activeSignatures.Clear();
+
+ // In order to find the signatures to be displayed in this session, we'll need to go through each of the signature help
+ // providers and ask them for signatures at the trigger point.
+
+ List<ISignature> signatures = new List<ISignature>();
+ foreach (ISignatureHelpSource provider in _componentContext.GetSignatureHelpSources(this))
+ {
+ provider.AugmentSignatureHelpSession(this, signatures);
+
+ // The source could have done anything to the session. Let's verify that we're still alive and well.
+ if (this.IsDismissed)
+ {
+ return;
+ }
+ }
+
+ if (signatures != null)
+ {
+ _activeSignatures.AddRange(signatures);
+ }
+
+ // If we ended-up with zero signatures, that's it. We need to dismiss and get out of here.
+ if (_activeSignatures.Count == 0)
+ {
+ this.Dismiss();
+ return;
+ }
+
+ // Let's determine which signature should be "selected" before we find a presenter.
+ if (!_activeSignatures.Contains(_selectedSignature))
+ {
+ // The old selection is no longer valid (or we never had one). Let's select the first signature.
+ this.SelectedSignature = _activeSignatures[0];
+ }
+
+ // If we don't already have a presenter for this session, find one.
+ if (base.presenter == null)
+ {
+ base.presenter = FindPresenter(this, _componentContext.OrderedIntellisensePresenterProviderExports, _componentContext.GuardedOperations);
+ if (base.presenter != null)
+ {
+ this.RaisePresenterChanged();
+ }
+ else
+ {
+ this.Dismiss();
+ return;
+ }
+ }
+
+ base.RaiseRecalculated();
+ }
+
+ public override void Dismiss()
+ {
+ if (!_isDismissed)
+ {
+ // Stop listening to the caret movement event
+
+ if (_trackCaret)
+ {
+ base.textView.Caret.PositionChanged -= new EventHandler<CaretPositionChangedEventArgs>(OnCaretPositionChanged);
+ }
+
+ _isDismissed = true;
+
+ // Fire the "Dismissed" event. This will signal consumers (namely, the session stack) to release pointers to this
+ // session and let us die a peaceful death of garbage collection
+
+ base.RaiseDismissed();
+
+ // Now that we're dismissed, there's no reason to hang-on to the rest of this stuff.
+ _componentContext = null;
+ _activeSignatures = null;
+ _readOnlySignatures = null;
+ _selectedSignature = null;
+
+ base.Dismiss();
+ }
+ }
+
+ public override bool IsDismissed
+ {
+ get { return (_isDismissed); }
+ }
+
+ public override void Collapse()
+ {
+ this.Dismiss();
+ }
+
+ void OnCaretPositionChanged(object sender, CaretPositionChangedEventArgs e)
+ {
+ // It is possible that one of the previous Caret.PositionChanged event handlers dismissed
+ // the session and removed this handler (and null'd all data) from the event. In that case
+ // we should simply ignore this call.
+ if (_isDismissed)
+ {
+ return;
+ }
+
+ // Let's make sure that the caret is still inside the applicable-to for each of the signatures. We'll want to remove
+ // any completions that aren't applicable.
+
+ ITextSnapshot snapshot = this.TextView.TextBuffer.CurrentSnapshot;
+ SnapshotSpan caretSurfaceSpan = new SnapshotSpan(snapshot, this.TextView.Caret.Position.BufferPosition.Position, 0);
+
+ List<ISignature> signaturesToRemove = new List<ISignature>();
+ if (null != this.Signatures)
+ {
+ foreach (ISignature signature in this.Signatures)
+ {
+ var signatureSurfaceSpans = this.TextView.BufferGraph.MapUpToBuffer
+ (signature.ApplicableToSpan.GetSpan(signature.ApplicableToSpan.TextBuffer.CurrentSnapshot),
+ signature.ApplicableToSpan.TrackingMode,
+ this.TextView.TextBuffer);
+ foreach (var signatureSurfaceSpan in signatureSurfaceSpans)
+ {
+ if (!signatureSurfaceSpan.IntersectsWith(caretSurfaceSpan))
+ {
+ signaturesToRemove.Add(signature);
+ }
+ }
+ }
+ }
+
+ foreach (ISignature sigToRemove in signaturesToRemove)
+ {
+ _activeSignatures.Remove(sigToRemove);
+ }
+
+ // If we rejected all of the signatures, we'll want to make sure to dismiss the session
+ if (_activeSignatures.Count == 0)
+ {
+ this.Dismiss();
+ }
+ }
+
+ private void RaiseSelectedSignatureChanged(ISignature oldSelectedSignature, ISignature newSelectedSignature)
+ {
+ EventHandler<SelectedSignatureChangedEventArgs> tempHandler = this.SelectedSignatureChanged;
+ if (tempHandler != null)
+ {
+ tempHandler(this, new SelectedSignatureChangedEventArgs(oldSelectedSignature, newSelectedSignature));
+ }
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/SignatureHelpSessionView.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/SignatureHelpSessionView.cs
new file mode 100644
index 0000000000..b0a4de6cab
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/Intellisense/SignatureHelp/SignatureHelpSessionView.cs
@@ -0,0 +1,473 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Microsoft Corporation. All rights reserved.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+using System;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Globalization;
+using Gtk;
+using Microsoft.VisualStudio.Platform;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Utilities;
+using MonoDevelop.Components;
+using MonoDevelop.Core;
+
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
+{
+ internal sealed class SignatureHelpSessionView : IDisposable, INotifyPropertyChanged
+ {
+ private DefaultSignatureHelpPresenterProvider _componentContext;
+ private ISignatureHelpSession _session;
+ private string _pagerText;
+ private bool _pagerVisibility = false;
+ private ITextBuffer _signatureTextBuffer;
+ private MonoDevelop.Components.FixedWidthWrapLabel _signatureWpfTextView;
+ private string _signatureDocumentation;
+ private bool _signatureDocumentationVisibility;
+ private string _currentParameterName;
+ private string _currentParameterDocumentation;
+ private bool _currentParameterVisibility;
+ private IContentType _textContentType;
+ private double _pagerWidth;
+ private bool _isAutoSizePending = false;
+
+ internal SignatureHelpSessionView(DefaultSignatureHelpPresenterProvider componentContext)
+ {
+ _componentContext = componentContext;
+ }
+
+ public ISignatureHelpSession Session
+ {
+ get { return _session; }
+ set
+ {
+ this.UnBindFromSession();
+ _session = value;
+ this.BindToSession();
+ }
+ }
+
+ public string PagerText
+ {
+ get { return _pagerText; }
+ private set
+ {
+ _pagerText = value;
+ this.RaisePropertyChanged("PagerText");
+ }
+ }
+
+ public bool PagerVisibility
+ {
+ get { return _pagerVisibility; }
+ private set
+ {
+ _pagerVisibility = value;
+ this.RaisePropertyChanged("PagerVisibility");
+ }
+ }
+
+ public FixedWidthWrapLabel SignatureWpfViewVisualElement { get { return _signatureWpfTextView; } }
+
+ public string SignatureDocumentation
+ {
+ get { return _signatureDocumentation; }
+ private set
+ {
+ _signatureDocumentation = value;
+ this.RaisePropertyChanged("SignatureDocumentation");
+ }
+ }
+
+ public bool SignatureDocumentationVisibility
+ {
+ get { return _signatureDocumentationVisibility; }
+ private set
+ {
+ _signatureDocumentationVisibility = value;
+ this.RaisePropertyChanged("SignatureDocumentationVisibility");
+ }
+ }
+
+ public string CurrentParameterName
+ {
+ get { return _currentParameterName; }
+ private set
+ {
+ _currentParameterName = value;
+ this.RaisePropertyChanged("CurrentParameterName");
+ }
+ }
+
+ public string CurrentParameterDocumentation
+ {
+ get { return _currentParameterDocumentation; }
+ private set
+ {
+ _currentParameterDocumentation = value;
+ this.RaisePropertyChanged("CurrentParameterDocumentation");
+ }
+ }
+
+ public bool CurrentParameterVisibility
+ {
+ get { return _currentParameterVisibility; }
+ private set
+ {
+ _currentParameterVisibility = value;
+ this.RaisePropertyChanged("CurrentParameterVisibility");
+ }
+ }
+
+ public double ContainerMaxWidth
+ {
+ get
+ {
+ return Helpers.GetScreenRect(this.Session).Width * 0.9;
+ }
+ }
+
+ public double ContainerMaxHeight
+ {
+ get
+ {
+ return Helpers.GetScreenRect(this.Session).Height * 0.5;
+ }
+ }
+
+ public double CurrentParameterNameMaxWidth
+ {
+ get
+ {
+ return Helpers.GetScreenRect(this.Session).Width * 0.2;
+ }
+ }
+
+ public double PagerWidth
+ {
+ get
+ {
+ return _pagerWidth;
+ }
+ set
+ {
+ _pagerWidth = value;
+
+ this.AutoSizeSignatureTextView();
+ }
+ }
+
+ public void Dispose()
+ {
+ this.UnBindFromSession();
+ _signatureWpfTextView.Dispose();
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ private void OnSession_SelectedSignatureChanged(object sender, SelectedSignatureChangedEventArgs e)
+ {
+ // Stop listening to CurrentParameterChanged on the old selected signature, and start listening on the new selected
+ // signature.
+ if (e.PreviousSelectedSignature != null)
+ {
+ e.PreviousSelectedSignature.CurrentParameterChanged -= this.OnSelectedSignature_CurrentParameterChanged;
+ }
+ if (e.NewSelectedSignature != null)
+ {
+ e.NewSelectedSignature.CurrentParameterChanged += this.OnSelectedSignature_CurrentParameterChanged;
+ }
+
+ // Since the selected signature changed, we need to update the signature info being displayed.
+ this.UpdateSignatureInfo();
+ }
+
+ private void OnSelectedSignature_CurrentParameterChanged(object sender, CurrentParameterChangedEventArgs e)
+ {
+ this.UpdateParameterInfo();
+ }
+
+ private void OnSignatures_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ this.UpdateSignatureInfo();
+ }
+
+ private void CreateSignatureTextView()
+ {
+ IEditorOptions options = _componentContext.EditorOptionsFactoryService.CreateOptions();
+ _signatureTextBuffer = _componentContext.TextBufferFactoryService.CreateTextBuffer();
+ _signatureWpfTextView = new MonoDevelop.Components.FixedWidthWrapLabel();
+ _signatureWpfTextView.Indent = -20;
+
+ _signatureWpfTextView.Wrap = Pango.WrapMode.WordChar;
+ _signatureWpfTextView.BreakOnCamelCasing = false;
+ _signatureWpfTextView.BreakOnPunctuation = false;
+ }
+
+ private void BindToSession()
+ {
+ // No work to do if we don't have a session
+ if ((_session == null) || (_session.IsDismissed))
+ {
+ return;
+ }
+
+ // Create the signature text view (if it hasn't already been created)
+ if (_signatureWpfTextView == null)
+ {
+ this.CreateSignatureTextView();
+ }
+
+ // Subscribe to a few events
+ _session.SelectedSignatureChanged += this.OnSession_SelectedSignatureChanged;
+ if (_session.SelectedSignature != null)
+ {
+ _session.SelectedSignature.CurrentParameterChanged += this.OnSelectedSignature_CurrentParameterChanged;
+ }
+ ((INotifyCollectionChanged)_session.Signatures).CollectionChanged += this.OnSignatures_CollectionChanged;
+
+ // Ensure we watch the view for layouts and resize it to ensure its contents fit
+ _signatureWpfTextView.SizeAllocated += this.OnTextView_LayoutChanged;
+
+ // Add the session as a property to the signature help text buffer. This will be used by the current parameter bolding
+ // classifier.
+ _signatureTextBuffer.Properties.AddProperty(typeof(ISignatureHelpSession), _session);
+
+ // Start the process of updating our view model to match the selected signature for this newly-bound session.
+ this.UpdateSignatureInfo();
+ }
+
+ private void OnTextView_LayoutChanged(object o, SizeAllocatedArgs args)
+ {
+ AutoSizeSignatureTextView();
+ }
+
+ private void UnBindFromSession()
+ {
+ // Don't do anything with an invalid session (dismissed session is ok).
+ if (_session == null)
+ {
+ return;
+ }
+
+ // Unsubscribe from some events.
+ _session.SelectedSignatureChanged -= this.OnSession_SelectedSignatureChanged;
+ if (_session.SelectedSignature != null)
+ {
+ _session.SelectedSignature.CurrentParameterChanged -= this.OnSelectedSignature_CurrentParameterChanged;
+ }
+ ((INotifyCollectionChanged)_session.Signatures).CollectionChanged -= this.OnSignatures_CollectionChanged;
+
+ _signatureWpfTextView.SizeAllocated -= this.OnTextView_LayoutChanged;
+
+ this.PagerVisibility = false;
+ this.SignatureDocumentationVisibility = false;
+ this.CurrentParameterVisibility = false;
+
+ // Make sure that our property added to the signature text buffer is released. Also, the buffer should be empty.
+ _signatureTextBuffer.Replace(new Span(0, _signatureTextBuffer.CurrentSnapshot.Length), string.Empty);
+ _signatureTextBuffer.Properties.RemoveProperty(typeof(ISignatureHelpSession));
+
+ // Change the content-type of our signature text buffer back to text. This is how we'll ensure that our classifiers
+ // always get run. When we change the content type when binding to a session, our classifiers will be re-run.
+ _signatureTextBuffer.ChangeContentType(this.TextContentType, null);
+
+ // Null-out our ref to the session.
+ _session = null;
+ }
+
+ private void RaisePropertyChanged(string propertyName)
+ {
+ PropertyChangedEventHandler tempHandler = this.PropertyChanged;
+ if (tempHandler != null)
+ {
+ tempHandler(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
+ }
+ }
+
+ private void UpdateSignatureInfo()
+ {
+ // If the session is in a bad state, just don't update.
+ Debug.Assert(_session != null, "How'd we get a null session?");
+ if ((_session == null) || _session.IsDismissed || (_session.Signatures.Count < 1))
+ {
+ return;
+ }
+
+ // Determine which signature we should be rendering. This is usually the selected signature, but if we run up against
+ // a session with no selected signature, just use the first one.
+ ISignature sigToRender = _session.SelectedSignature != null ? _session.SelectedSignature : _session.Signatures[0];
+
+ // Make sure the content type of the signature text buffer is correct.
+ IContentType desiredContentType = this.GetSignatureHelpContentType(sigToRender);
+ if (_signatureTextBuffer.ContentType != desiredContentType)
+ {
+ _signatureTextBuffer.ChangeContentType(desiredContentType, null);
+ }
+
+ // Update the text in the signature text buffer to match the new signature being displayed.
+ this.DisplayContent(sigToRender);
+
+ // If all the content didn't fit in one line, then try the pretty printed content unless it's null (see Dev11 #376399)
+ if (_signatureWpfTextView.RealWidth > this.ContainerMaxWidth - this.PagerWidth - 4 &&
+ sigToRender.PrettyPrintedContent != null)
+ {
+ this.DisplayPrettyPrintedContent(sigToRender);
+
+ // Ensure that the pretty printed content fits, otherwise, switch back to the regular content as a last resort
+ if (_signatureWpfTextView.RealWidth > this.ContainerMaxWidth - this.PagerWidth - 4)
+ {
+ this.DisplayContent(sigToRender);
+ }
+ }
+
+ // Update the documentation of the signature to match.
+ if (!string.IsNullOrEmpty(sigToRender.Documentation))
+ {
+ this.SignatureDocumentation = sigToRender.Documentation;
+ this.SignatureDocumentationVisibility = true;
+ }
+ else
+ {
+ this.SignatureDocumentationVisibility = false;
+ this.SignatureDocumentation = string.Empty;
+ }
+
+ // See if the pager should be enabled. It should show up only if there are at least 2 signatures.
+ if (this.Session.Signatures.Count > 1)
+ {
+ // The pager should be enabled.
+ int selectionIndex = this.Session.Signatures.IndexOf(sigToRender);
+ this.PagerText = GettextCatalog.GetString("{0} of {1}",
+ selectionIndex + 1,
+ this.Session.Signatures.Count);
+ this.PagerVisibility = true;
+ }
+ else
+ {
+ // The pager should not be enabled.
+ this.PagerVisibility = false;
+ this.PagerText = string.Empty;
+ }
+
+ // Whenever the signature information is being updated, chances are we have to update the parameter info as well.
+ this.UpdateParameterInfo();
+ }
+
+ private void DisplayContent(ISignature sigToRender)
+ {
+ // Ensure word-wrap is off initially. We only turn word-wrap on if we are forced to due to lack of
+ // space on the screen
+ _signatureTextBuffer.Properties[SignatureHelpParameterBoldingClassfier.UsePrettyPrintedContentKey] = false;
+ _signatureTextBuffer.Replace(new Span(0, _signatureTextBuffer.CurrentSnapshot.Length), sigToRender.Content);
+ var classifierSpans = PlatformCatalog.Instance.ClassifierAggregatorService.GetClassifier(_signatureTextBuffer)?.GetClassificationSpans(new SnapshotSpan(_signatureTextBuffer.CurrentSnapshot, 0, _signatureTextBuffer.CurrentSnapshot.Length));
+ if (classifierSpans != null && classifierSpans.Count > 0)
+ _signatureWpfTextView.Markup = MDUtils.ClassificationsToMarkup(_signatureTextBuffer.CurrentSnapshot, classifierSpans, sigToRender.CurrentParameter?.Locus);
+ else
+ {
+ _signatureWpfTextView.Markup = MonoDevelop.Ide.TypeSystem.Ambience.EscapeText(_signatureTextBuffer.CurrentSnapshot.GetText());
+ LoggingService.LogWarning("No classification spans found for signature helper:" + _signatureTextBuffer.ContentType);
+ }
+ _signatureWpfTextView.Visible = true;
+ }
+
+ private void DisplayPrettyPrintedContent(ISignature sigToRender)
+ {
+ Debug.Assert(sigToRender.PrettyPrintedContent != null, "We shouldn't try to display null PrettyPrintedContent.");
+ // Ensure word-wrap is off initially. We only turn word-wrap on if we are forced to due to lack of
+ // space on the screen
+ _signatureTextBuffer.Properties[SignatureHelpParameterBoldingClassfier.UsePrettyPrintedContentKey] = true;
+ _signatureTextBuffer.Replace(new Span(0, _signatureTextBuffer.CurrentSnapshot.Length), sigToRender.PrettyPrintedContent);
+ var classifierSpans = PlatformCatalog.Instance.ClassifierAggregatorService.GetClassifier(_signatureTextBuffer)?.GetClassificationSpans(new SnapshotSpan(_signatureTextBuffer.CurrentSnapshot, 0, _signatureTextBuffer.CurrentSnapshot.Length));
+ if (classifierSpans != null && classifierSpans.Count > 0)
+ _signatureWpfTextView.Markup = MDUtils.ClassificationsToMarkup(_signatureTextBuffer.CurrentSnapshot, classifierSpans, sigToRender.CurrentParameter?.Locus);
+ else
+ {
+ _signatureWpfTextView.Markup = MonoDevelop.Ide.TypeSystem.Ambience.EscapeText(_signatureTextBuffer.CurrentSnapshot.GetText());
+ LoggingService.LogWarning("No classification spans found for signature helper:" + _signatureTextBuffer.ContentType);
+ }
+ _signatureWpfTextView.Visible = true;
+ }
+
+ private void UpdateParameterInfo()
+ {
+ // If the session is in a bad state, just don't update.
+ Debug.Assert(_session != null, "How'd we get a null session?");
+ if ((_session == null) || _session.IsDismissed || (_session.Signatures.Count < 1))
+ {
+ return;
+ }
+
+ // Determine which parameter is the current one.
+ IParameter currentParam = _session.SelectedSignature != null
+ ? _session.SelectedSignature.CurrentParameter
+ : _session.Signatures[0].CurrentParameter;
+
+ // Should the "current parameter" section even be enabled? Not if either the name or documentation is empty.
+ if ((currentParam == null) ||
+ string.IsNullOrEmpty(currentParam.Name) ||
+ string.IsNullOrEmpty(currentParam.Documentation))
+ {
+ this.CurrentParameterVisibility = false;
+ this.CurrentParameterName = string.Empty;
+ this.CurrentParameterDocumentation = string.Empty;
+ }
+ else
+ {
+ this.CurrentParameterName = GettextCatalog.GetString("{0}:",
+ currentParam.Name);
+ this.CurrentParameterDocumentation = currentParam.Documentation;
+ this.CurrentParameterVisibility = true;
+ }
+ }
+
+ private IContentType TextContentType
+ {
+ get
+ {
+ if (_textContentType == null)
+ {
+ _textContentType = _componentContext.ContentTypeRegistryService.GetContentType("text");
+ }
+
+ return _textContentType;
+ }
+ }
+
+ private IContentType GetSignatureHelpContentType(ISignature signature)
+ {
+ if ((_session == null) || (_session.IsDismissed))
+ {
+ throw new InvalidOperationException("We can't determine the signature help content type without a session");
+ }
+
+ IContentType baseContentType = signature.ApplicableToSpan.TextBuffer.ContentType;
+ string newContentTypeName = string.Format(CultureInfo.InvariantCulture, "{0} Signature Help", baseContentType.TypeName);
+ //TODO: Workaround VS bug
+ if (newContentTypeName == "HTMLXProjection Signature Help")
+ newContentTypeName = "CSharp Signature Help";
+ IContentType sigHelpContentType = _componentContext.ContentTypeRegistryService.GetContentType(newContentTypeName);
+
+ if (sigHelpContentType == null)
+ {
+ string[] baseContentTypeNames = new string[] { "sighelp" };
+
+ sigHelpContentType = _componentContext.ContentTypeRegistryService.AddContentType
+ (newContentTypeName,
+ baseContentTypeNames);
+ }
+
+ return sigHelpContentType;
+ }
+
+ private void AutoSizeSignatureTextView()
+ {
+ //TODO
+ //Helpers.AutoSizeTextView(_signatureWpfTextView, this.ContainerMaxWidth - this.PagerWidth - 4 /* 4px of Margin */, this.ContainerMaxHeight);
+ _isAutoSizePending = false;
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/QuickInfo/AsyncQuickInfoBroker.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/QuickInfo/AsyncQuickInfoBroker.cs
new file mode 100644
index 0000000000..0896238889
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/QuickInfo/AsyncQuickInfoBroker.cs
@@ -0,0 +1,148 @@
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
+{
+ using System;
+ using System.Collections.Generic;
+ using System.ComponentModel.Composition;
+ using System.Diagnostics;
+ using System.Linq;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using Microsoft.Internal.VisualStudio.Language.Intellisense;
+ using Microsoft.VisualStudio.Language.Utilities;
+ using Microsoft.VisualStudio.Text;
+ using Microsoft.VisualStudio.Text.Adornments;
+ using Microsoft.VisualStudio.Text.Editor;
+ using Microsoft.VisualStudio.Text.Utilities;
+ using Microsoft.VisualStudio.Threading;
+ using Microsoft.VisualStudio.Utilities;
+
+ [Export(typeof(IAsyncQuickInfoBroker))]
+ internal sealed class AsyncQuickInfoBroker : IAsyncQuickInfoBroker
+ {
+ private readonly IEnumerable<Lazy<IAsyncQuickInfoSourceProvider, IOrderableContentTypeMetadata>> unorderedSourceProviders;
+ private readonly IGuardedOperations guardedOperations;
+ private readonly IToolTipService toolTipService;
+ private readonly JoinableTaskContext joinableTaskContext;
+ private IEnumerable<Lazy<IAsyncQuickInfoSourceProvider, IOrderableContentTypeMetadata>> orderedSourceProviders;
+
+ [ImportingConstructor]
+ public AsyncQuickInfoBroker(
+ [ImportMany]IEnumerable<Lazy<IAsyncQuickInfoSourceProvider, IOrderableContentTypeMetadata>> unorderedSourceProviders,
+ IGuardedOperations guardedOperations,
+ IToolTipService toolTipService,
+ JoinableTaskContext joinableTaskContext)
+ {
+ // Bug #512117: Remove compatibility shims for 2nd gen. Quick Info APIs.
+ // Combines new + legacy providers into a single series for relative ordering.
+ var combinedProviders = unorderedSourceProviders ?? throw new ArgumentNullException(nameof(unorderedSourceProviders));
+
+ this.unorderedSourceProviders = combinedProviders;
+#pragma warning restore 618
+ this.guardedOperations = guardedOperations ?? throw new ArgumentNullException(nameof(guardedOperations));
+ this.joinableTaskContext = joinableTaskContext ?? throw new ArgumentNullException(nameof(joinableTaskContext));
+ this.toolTipService = toolTipService;
+ }
+
+ #region IAsyncQuickInfoBroker
+
+ public IAsyncQuickInfoSession GetSession(ITextView textView)
+ {
+ if (textView.Properties.TryGetProperty(typeof(AsyncQuickInfoSession), out AsyncQuickInfoSession property))
+ {
+ return property;
+ }
+
+ return null;
+ }
+
+ public bool IsQuickInfoActive(ITextView textView) => GetSession(textView) != null;
+
+ public async Task<IAsyncQuickInfoSession> TriggerQuickInfoAsync(
+ ITextView textView,
+ ITrackingPoint triggerPoint,
+ QuickInfoSessionOptions options,
+ CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ // Caret element requires UI thread.
+ await this.joinableTaskContext.Factory.SwitchToMainThreadAsync();
+
+ // We switched threads and there is some latency, so ensure that we're still not canceled.
+ cancellationToken.ThrowIfCancellationRequested();
+
+ // Dismiss any currently open session.
+ var currentSession = this.GetSession(textView);
+ if (currentSession != null)
+ {
+ await currentSession.DismissAsync();
+ }
+
+ // Get the trigger point from the caret if none is provided.
+ triggerPoint = triggerPoint ?? textView.TextSnapshot.CreateTrackingPoint(
+ textView.Caret.Position.BufferPosition,
+ PointTrackingMode.Negative);
+
+ var newSession = new AsyncQuickInfoSession(
+ this.OrderedSourceProviders,
+ this.guardedOperations,
+ this.joinableTaskContext,
+ this.toolTipService,
+ textView,
+ triggerPoint,
+ options,
+ null);
+
+ // StartAsync() is responsible for dispatching a StateChange
+ // event if canceled so no need to clean these up on cancellation.
+ newSession.StateChanged += this.OnStateChanged;
+ textView.Properties.AddProperty(typeof(AsyncQuickInfoSession), newSession);
+
+ try
+ {
+ await newSession.StartAsync(cancellationToken);
+ }
+ catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested)
+ {
+ // Don't throw OperationCanceledException unless the caller canceled us.
+ // This can happen if computation was canceled by a quick info source
+ // dismissing the session during computation, which we want to consider
+ // more of a 'N/A' than an error.
+ return null;
+ }
+
+ return newSession;
+ }
+
+ #endregion
+
+ #region Private Impl
+
+ // Bug #512117: Remove compatibility shims for 2nd gen. Quick Info APIs.
+ // This interface exists only to expose additional functionality required by the shims.
+#pragma warning disable 618
+ private IEnumerable<Lazy<IAsyncQuickInfoSourceProvider, IOrderableContentTypeMetadata>> OrderedSourceProviders
+ => this.orderedSourceProviders ?? (this.orderedSourceProviders = Orderer.Order(this.unorderedSourceProviders));
+#pragma warning restore 618
+
+ // Listens for the session being dismissed so that we can remove it from the view's property bag.
+ private void OnStateChanged(object sender, QuickInfoSessionStateChangedEventArgs e)
+ {
+ IntellisenseUtilities.ThrowIfNotOnMainThread(this.joinableTaskContext);
+
+ if (e.NewState == QuickInfoSessionState.Dismissed)
+ {
+ if (sender is AsyncQuickInfoSession session)
+ {
+ session.TextView.Properties.RemoveProperty(typeof(AsyncQuickInfoSession));
+ session.StateChanged -= this.OnStateChanged;
+ return;
+ }
+
+ Debug.Fail("Unexpected sender type");
+ }
+ }
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/QuickInfo/AsyncQuickInfoSession.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/QuickInfo/AsyncQuickInfoSession.cs
new file mode 100644
index 0000000000..da67867c20
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/QuickInfo/AsyncQuickInfoSession.cs
@@ -0,0 +1,535 @@
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.Immutable;
+ using System.Collections.ObjectModel;
+ using System.Diagnostics;
+ using System.Linq;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using Microsoft.Internal.VisualStudio.Language.Intellisense;
+ using Microsoft.VisualStudio.Language.Intellisense;
+ using Microsoft.VisualStudio.Language.Utilities;
+ using Microsoft.VisualStudio.Text;
+ using Microsoft.VisualStudio.Text.Adornments;
+ using Microsoft.VisualStudio.Text.Editor;
+ using Microsoft.VisualStudio.Text.Utilities;
+ using Microsoft.VisualStudio.Threading;
+ using Microsoft.VisualStudio.Utilities;
+
+ internal sealed class AsyncQuickInfoSession : IAsyncQuickInfoSession
+ {
+ private readonly IEnumerable<Lazy<IAsyncQuickInfoSourceProvider, IOrderableContentTypeMetadata>> orderedSourceProviders;
+
+ private readonly IGuardedOperations guardedOperations;
+ private readonly IToolTipService toolTipService;
+ private readonly JoinableTaskContext joinableTaskContext;
+ private readonly ITrackingPoint triggerPoint;
+
+ // For the purposes of synchronization, state updates are non-atomic and 'Calculating'
+ // state is considered to be transient. The properties of the object can be updated
+ // individually and are immediately visible to all threads. External extenders are
+ // essentially not impacted by this lack of atomicity because they only have a reference
+ // to the session from the broker after it is finished calculating, and the non-atomic
+ // updating of the properties happens after all IAsyncQuickInfoSources have returned.
+ // This class avoids its own races by marshalling all calls into the class and all state
+ // changes through the UI thread and by only allowing one invocation of 'StartAsync()'.
+
+ #region Cross Thread Readable, Modifiable
+
+ // All state in this region can be read or modified from any thread and must
+ // be accessed with VOLATILE.READ() + VOLATILE.WRITE().
+ private ImmutableList<object> content = ImmutableList<object>.Empty;
+ private ITrackingSpan applicableToSpan;
+
+ #endregion
+
+ #region Cross Thread Readable, Modifiable Only Via UI Thread
+
+ // State in this region can be read from any thread at any time (often via properties)
+ // but writes are synchronized via the UI thread. All readers should use VOLATILE.READ()
+ // all writers should use VOLATILE.WRITE().
+ private bool uiThreadWritableHasInteractiveContent;
+ private int uiThreadWritableState = (int)QuickInfoSessionState.Created;
+
+ #endregion
+
+ #region Reable/Writeable via UI Thread Only
+
+ private CancellationTokenSource uiThreadOnlyLinkedCancellationTokenSource;
+ internal IToolTipPresenter uiThreadOnlyPresenter;
+
+ #endregion
+
+ public AsyncQuickInfoSession (
+ IEnumerable<Lazy<IAsyncQuickInfoSourceProvider, IOrderableContentTypeMetadata>> orderedSourceProviders,
+ IGuardedOperations guardedOperations,
+ JoinableTaskContext joinableTaskContext,
+ IToolTipService toolTipService,
+ ITextView textView,
+ ITrackingPoint triggerPoint,
+ QuickInfoSessionOptions options,
+ PropertyCollection propertyCollection)
+ {
+ this.orderedSourceProviders = orderedSourceProviders ?? throw new ArgumentNullException(nameof(orderedSourceProviders));
+ this.guardedOperations = guardedOperations ?? throw new ArgumentNullException(nameof(guardedOperations));
+ this.joinableTaskContext = joinableTaskContext ?? throw new ArgumentNullException(nameof(joinableTaskContext));
+ this.toolTipService = toolTipService ?? throw new ArgumentNullException(nameof(toolTipService));
+ this.TextView = textView ?? throw new ArgumentNullException(nameof(textView));
+ this.triggerPoint = triggerPoint ?? throw new ArgumentNullException(nameof(triggerPoint));
+ this.Options = options;
+
+ // Bug #512117: Remove compatibility shims for 2nd gen. Quick Info APIs.
+ // We can remove this null check once we remove the legacy APIs.
+ this.Properties = propertyCollection ?? new PropertyCollection();
+
+ // Trigger point must be a tracking point on the view's buffer.
+ if (triggerPoint.TextBuffer != textView.TextBuffer)
+ {
+ throw new ArgumentException("The specified ITextSnapshot doesn't belong to the correct TextBuffer");
+ }
+ }
+
+ #region IAsyncQuickInfoSession
+
+ // All state changes are dispatched on the UI thread via TransitionState().
+ public event EventHandler<QuickInfoSessionStateChangedEventArgs> StateChanged;
+
+ public bool HasInteractiveContent => Volatile.Read(ref this.uiThreadWritableHasInteractiveContent);
+
+ public ITrackingSpan ApplicableToSpan => Volatile.Read(ref this.applicableToSpan);
+
+ public QuickInfoSessionOptions Options { get; }
+
+ public PropertyCollection Properties { get; }
+
+ public QuickInfoSessionState State => (QuickInfoSessionState)Volatile.Read(ref this.uiThreadWritableState);
+
+ public ITextView TextView { get; }
+
+ public IEnumerable<object> Content => Volatile.Read(ref this.content);
+
+ public async Task DismissAsync()
+ {
+ // Ensure that we have the UI thread. To avoid races, the rest of this method must be sync.
+ await this.joinableTaskContext.Factory.SwitchToMainThreadAsync();
+
+ var currentState = this.State;
+ if (currentState != QuickInfoSessionState.Dismissed)
+ {
+ // Cancel any running computations.
+ this.uiThreadOnlyLinkedCancellationTokenSource?.Cancel();
+ this.uiThreadOnlyLinkedCancellationTokenSource = null;
+
+ // Dismiss presenter.
+ var presenter = this.uiThreadOnlyPresenter;
+ if (presenter != null)
+ {
+ presenter.Dismissed -= this.OnDismissed;
+ this.uiThreadOnlyPresenter.Dismiss();
+
+ this.uiThreadOnlyPresenter = null;
+ }
+
+ // Update object state.
+ Volatile.Write(ref this.content, ImmutableList<object>.Empty);
+ Volatile.Write(ref this.applicableToSpan, null);
+
+ // Alert subscribers on the UI thread.
+ this.TransitionTo(QuickInfoSessionState.Dismissed);
+ }
+ }
+
+ public ITrackingPoint GetTriggerPoint(ITextBuffer textBuffer)
+ {
+ var mappedTriggerPoint = GetTriggerPoint(textBuffer.CurrentSnapshot);
+
+ if (!mappedTriggerPoint.HasValue)
+ {
+ return null;
+ }
+
+ return mappedTriggerPoint.Value.Snapshot.CreateTrackingPoint(mappedTriggerPoint.Value, PointTrackingMode.Negative);
+ }
+
+ public SnapshotPoint? GetTriggerPoint(ITextSnapshot textSnapshot)
+ {
+ var triggerSnapshotPoint = this.triggerPoint.GetPoint(this.TextView.TextSnapshot);
+ var triggerSpan = new SnapshotSpan(triggerSnapshotPoint, 0);
+
+ var mappedSpans = new FrugalList<SnapshotSpan>();
+ MappingHelper.MapDownToBufferNoTrack(triggerSpan, textSnapshot.TextBuffer, mappedSpans);
+
+ if (mappedSpans.Count == 0)
+ {
+ return null;
+ }
+ else
+ {
+ return mappedSpans[0].Start;
+ }
+ }
+
+ #endregion
+
+ #region Internal Impl
+
+ internal async Task StartAsync(CancellationToken cancellationToken)
+ {
+ if (this.State != QuickInfoSessionState.Created)
+ {
+ throw new InvalidOperationException($"Session must be in the {QuickInfoSessionState.Created} state to be started");
+ }
+
+ // Ensure we have the UI thread.
+ await this.joinableTaskContext.Factory.SwitchToMainThreadAsync();
+
+ cancellationToken.ThrowIfCancellationRequested();
+
+ // Create a linked cancellation token and store this in the class so we can be canceled by calls to DismissAsync()
+ // without impacting the caller's cancellation token.
+ using (var linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
+ {
+ try
+ {
+ this.uiThreadOnlyLinkedCancellationTokenSource = linkedCancellationTokenSource;
+ var failures = await this.ComputeContentAndStartPresenterAsync(this.uiThreadOnlyLinkedCancellationTokenSource.Token);
+
+ if (failures?.Any() ?? false)
+ {
+ // Guarded operations is not safe to use from the background so
+ // we catch all exceptions and post them here on the UI thread.
+ this.guardedOperations.HandleException(this, new AggregateException(failures));
+ }
+ }
+ finally
+ {
+ this.uiThreadOnlyLinkedCancellationTokenSource = null;
+
+ // If we were unable to await some computation task and don't end up with
+ // a visible presenter + content, ensure that we cleanup and change our state appropriately.
+ if (Volatile.Read(ref this.uiThreadOnlyPresenter) == null)
+ {
+ await this.DismissAsync().ConfigureAwait(false);
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ #region Private Impl
+
+ private async Task<IList<Exception>> ComputeContentAndStartPresenterAsync(CancellationToken cancellationToken)
+ {
+ IntellisenseUtilities.ThrowIfNotOnMainThread(this.joinableTaskContext);
+
+ // Read current state.
+ var originalState = this.State;
+ (IList<object> items, IList<ITrackingSpan> applicableToSpans, IList<Exception> failures)? results = null;
+
+ // Alert subscribers on the UI thread.
+ this.TransitionTo(QuickInfoSessionState.Calculating);
+
+ cancellationToken.ThrowIfCancellationRequested();
+
+ // Find and create the sources. Sources cache is smart enough to
+ // invalidate on content-type changed and free on view close.
+ var sources = IntellisenseSourceCache.GetSources(
+ this.TextView,
+ GetBuffersForTriggerPoint().ToList(),
+ this.CreateSources);
+
+ // Compute quick info items. This method switches off the UI thread.
+ // From here on out we're on an arbitrary thread.
+ results = await ComputeContentAsync(sources, cancellationToken).ConfigureAwait(false);
+
+ cancellationToken.ThrowIfCancellationRequested();
+
+ // Update our content, or put the empty list if there is none.
+ Volatile.Write(
+ ref this.content,
+ results != null ? ImmutableList.CreateRange(results.Value.items) : ImmutableList<object>.Empty);
+
+ await StartUIThreadEpilogueAsync(originalState, results?.applicableToSpans, cancellationToken).ConfigureAwait(false);
+
+ return results?.failures;
+ }
+
+ private async Task StartUIThreadEpilogueAsync(
+ QuickInfoSessionState originalState,
+ IList<ITrackingSpan> applicableToSpans,
+ CancellationToken cancellationToken)
+ {
+ // Ensure we're back on the UI thread.
+ await this.joinableTaskContext.Factory.SwitchToMainThreadAsync();
+
+ if (applicableToSpans != null)
+ {
+ // Update the applicable-to span.
+ this.ComputeApplicableToSpan(applicableToSpans);
+ }
+
+ // Check if any of our content is interactive and cache that so it's not done on mouse move.
+ this.ComputeHasInteractiveContent();
+
+ // If we have results and a span for which to show them and we aren't cancelled update the tip.
+ if ((originalState == QuickInfoSessionState.Created)
+ && this.Content.Any()
+ && (this.ApplicableToSpan != null)
+ && !cancellationToken.IsCancellationRequested)
+ {
+ this.CreateAndStartPresenter();
+ }
+ }
+
+ private void CreateAndStartPresenter()
+ {
+ IntellisenseUtilities.ThrowIfNotOnMainThread(this.joinableTaskContext);
+ Debug.Assert(this.uiThreadOnlyPresenter == null);
+
+ // Configure presenter behavior.
+ var parameters = new ToolTipParameters(
+ this.Options.HasFlag(QuickInfoSessionOptions.TrackMouse),
+ keepOpenFunc: this.ContentRequestsKeepOpen);
+
+ // Create and show presenter.
+ this.uiThreadOnlyPresenter = this.toolTipService.CreatePresenter(this.TextView, parameters);
+ this.uiThreadOnlyPresenter.Dismissed += this.OnDismissed;
+ this.uiThreadOnlyPresenter.StartOrUpdate(this.ApplicableToSpan, this.Content);
+
+ // Ensure that the presenter didn't dismiss the session.
+ if (this.State != QuickInfoSessionState.Dismissed)
+ {
+ // Update state and alert subscribers on the UI thread.
+ this.TransitionTo(QuickInfoSessionState.Visible);
+ }
+ }
+
+ private bool ContentRequestsKeepOpen()
+ {
+ IntellisenseUtilities.ThrowIfNotOnMainThread(this.joinableTaskContext);
+
+ if (this.HasInteractiveContent)
+ {
+ foreach (var content in this.Content)
+ {
+ if ((content is IInteractiveQuickInfoContent interactiveContent)
+ && ((interactiveContent.KeepQuickInfoOpen || interactiveContent.IsMouseOverAggregated)))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private void OnDismissed(object sender, EventArgs e)
+ {
+ IntellisenseUtilities.ThrowIfNotOnMainThread(this.joinableTaskContext);
+
+ this.joinableTaskContext.Factory.RunAsync(async delegate
+ {
+ await this.DismissAsync().ConfigureAwait(false);
+ });
+ }
+
+ private async Task<(IList<object> items, IList<ITrackingSpan> applicableToSpans, IList<Exception> failures)> ComputeContentAsync(
+ IEnumerable<OrderedSource> unorderedSources,
+ CancellationToken cancellationToken)
+ {
+ // Ensure we're off the UI thread.
+ await TaskScheduler.Default;
+
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var items = new FrugalList<object>();
+ var applicableToSpans = new FrugalList<ITrackingSpan>();
+ var failures = new FrugalList<Exception>();
+
+ // Sources from the cache are from the flattened projection buffer graph
+ // so they're initially out of order. We recorded their MEF ordering in
+ // a property though so we can reorder them now.
+ foreach (var source in unorderedSources.OrderBy(source => source.Order))
+ {
+ // This code is sequential to enable back-compat with the IQuickInfo* APIs,
+ // but when the shims are removed, consider parallelizing as a potential optimization.
+ await this.ComputeSourceContentAsync(
+ source.Source,
+ items,
+ applicableToSpans,
+ failures,
+ cancellationToken);
+ }
+
+ return (items, applicableToSpans, failures);
+ }
+
+ private void ComputeHasInteractiveContent()
+ {
+ foreach (var result in this.Content)
+ {
+ if (result is IInteractiveQuickInfoContent)
+ {
+ Volatile.Write(ref this.uiThreadWritableHasInteractiveContent, true);
+ break;
+ }
+ }
+ }
+
+ private async Task ComputeSourceContentAsync(
+ IAsyncQuickInfoSource source,
+ IList<object> items,
+ IList<ITrackingSpan> applicableToSpans,
+ IList<Exception> failures,
+ CancellationToken cancellationToken)
+ {
+ Debug.Assert(!this.joinableTaskContext.IsOnMainThread);
+
+ cancellationToken.ThrowIfCancellationRequested();
+
+ try
+ {
+ var result = await source.GetQuickInfoItemAsync(this, cancellationToken).ConfigureAwait(false);
+ if (result != null)
+ {
+ items.Add(result.Item);
+ if (result.ApplicableToSpan != null)
+ {
+ applicableToSpans.Add(result.ApplicableToSpan);
+ }
+ }
+ }
+ catch (Exception ex) when (ex.GetType() != typeof(OperationCanceledException))
+ {
+ failures.Add(ex);
+ }
+ }
+
+ private void ComputeApplicableToSpan(IEnumerable<ITrackingSpan> applicableToSpans)
+ {
+ // Requires UI thread for access to BufferGraph.
+ IntellisenseUtilities.ThrowIfNotOnMainThread(this.joinableTaskContext);
+
+ ITrackingSpan newApplicableToSpan = Volatile.Read(ref this.applicableToSpan);
+
+ foreach (var result in applicableToSpans)
+ {
+ var applicableToSpan = result;
+
+ if (applicableToSpan != null)
+ {
+ SnapshotSpan subjectAppSnapSpan = applicableToSpan.GetSpan(applicableToSpan.TextBuffer.CurrentSnapshot);
+
+ var surfaceAppSpans = this.TextView.BufferGraph.MapUpToBuffer(
+ subjectAppSnapSpan,
+ applicableToSpan.TrackingMode,
+ this.TextView.TextBuffer);
+
+ if (surfaceAppSpans.Count >= 1)
+ {
+ applicableToSpan = surfaceAppSpans[0].Snapshot.CreateTrackingSpan(surfaceAppSpans[0], applicableToSpan.TrackingMode);
+
+ newApplicableToSpan = IntellisenseUtilities.GetEncapsulatingSpan(
+ this.TextView,
+ newApplicableToSpan,
+ applicableToSpan);
+ }
+ }
+ }
+
+ Volatile.Write(ref this.applicableToSpan, newApplicableToSpan);
+ }
+
+ private IEnumerable<OrderedSource> CreateSources(ITextBuffer textBuffer)
+ {
+ IntellisenseUtilities.ThrowIfNotOnMainThread(this.joinableTaskContext);
+
+ int i = 0;
+
+ foreach (var sourceProvider in this.orderedSourceProviders)
+ {
+ foreach (var contentType in sourceProvider.Metadata.ContentTypes)
+ {
+ if (textBuffer.ContentType.IsOfType(contentType))
+ {
+ var source = this.guardedOperations.InstantiateExtension(
+ this,
+ sourceProvider,
+ provider => provider.TryCreateQuickInfoSource(textBuffer));
+
+ if (source != null)
+ {
+ yield return new OrderedSource(i, source);
+ }
+ }
+ }
+
+ ++i;
+ }
+ }
+
+ private Collection<ITextBuffer> GetBuffersForTriggerPoint()
+ {
+ IntellisenseUtilities.ThrowIfNotOnMainThread(this.joinableTaskContext);
+
+ return this.TextView.BufferGraph.GetTextBuffers(
+ buffer => this.GetTriggerPoint(buffer.CurrentSnapshot) != null);
+ }
+
+ private void TransitionTo(QuickInfoSessionState newState)
+ {
+ IntellisenseUtilities.ThrowIfNotOnMainThread(this.joinableTaskContext);
+
+ var oldState = this.State;
+ bool isValid = false;
+
+ switch (newState)
+ {
+ case QuickInfoSessionState.Created:
+ isValid = false;
+ break;
+ case QuickInfoSessionState.Calculating:
+ isValid = oldState == QuickInfoSessionState.Created || oldState == QuickInfoSessionState.Visible;
+ break;
+ case QuickInfoSessionState.Dismissed:
+ isValid = oldState == QuickInfoSessionState.Visible
+ || oldState == QuickInfoSessionState.Calculating;
+ break;
+ case QuickInfoSessionState.Visible:
+ isValid = oldState == QuickInfoSessionState.Calculating;
+ break;
+ }
+
+ if (!isValid)
+ {
+ throw new InvalidOperationException($"Invalid {nameof(IAsyncQuickInfoSession)} state transition from {oldState} to {newState}");
+ }
+
+ Volatile.Write(ref this.uiThreadWritableState, (int)newState);
+ this.StateChanged?.Invoke(this, new QuickInfoSessionStateChangedEventArgs(oldState, newState));
+ }
+
+ private sealed class OrderedSource : IDisposable
+ {
+ public OrderedSource(int order, IAsyncQuickInfoSource source)
+ {
+ this.Order = order;
+ this.Source = source ?? throw new ArgumentNullException(nameof(source));
+ }
+
+ public IAsyncQuickInfoSource Source { get; }
+
+ public int Order { get; }
+
+ public void Dispose()
+ {
+ this.Source.Dispose();
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/QuickInfo/QuickInfoController.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/QuickInfo/QuickInfoController.cs
new file mode 100644
index 0000000000..ce7fea00e3
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/QuickInfo/QuickInfoController.cs
@@ -0,0 +1,165 @@
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
+{
+ using System;
+ using System.Diagnostics;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using Microsoft.VisualStudio.Language.Utilities;
+ using Microsoft.VisualStudio.Text;
+ using Microsoft.VisualStudio.Text.Editor;
+ using Microsoft.VisualStudio.Threading;
+
+ internal sealed class QuickInfoController
+ {
+ private readonly IAsyncQuickInfoBroker quickInfoBroker;
+ private readonly JoinableTaskContext joinableTaskContext;
+ private readonly ITextView textView;
+ private CancellationTokenSource cancellationTokenSource;
+
+ internal QuickInfoController(
+ IAsyncQuickInfoBroker quickInfoBroker,
+ JoinableTaskContext joinableTaskContext,
+ ITextView textView)
+ {
+ this.quickInfoBroker = quickInfoBroker ?? throw new ArgumentNullException(nameof(quickInfoBroker));
+ this.joinableTaskContext = joinableTaskContext ?? throw new ArgumentNullException(nameof(joinableTaskContext));
+ this.textView = textView ?? throw new ArgumentNullException(nameof(textView));
+
+ IntellisenseUtilities.ThrowIfNotOnMainThread(joinableTaskContext);
+
+ this.textView.MouseHover += this.OnMouseHover;
+ this.textView.Closed += this.OnTextViewClosed;
+ }
+
+ // Internal for unit test.
+ internal void OnTextViewClosed(object sender, EventArgs e)
+ {
+ IntellisenseUtilities.ThrowIfNotOnMainThread(this.joinableTaskContext);
+
+ this.textView.Closed -= this.OnTextViewClosed;
+
+ // Cancel any calculating sessions and dispose the token.
+ this.CancelAndDisposeToken();
+
+ // Terminate any open quick info sessions.
+ this.joinableTaskContext.Factory.RunAsync(async delegate
+ {
+ var session = this.quickInfoBroker.GetSession(this.textView);
+ if (session != null)
+ {
+ await session.DismissAsync();
+ }
+ });
+
+ this.textView.MouseHover -= this.OnMouseHover;
+ }
+
+ private void OnMouseHover(object sender, MouseHoverEventArgs e)
+ {
+ IntellisenseUtilities.ThrowIfNotOnMainThread(this.joinableTaskContext);
+
+ SnapshotPoint? surfaceHoverPointNullable = e.TextPosition.GetPoint(
+ this.textView.TextViewModel.DataBuffer,
+ PositionAffinity.Predecessor);
+
+ // Does hover correspond to actual position in document or
+ // is there already a session around that is valid?
+ if (!surfaceHoverPointNullable.HasValue || this.IsSessionStillValid(surfaceHoverPointNullable.Value))
+ {
+ return;
+ }
+
+ // Cancel last queued quick info update, if there is one.
+ CancelAndDisposeToken();
+
+ this.cancellationTokenSource = new CancellationTokenSource();
+
+ // Start quick info session async on the UI thread.
+ this.joinableTaskContext.Factory.RunAsync(async delegate
+ {
+ await UpdateSessionStateAsync(surfaceHoverPointNullable.Value, this.cancellationTokenSource.Token);
+
+ // Clean up the cancellation token source.
+ Debug.Assert(this.joinableTaskContext.IsOnMainThread);
+ this.cancellationTokenSource.Dispose();
+ this.cancellationTokenSource = null;
+ });
+ }
+
+ private async Task UpdateSessionStateAsync(SnapshotPoint surfaceHoverPoint, CancellationToken cancellationToken)
+ {
+ // If we were cancelled while queued, do nothing.
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ ITrackingPoint triggerPoint = surfaceHoverPoint.Snapshot.CreateTrackingPoint(
+ surfaceHoverPoint.Position,
+ PointTrackingMode.Negative);
+
+ try
+ {
+ await this.quickInfoBroker.TriggerQuickInfoAsync(
+ this.textView,
+ triggerPoint,
+ QuickInfoSessionOptions.TrackMouse,
+ cancellationToken);
+ }
+ catch (OperationCanceledException) { /* swallow exception */ }
+ }
+
+ /// <summary>
+ /// Ensures that the specified session is still valid given the specified point. If the point is within the applicability
+ /// span of the session, the session will be left alone and the method will return true. If the point is outside of the
+ /// sessions applicability span, the session will be dismissed and the method will return false.
+ /// </summary>
+ private bool IsSessionStillValid(SnapshotPoint point)
+ {
+ // Make sure we're being called with a surface snapshot point.
+ Debug.Assert(point.Snapshot.TextBuffer == this.textView.TextViewModel.DataBuffer);
+
+ var session = this.quickInfoBroker.GetSession(this.textView);
+
+ if (session != null)
+ {
+ // First check that the point and applicable span are from the same subject buffer,
+ // and then that they intersect.
+ if ((session.ApplicableToSpan != null) &&
+ (session.ApplicableToSpan.TextBuffer == point.Snapshot.TextBuffer) &&
+ (session.ApplicableToSpan.GetSpan(point.Snapshot).IntersectsWith(new Span(point.Position, 0))))
+ {
+ return true;
+ }
+
+ // If this session has an interactive content give it a chance to keep the session alive.
+ if (session.HasInteractiveContent)
+ {
+ foreach (var content in session.Content)
+ {
+ foreach (var result in session.Content)
+ {
+ if (result is IInteractiveQuickInfoContent interactiveContent
+ && (interactiveContent.KeepQuickInfoOpen || interactiveContent.IsMouseOverAggregated))
+ {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private void CancelAndDisposeToken()
+ {
+ if (this.cancellationTokenSource != null)
+ {
+ this.cancellationTokenSource.Cancel();
+ this.cancellationTokenSource.Dispose();
+ this.cancellationTokenSource = null;
+ }
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/QuickInfo/QuickInfoTextViewCreationListener.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/QuickInfo/QuickInfoTextViewCreationListener.cs
new file mode 100644
index 0000000000..0df39fdac1
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/QuickInfo/QuickInfoTextViewCreationListener.cs
@@ -0,0 +1,40 @@
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
+{
+ using System;
+ using System.ComponentModel.Composition;
+ using Microsoft.VisualStudio.Text.Editor;
+ using Microsoft.VisualStudio.Threading;
+ using Microsoft.VisualStudio.Utilities;
+
+ [Export(typeof(ITextViewCreationListener))]
+ [ContentType("any")]
+ [TextViewRole(PredefinedTextViewRoles.Editable)]
+ [TextViewRole(PredefinedTextViewRoles.EmbeddedPeekTextView)]
+ [TextViewRole(PredefinedTextViewRoles.CodeDefinitionView)]
+ internal sealed class QuickInfoTextViewCreationListener : ITextViewCreationListener
+ {
+ private readonly IAsyncQuickInfoBroker quickInfoBroker;
+ private readonly JoinableTaskContext joinableTaskContext;
+
+ [ImportingConstructor]
+ public QuickInfoTextViewCreationListener(
+ IAsyncQuickInfoBroker quickInfoBroker,
+ JoinableTaskContext joinableTaskContext)
+ {
+ this.quickInfoBroker = quickInfoBroker
+ ?? throw new ArgumentNullException(nameof(quickInfoBroker));
+ this.joinableTaskContext = joinableTaskContext
+ ?? throw new ArgumentNullException(nameof(joinableTaskContext));
+ }
+
+ public void TextViewCreated(ITextView textView)
+ {
+ // No need to do anything further, this type hooks up events to the
+ // text view and tracks its own life cycle.
+ new QuickInfoController(
+ this.quickInfoBroker,
+ this.joinableTaskContext,
+ textView);
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/QuickInfo/SquiggleQuickInfoSource.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/QuickInfo/SquiggleQuickInfoSource.cs
new file mode 100644
index 0000000000..c92dfb3570
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/QuickInfo/SquiggleQuickInfoSource.cs
@@ -0,0 +1,139 @@
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using Microsoft.VisualStudio.Language.Utilities;
+ using Microsoft.VisualStudio.Text;
+ using Microsoft.VisualStudio.Text.Editor;
+ using Microsoft.VisualStudio.Text.Tagging;
+
+ internal sealed class SquiggleQuickInfoSource : IAsyncQuickInfoSource
+ {
+ private bool disposed = false;
+ private SquiggleQuickInfoSourceProvider componentContext;
+ private ITextBuffer textBuffer;
+ private ITextView tagAggregatorTextView;
+ private ITagAggregator<IErrorTag> tagAggregator;
+
+ public SquiggleQuickInfoSource(
+ SquiggleQuickInfoSourceProvider componentContext,
+ ITextBuffer textBuffer)
+ {
+ this.componentContext = componentContext
+ ?? throw new ArgumentNullException(nameof(componentContext));
+ this.textBuffer = textBuffer
+ ?? throw new ArgumentNullException(nameof(textBuffer));
+ }
+
+ public async Task<QuickInfoItem> GetQuickInfoItemAsync(
+ IAsyncQuickInfoSession session,
+ CancellationToken cancellationToken)
+ {
+ if (this.disposed)
+ {
+ throw new ObjectDisposedException("SquiggleQuickInfoSource");
+ }
+
+ if (session.TextView.TextBuffer != this.textBuffer)
+ {
+ return null;
+ }
+
+ // TagAggregators must be used exclusively on the UI thread.
+ await this.componentContext.JoinableTaskContext.Factory.SwitchToMainThreadAsync();
+
+ ITrackingSpan applicableToSpan = null;
+ object quickInfoContent = null;
+ ITagAggregator<IErrorTag> tagAggregator = GetTagAggregator(session.TextView);
+
+ Debug.Assert(tagAggregator != null, "Couldn't create a tag aggregator for error tags");
+ if (tagAggregator != null)
+ {
+ // Put together the span over which tags are to be discovered. This will be the zero-length span at the trigger
+ // point of the session.
+ SnapshotPoint? subjectTriggerPoint = session.GetTriggerPoint(this.textBuffer.CurrentSnapshot);
+ if (!subjectTriggerPoint.HasValue)
+ {
+ Debug.Fail("The squiggle QuickInfo source is being called when it shouldn't be.");
+ return null;
+ }
+
+ ITextSnapshot currentSnapshot = subjectTriggerPoint.Value.Snapshot;
+ var querySpan = new SnapshotSpan(subjectTriggerPoint.Value, 0);
+
+ // Ask for all of the error tags that intersect our query span. We'll get back a list of mapping tag spans.
+ // The first of these is what we'll use for our quick info.
+ IEnumerable<IMappingTagSpan<IErrorTag>> tags = tagAggregator.GetTags(querySpan);
+ ITrackingSpan appToSpan = null;
+ foreach (MappingTagSpan<IErrorTag> tag in tags)
+ {
+ NormalizedSnapshotSpanCollection applicableToSpans = tag.Span.GetSpans(currentSnapshot);
+ if (applicableToSpans.Count > 0)
+ {
+ // We've found a error tag at the right location with a tag span that maps to our subject buffer.
+ // Return the applicability span as well as the tooltip content.
+ appToSpan = IntellisenseUtilities.GetEncapsulatingSpan(
+ session.TextView,
+ appToSpan,
+ currentSnapshot.CreateTrackingSpan(
+ applicableToSpans[0].Span,
+ SpanTrackingMode.EdgeInclusive));
+
+ quickInfoContent = tag.Tag.ToolTipContent;
+ }
+ }
+
+ if (quickInfoContent != null)
+ {
+ applicableToSpan = appToSpan;
+ return new QuickInfoItem(
+ applicableToSpan,
+ quickInfoContent);
+ }
+ }
+
+ return null;
+ }
+
+ private ITagAggregator<IErrorTag> GetTagAggregator(ITextView textView)
+ {
+ if (this.tagAggregator == null)
+ {
+ this.tagAggregatorTextView = textView;
+ this.tagAggregator = this.componentContext.TagAggregatorFactoryService.CreateTagAggregator<IErrorTag>(textView);
+ }
+ else if (this.tagAggregatorTextView != textView)
+ {
+ throw new ArgumentException ("The SquiggleQuickInfoSource cannot be shared between TextViews.");
+ }
+
+ return this.tagAggregator;
+ }
+
+ public void Dispose()
+ {
+ if (this.disposed)
+ {
+ return;
+ }
+
+ this.disposed = true;
+
+ Debug.Assert(this.componentContext.JoinableTaskContext.IsOnMainThread);
+
+ // Get rid of the tag aggregator, if we created it.
+ if (this.tagAggregator != null)
+ {
+ this.tagAggregator.Dispose();
+ this.tagAggregator = null;
+ this.tagAggregatorTextView = null;
+ }
+
+ this.componentContext = null;
+ this.textBuffer = null;
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/QuickInfo/SquiggleQuickInfoSourceProvider.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/QuickInfo/SquiggleQuickInfoSourceProvider.cs
new file mode 100644
index 0000000000..668c0379be
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Impl/QuickInfo/SquiggleQuickInfoSourceProvider.cs
@@ -0,0 +1,26 @@
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
+{
+ using System.ComponentModel.Composition;
+ using Microsoft.VisualStudio.Text;
+ using Microsoft.VisualStudio.Text.Tagging;
+ using Microsoft.VisualStudio.Threading;
+ using Microsoft.VisualStudio.Utilities;
+
+ [Export(typeof(IAsyncQuickInfoSourceProvider))]
+ [Name("squiggle")]
+ [Order]
+ [ContentType("any")]
+ internal sealed class SquiggleQuickInfoSourceProvider : IAsyncQuickInfoSourceProvider
+ {
+ [Import]
+ internal IViewTagAggregatorFactoryService TagAggregatorFactoryService { get; set; }
+
+ [Import]
+ internal JoinableTaskContext JoinableTaskContext { get; set; }
+
+ public IAsyncQuickInfoSource TryCreateQuickInfoSource(ITextBuffer textBuffer)
+ {
+ return new SquiggleQuickInfoSource(this, textBuffer);
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Util/LanguageUtil/IntellisenseSourceCache.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Util/LanguageUtil/IntellisenseSourceCache.cs
new file mode 100644
index 0000000000..6be0e1eb98
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Util/LanguageUtil/IntellisenseSourceCache.cs
@@ -0,0 +1,115 @@
+// Copyright (c) Microsoft Corporation
+// All rights reserved
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Diagnostics;
+using System.Linq;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Utilities;
+
+namespace Microsoft.VisualStudio.Language.Utilities
+{
+ internal static class IntellisenseSourceCache
+ {
+ public static IEnumerable<TSource> GetSources<TSource>(ITextView textView, ITextBuffer buffer, Func<ITextBuffer, IEnumerable<TSource>> sourceCreator) where TSource : IDisposable
+ {
+ var viewCache = textView.Properties.GetOrCreateSingletonProperty(() => new ViewSourceCache<TSource>(textView, sourceCreator));
+ return viewCache.GetSources(new ITextBuffer[] { buffer });
+ }
+
+ public static IEnumerable<TSource> GetSources<TSource>(
+ ITextView textView,
+ IReadOnlyCollection<ITextBuffer> buffers,
+ Func<ITextBuffer, IEnumerable<TSource>> sourceCreator) where TSource : IDisposable
+ {
+ var viewCache = textView.Properties.GetOrCreateSingletonProperty(() => new ViewSourceCache<TSource>(textView, sourceCreator));
+ return viewCache.GetSources(buffers);
+ }
+
+ private class ViewSourceCache<TSource> where TSource : IDisposable
+ {
+ private readonly ITextView _textView;
+ private readonly Func<ITextBuffer, IEnumerable<TSource>> _sourceCreator;
+ private readonly HybridDictionary _bufferCache = new HybridDictionary();
+
+ public ViewSourceCache(ITextView textView, Func<ITextBuffer, IEnumerable<TSource>> sourceCreator)
+ {
+ _textView = textView;
+ _sourceCreator = sourceCreator;
+
+ _textView.Closed += (sender, args) => this.HandleViewClosed();
+ }
+
+ public IEnumerable<TSource> GetSources(IEnumerable<ITextBuffer> buffers)
+ {
+ if (_textView.IsClosed)
+ {
+ Debug.Fail("Intellisense is trying to operate after its view has been closed.");
+ return Enumerable.Empty<TSource>();
+ }
+
+ // For each buffer that should be involved, either return the cached source for that buffer, or create a source and
+ // cache it for later use.
+ IList<TSource> sources = new FrugalList<TSource>();
+ foreach (var buffer in buffers)
+ {
+ var bufferSources = (IEnumerable<TSource>) _bufferCache[buffer];
+
+ if (bufferSources == null)
+ {
+ bufferSources = _sourceCreator(buffer);
+ _bufferCache.Add(buffer, bufferSources);
+
+ buffer.ContentTypeChanged += OnContentTypeChanged;
+ }
+
+ foreach (var source in bufferSources)
+ {
+ sources.Add(source);
+ }
+ }
+
+ return sources;
+ }
+
+ private void OnContentTypeChanged(object sender, ContentTypeChangedEventArgs e)
+ {
+ var buffer = (ITextBuffer)sender;
+ buffer.ContentTypeChanged -= OnContentTypeChanged;
+
+ var bufferSources = (IEnumerable<TSource>)_bufferCache[buffer];
+
+ if (bufferSources != null)
+ {
+ foreach (TSource source in bufferSources)
+ {
+ source.Dispose();
+ }
+ }
+
+ _bufferCache.Remove(buffer);
+ }
+
+ public void HandleViewClosed()
+ {
+ foreach (ITextBuffer buffer in _bufferCache.Keys)
+ {
+ buffer.ContentTypeChanged -= OnContentTypeChanged;
+ }
+
+ foreach (IEnumerable<TSource> sourcesList in _bufferCache.Values)
+ {
+ foreach (var source in sourcesList)
+ {
+ source.Dispose();
+ }
+ }
+
+ _bufferCache.Clear();
+ }
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Util/LanguageUtil/IntellisenseUtilities.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Util/LanguageUtil/IntellisenseUtilities.cs
new file mode 100644
index 0000000000..574ac2e8a9
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Language/Util/LanguageUtil/IntellisenseUtilities.cs
@@ -0,0 +1,100 @@
+namespace Microsoft.VisualStudio.Language.Utilities
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using Microsoft.VisualStudio.Text;
+ using Microsoft.VisualStudio.Text.Editor;
+ using Microsoft.VisualStudio.Threading;
+
+ internal static class IntellisenseUtilities
+ {
+ internal static void ThrowIfNotOnMainThread(JoinableTaskContext joinableTaskContext)
+ {
+ if (!joinableTaskContext.IsOnMainThread)
+ {
+ throw new InvalidOperationException("Operation must occur on main thread.");
+ }
+ }
+
+ internal static ITrackingSpan GetEncapsulatingSpan(ITextView textView, ITrackingSpan span1, ITrackingSpan span2)
+ {
+ // If either of the spans is null, return the other one. If they're both null, we'll end up returning null
+ // (as it should be).
+ if (span1 == null)
+ {
+ return MapTrackingSpanToBuffer(textView, span2);
+ }
+
+ if (span2 == null)
+ {
+ return MapTrackingSpanToBuffer(textView, span1);
+ }
+
+ var surfaceSpans = new List<SnapshotSpan>();
+ surfaceSpans.AddRange(MapTrackingSpanToSpansOnBuffer(textView, span1));
+ surfaceSpans.AddRange(MapTrackingSpanToSpansOnBuffer(textView, span2));
+
+ ITrackingSpan encapsulatingSpan = null;
+ foreach (var span in surfaceSpans)
+ {
+ encapsulatingSpan = GetEncapsulatingSpan(
+ encapsulatingSpan,
+ span.Snapshot.CreateTrackingSpan(span, span1.TrackingMode));
+ }
+
+ return encapsulatingSpan;
+ }
+
+ private static ITrackingSpan GetEncapsulatingSpan(ITrackingSpan span1, ITrackingSpan span2)
+ {
+ // If either of the spans is null, return the other one. If they're both null, we'll end up returning null
+ // (as it should be).
+ if (span1 == null)
+ {
+ return span2;
+ }
+
+ if (span2 == null)
+ {
+ return span1;
+ }
+
+ // Use the other overload if you need mapping between buffers.
+ if (span1.TextBuffer != span2.TextBuffer)
+ {
+ throw new ArgumentException("Spans must be from the same textBuffer");
+ }
+
+ var snapshot = span1.TextBuffer.CurrentSnapshot;
+ var snapSpan1 = span1.GetSpan(snapshot);
+ var snapSpan2 = span2.GetSpan(snapshot);
+
+ return snapshot.CreateTrackingSpan(
+ Span.FromBounds(
+ Math.Min(snapSpan1.Start.Position, snapSpan2.Start.Position),
+ Math.Max(snapSpan1.End.Position, snapSpan2.End.Position)),
+ span1.TrackingMode);
+ }
+
+ private static ITrackingSpan MapTrackingSpanToBuffer(ITextView textView, ITrackingSpan trackingSpan)
+ {
+ if (trackingSpan == null)
+ {
+ return null;
+ }
+
+ var spans = MapTrackingSpanToSpansOnBuffer(textView, trackingSpan);
+ Debug.Assert(spans.Count == 1);
+ return spans[0].Snapshot.CreateTrackingSpan(spans[0], trackingSpan.TrackingMode);
+ }
+
+ private static NormalizedSnapshotSpanCollection MapTrackingSpanToSpansOnBuffer(ITextView textView, ITrackingSpan trackingSpan)
+ {
+ return textView.BufferGraph.MapUpToBuffer(
+ trackingSpan.GetSpan(trackingSpan.TextBuffer.CurrentSnapshot),
+ trackingSpan.TrackingMode,
+ textView.TextBuffer);
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/MDUtils.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/MDUtils.cs
new file mode 100644
index 0000000000..10e31d09b3
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/MDUtils.cs
@@ -0,0 +1,165 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Classification;
+using MonoDevelop.Core;
+using MonoDevelop.Ide.Editor;
+using MonoDevelop.Ide.Editor.Highlighting;
+using MonoDevelop.Ide.TypeSystem;
+
+namespace Microsoft.VisualStudio.Language.Intellisense.Implementation
+{
+ static class MDUtils
+ {
+ public static string ClassificationsToMarkup (ITextSnapshot snapshot, IList<ClassificationSpan> classifications, Span? locus)
+ {
+ var markup = new StringBuilder ();
+ var theme = DefaultSourceEditorOptions.Instance.GetEditorTheme ();
+ foreach (var part in classifications) {
+ //if () { TODO: Check if we hanle new lines
+ // markup.AppendLine ();
+ // continue;
+ //}
+ markup.Append ("<span foreground=\"");
+ markup.Append (GetThemeColor (theme, GetThemeColor (part.ClassificationType.Classification)));
+ markup.Append ("\">");
+ if (locus is Span locusSpan && part.Span.Intersection (locusSpan) is SnapshotSpan intersection) {
+ if (intersection.Start == part.Span.Start) {
+ if (intersection.End == part.Span.End) {
+ markup.Append ("<b>");
+ markup.Append (Ambience.EscapeText (snapshot.GetText (part.Span)));
+ markup.Append ("</b>");
+ } else {
+ markup.Append ("<b>");
+ markup.Append (Ambience.EscapeText (snapshot.GetText (intersection)));
+ markup.Append ("</b>");
+ markup.Append (Ambience.EscapeText (snapshot.GetText (intersection.End, part.Span.End - intersection.End)));
+ }
+ } else {
+ if (intersection.End == part.Span.End) {
+ markup.Append (Ambience.EscapeText (snapshot.GetText (part.Span.Start, intersection.Start - part.Span.Start)));
+ markup.Append ("<b>");
+ markup.Append (Ambience.EscapeText (snapshot.GetText (intersection)));
+ markup.Append ("</b>");
+ } else {
+ markup.Append (Ambience.EscapeText (snapshot.GetText (part.Span.Start, intersection.Start - part.Span.Start)));
+ markup.Append ("<b>");
+ markup.Append (Ambience.EscapeText (snapshot.GetText (intersection)));
+ markup.Append ("</b>");
+ markup.Append (Ambience.EscapeText (snapshot.GetText (intersection.End, part.Span.End - intersection.End)));
+ }
+ }
+ } else {
+ markup.Append (Ambience.EscapeText (snapshot.GetText (part.Span)));
+ }
+ markup.Append ("</span>");
+ }
+ return markup.ToString ();
+ }
+
+ private static void AppendSpan (ITextSnapshot snapshot, StringBuilder markup, EditorTheme theme, ClassificationSpan part)
+ {
+ markup.Append ("<span foreground=\"");
+ markup.Append (GetThemeColor (theme, GetThemeColor (part.ClassificationType.Classification)));
+ markup.Append ("\">");
+ markup.Append (Ambience.EscapeText (snapshot.GetText (part.Span)));
+ markup.Append ("</span>");
+ }
+
+ static string GetThemeColor (EditorTheme theme, string scope)
+ {
+ return SyntaxHighlightingService.GetColorFromScope (theme, scope, EditorThemeColors.Foreground).ToPangoString ();
+ }
+
+ static string GetThemeColor (string type)
+ {
+ switch (type) {
+ case ClassificationTypeNames.Keyword:
+ return "keyword";
+ case ClassificationTypeNames.ClassName:
+ return EditorThemeColors.UserTypes;
+ case ClassificationTypeNames.DelegateName:
+ return EditorThemeColors.UserTypesDelegates;
+ case ClassificationTypeNames.EnumName:
+ return EditorThemeColors.UserTypesEnums;
+ case ClassificationTypeNames.InterfaceName:
+ return EditorThemeColors.UserTypesInterfaces;
+ case ClassificationTypeNames.ModuleName:
+ return EditorThemeColors.UserTypes;
+ case ClassificationTypeNames.StructName:
+ return EditorThemeColors.UserTypesValueTypes;
+ case ClassificationTypeNames.TypeParameterName:
+ return EditorThemeColors.UserTypesTypeParameters;
+ case ClassificationTypeNames.Identifier:
+ return "source.cs";//TODO: This things say .cs, C#
+ case ClassificationTypeNames.NumericLiteral:
+ return "constant.numeric";
+ case ClassificationTypeNames.StringLiteral:
+ return "string.quoted";
+ case ClassificationTypeNames.WhiteSpace:
+ return "source.cs";
+ case ClassificationTypeNames.Operator:
+ return "keyword.source";
+ case ClassificationTypeNames.Punctuation:
+ return "punctuation";
+ case ClassificationTypeNames.Text:
+ return "source.cs";
+ default:
+ LoggingService.LogWarning ("Warning unexpected classification type: " + type);
+ return "source.cs";
+ }
+ }
+
+ public static class ClassificationTypeNames
+ {
+ public const string Comment = "comment";
+ public const string ExcludedCode = "excluded code";
+ public const string Identifier = "identifier";
+ public const string Keyword = "keyword";
+ public const string NumericLiteral = "number";
+ public const string Operator = "operator";
+ public const string PreprocessorKeyword = "preprocessor keyword";
+ public const string StringLiteral = "string";
+ public const string WhiteSpace = "whitespace";
+ public const string Text = "text";
+
+ public const string PreprocessorText = "preprocessor text";
+ public const string Punctuation = "punctuation";
+ public const string VerbatimStringLiteral = "string - verbatim";
+
+ public const string ClassName = "class name";
+ public const string DelegateName = "delegate name";
+ public const string EnumName = "enum name";
+ public const string InterfaceName = "interface name";
+ public const string ModuleName = "module name";
+ public const string StructName = "struct name";
+ public const string TypeParameterName = "type parameter name";
+
+ public const string XmlDocCommentAttributeName = "xml doc comment - attribute name";
+ public const string XmlDocCommentAttributeQuotes = "xml doc comment - attribute quotes";
+ public const string XmlDocCommentAttributeValue = "xml doc comment - attribute value";
+ public const string XmlDocCommentCDataSection = "xml doc comment - cdata section";
+ public const string XmlDocCommentComment = "xml doc comment - comment";
+ public const string XmlDocCommentDelimiter = "xml doc comment - delimiter";
+ public const string XmlDocCommentEntityReference = "xml doc comment - entity reference";
+ public const string XmlDocCommentName = "xml doc comment - name";
+ public const string XmlDocCommentProcessingInstruction = "xml doc comment - processing instruction";
+ public const string XmlDocCommentText = "xml doc comment - text";
+
+ public const string XmlLiteralAttributeName = "xml literal - attribute name";
+ public const string XmlLiteralAttributeQuotes = "xml literal - attribute quotes";
+ public const string XmlLiteralAttributeValue = "xml literal - attribute value";
+ public const string XmlLiteralCDataSection = "xml literal - cdata section";
+ public const string XmlLiteralComment = "xml literal - comment";
+ public const string XmlLiteralDelimiter = "xml literal - delimiter";
+ public const string XmlLiteralEmbeddedExpression = "xml literal - embedded expression";
+ public const string XmlLiteralEntityReference = "xml literal - entity reference";
+ public const string XmlLiteralName = "xml literal - name";
+ public const string XmlLiteralProcessingInstruction = "xml literal - processing instruction";
+ public const string XmlLiteralText = "xml literal - text";
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TagBasedSyntaxHighlighting.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TagBasedSyntaxHighlighting.cs
index 0fa65b18b8..08e7984999 100644
--- a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TagBasedSyntaxHighlighting.cs
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TagBasedSyntaxHighlighting.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
//
@@ -48,6 +48,9 @@ namespace Microsoft.VisualStudio.Platform
public Task<HighlightedLine> GetHighlightedLineAsync(IDocumentLine line, CancellationToken cancellationToken)
{
+ //TODO verify that the snapshot line from this.textBuffer is equivalent to the document line converted to a snapshotline.
+ //Possibly take in a TextDataModel as a parameter and verify the buffers are appropriate.
+ //ITextSnapshotLine snapshotLine = (line as Mono.TextEditor.TextDocument.DocumentLineFromTextSnapshotLine)?.Line;
ITextSnapshotLine snapshotLine = textBuffer.CurrentSnapshot.GetLineFromLineNumber (line.LineNumber - 1);
if ((this.classifier == null) || (snapshotLine == null))
{
@@ -194,6 +197,9 @@ namespace Microsoft.VisualStudio.Platform
styleName = "punctuation.separator.key-value.html";
break;
case "HTML Server-Side Script":
+ //styleName = "punctuation.section.embedded.begin"; // suggested by mike, does nothing
+ //styleName = "punctuation.section.embedded.begin.cs"; // suggested by mike, does nothing
+ styleName = "meta.preprocessor.source.cs"; // TODO: Find a name to use here
//styleName = style.HtmlServerSideScript.Name;
break;
case "HTML Tag Delimiter":
@@ -257,7 +263,12 @@ namespace Microsoft.VisualStudio.Platform
styleName = "variable.other.less";
break;
default:
- styleName = EditorThemeColors.Foreground;
+ // If the stylename looks like a textmate style, just use it
+ if (classificationType.Classification.IndexOf('.') >= 0)
+ {
+ styleName = classificationType.Classification;
+ }
+
break;
}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Def/TextUIWpf/Adornments/ToolTipPresenterStyle.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Def/TextUIWpf/Adornments/ToolTipPresenterStyle.cs
new file mode 100644
index 0000000000..655524eae3
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Def/TextUIWpf/Adornments/ToolTipPresenterStyle.cs
@@ -0,0 +1,35 @@
+namespace Microsoft.VisualStudio.Text.Adornments
+{
+ using Xwt.Drawing;
+ using Microsoft.VisualStudio.Text.Editor;
+
+ ///<summary>
+ /// Defines a set of properties that will be used to style the default WPF ToolTip presenter.
+ ///</summary>
+ /// <remarks>
+ /// This is a MEF component part, and should be exported with the following attributes:
+ /// [Export(typeof(ToolTipPresenterStyle))]
+ /// [Name]
+ /// [Order]
+ /// All exports of this component part should be ordered before the "default" ToolTip presenter style. At a minimum, this
+ /// means adding [Order(Before="default")] to the export metadata.
+ /// </remarks>
+ public class ToolTipPresenterStyle
+ {
+ /// <summary>
+ /// Gets a string that identifies the appearance category for the <see cref="ITextView"/>
+ /// displayed in the default ToolTip presenter.
+ /// </summary>
+ public virtual string AppearanceCategory { get; protected set; }
+
+ /// <summary>
+ /// Gets a <see cref="Brush"/> that will be used to paint the borders in the ToolTip presenter.
+ /// </summary>
+ public virtual Color BorderBrush { get; protected set; }
+
+ /// <summary>
+ /// Gets a <see cref="Brush"/> that will be used to paint the background of the ToolTip presenter.
+ /// </summary>
+ public virtual Color BackgroundBrush { get; protected set; }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Def/TextUIWpf/Editor/ISpaceReservationAgent.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Def/TextUIWpf/Editor/ISpaceReservationAgent.cs
new file mode 100644
index 0000000000..7fd2dd12a8
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Def/TextUIWpf/Editor/ISpaceReservationAgent.cs
@@ -0,0 +1,49 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+//
+namespace Microsoft.VisualStudio.Text.Editor
+{
+ using System.Windows.Media;
+ using System;
+
+ /// <summary>
+ /// Handles the display of space reservation adornments.
+ /// </summary>
+ public interface ISpaceReservationAgent
+ {
+ /// <summary>
+ /// Positions and displays the contents of the the <see cref="ISpaceReservationAgent"/>.
+ /// </summary>
+ /// <param name="reservedSpace">Currently reserved space.</param>
+ /// <returns>The space. If null is returned, the <see cref="ISpaceReservationManager"/> will remove the agent.</returns>
+ /// <remarks>If an agent does not want to be removed, but also does not wish to request any additional space, it can return a non-null but empty Geometry.</remarks>
+ Geometry PositionAndDisplay(Geometry reservedSpace);
+
+ /// <summary>
+ /// Called whenever the content of the space reservation agent should be hidden.
+ /// </summary>
+ /// <remarks>This method is called by the manager to hide the content of the space reservation agent.</remarks>
+ void Hide();
+
+ /// <summary>
+ /// Determines whether the mouse is over this agent or anything it contains.
+ /// </summary>
+ bool IsMouseOver { get; }
+
+ /// <summary>
+ /// Determines whether the adornment created by the space reservation agent has keyboard focus.
+ /// </summary>
+ bool HasFocus { get; }
+
+ /// <summary>
+ /// Occurs when the adornment created by the ISpaceReservationAgent loses focus.
+ /// </summary>
+ event EventHandler LostFocus;
+
+ /// <summary>
+ /// Occurs when the adornment created by the ISpaceReservationAgent gets focus.
+ /// </summary>
+ event EventHandler GotFocus;
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Def/TextUIWpf/Editor/ISpaceReservationManager.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Def/TextUIWpf/Editor/ISpaceReservationManager.cs
new file mode 100644
index 0000000000..b64d4ab8bb
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Def/TextUIWpf/Editor/ISpaceReservationManager.cs
@@ -0,0 +1,79 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+//
+namespace Microsoft.VisualStudio.Text.Editor
+{
+ using System.Collections.ObjectModel;
+ using Microsoft.VisualStudio.Text.Adornments;
+ using System;
+ using MonoDevelop.Components;
+
+ /// <summary>
+ /// Manages space reservation adornments.
+ /// </summary>
+ public interface ISpaceReservationManager
+ {
+ /// <summary>
+ /// Creates a default implementation of an <see cref="ISpaceReservationAgent"/> that displays <paramref name="content"/> in a popup window.
+ /// </summary>
+ /// <param name="visualSpan">The span of text associated with the tip.</param>
+ /// <param name="style">The style options for displaying the tip.</param>
+ /// <param name="content">The UI element to be displayed in the tip.</param>
+ /// <returns>An <see cref="ISpaceReservationAgent"/> that will display the desired content in a popup window.</returns>
+ ISpaceReservationAgent CreatePopupAgent(ITrackingSpan visualSpan, PopupStyles style, Xwt.Widget content);
+
+ /// <summary>
+ /// Updates <paramref name="agent"/> with the <paramref name="visualSpan"/>.
+ /// This only works for PopupAgents and returns for other agents.
+ /// </summary>
+ /// <param name="agent">The agent to add.</param>
+ /// <param name="visualSpan">The agent's new visual span.</param>
+ void UpdatePopupAgent(ISpaceReservationAgent agent, ITrackingSpan visualSpan, PopupStyles styles);
+
+ /// <summary>
+ /// Adds <paramref name="agent"/> to the list of agents managed by this manager.
+ /// </summary>
+ /// <param name="agent">The agent to add.</param>
+ void AddAgent(ISpaceReservationAgent agent);
+
+ /// <summary>
+ /// Removes <paramref name="agent"/> from the list of agents managed by this manager.
+ /// </summary>
+ /// <param name="agent">The agent to remove.</param>
+ /// <returns><c>true</c> if the agent was in the list of agents to remove.</returns>
+ bool RemoveAgent(ISpaceReservationAgent agent);
+
+ /// <summary>
+ /// Gets the list of agents managed by this manager.
+ /// </summary>
+ /// <remarks>Any implementation of aa <see cref="ISpaceReservationAgent"/> can be used for this method.</remarks>
+ ReadOnlyCollection<ISpaceReservationAgent> Agents { get; }
+
+ /// <summary>
+ /// Occurs when the agent is changed.
+ /// </summary>
+ /// <remarks></remarks>
+ event EventHandler<SpaceReservationAgentChangedEventArgs> AgentChanged;
+
+ /// <summary>
+ /// Determines whether the mouse is over an agent managed by this manager.
+ /// </summary>
+ bool IsMouseOver { get; }
+
+ /// <summary>
+ /// Determines whether the adornment created by the space reservation agent has keyboard focus.
+ /// </summary>
+ bool HasAggregateFocus { get; }
+
+ /// <summary>
+ /// Occurs when keyboard focus is lost by any of the managed adornments.
+ /// </summary>
+ event EventHandler LostAggregateFocus;
+
+ /// <summary>
+ /// Occurs when any of the managed adornments gets keyboard focus.
+ /// </summary>
+ event EventHandler GotAggregateFocus;
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Def/TextUIWpf/Editor/SpaceReservationAgentChangedEventArgs.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Def/TextUIWpf/Editor/SpaceReservationAgentChangedEventArgs.cs
new file mode 100644
index 0000000000..55aea71068
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Def/TextUIWpf/Editor/SpaceReservationAgentChangedEventArgs.cs
@@ -0,0 +1,39 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+//
+namespace Microsoft.VisualStudio.Text.Editor
+{
+ using System;
+
+ /// <summary>
+ /// Provides information when an <see cref="ISpaceReservationAgent"/> is changed in an <see cref="ISpaceReservationManager"/>.
+ /// </summary>
+
+ public class SpaceReservationAgentChangedEventArgs : EventArgs
+ {
+ private readonly ISpaceReservationAgent _newAgent;
+ private readonly ISpaceReservationAgent _oldAgent;
+
+ /// <summary>
+ /// Initializes a new instance of <see cref="SpaceReservationAgentChangedEventArgs"/>.
+ /// </summary>
+ /// <param name="oldAgent">The <see cref="ISpaceReservationAgent "/> associated with the previous value.</param>
+ /// <param name="newAgent">The <see cref="ISpaceReservationAgent "/> associated with the new value.</param>
+ public SpaceReservationAgentChangedEventArgs(ISpaceReservationAgent oldAgent, ISpaceReservationAgent newAgent)
+ {
+ _oldAgent = oldAgent;
+ _newAgent = newAgent;
+ }
+
+ /// <summary>
+ /// Gets the old agent.
+ /// </summary>
+ public ISpaceReservationAgent OldAgent { get { return _oldAgent; } }
+
+ /// <summary>
+ /// Gets the new agent.
+ /// </summary>
+ public ISpaceReservationAgent NewAgent { get { return _newAgent; } }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Def/TextUIWpf/Editor/SpaceReservationManagerDefinition.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Def/TextUIWpf/Editor/SpaceReservationManagerDefinition.cs
new file mode 100644
index 0000000000..4691cf03c0
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Def/TextUIWpf/Editor/SpaceReservationManagerDefinition.cs
@@ -0,0 +1,27 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+//
+namespace Microsoft.VisualStudio.Text.Editor
+{
+ /// <summary>
+ /// Represents metadata for an <see cref="ISpaceReservationManager"/>.
+ /// </summary>
+ /// <remarks>
+ /// Because you cannot subclass this type, you can simply use the [Export] attribute.
+ /// </remarks>
+ /// <example>
+ /// internal sealed class Components
+ /// {
+ /// [Export]
+ /// [Name("SampleSpaceReservationManager")]
+ /// [Order(After = "Selection", Before = "Text")]
+ /// internal SpaceReservationManagerDefinition sampleManagerDefinition;
+ ///
+ /// { other components }
+ /// }
+ /// </example>
+ public sealed class SpaceReservationManagerDefinition
+ {
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/ToolTipAdornment/GuardedToolTipPresenter.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/ToolTipAdornment/GuardedToolTipPresenter.cs
new file mode 100644
index 0000000000..937358b5b8
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/ToolTipAdornment/GuardedToolTipPresenter.cs
@@ -0,0 +1,56 @@
+namespace Microsoft.VisualStudio.Text.AdornmentLibrary.ToolTip.Implementation
+{
+ using System;
+ using System.Collections.Generic;
+ using Microsoft.VisualStudio.Text;
+ using Microsoft.VisualStudio.Text.Adornments;
+ using Microsoft.VisualStudio.Utilities;
+
+ internal sealed class GuardedToolTipPresenter : IToolTipPresenter
+ {
+ private readonly IGuardedOperations guardedOperations;
+ private bool isDismissed = false;
+
+ public GuardedToolTipPresenter(
+ IGuardedOperations guardedOperations,
+ IToolTipPresenter presenter)
+ {
+ this.guardedOperations = guardedOperations ?? throw new ArgumentNullException(nameof(guardedOperations));
+ this.Presenter = presenter ?? throw new ArgumentNullException(nameof(presenter));
+
+ presenter.Dismissed += this.OnDismissed;
+ }
+
+ public event EventHandler Dismissed;
+
+ internal IToolTipPresenter Presenter { get; }
+
+ public void Dismiss()
+ {
+ if (!this.isDismissed)
+ {
+ this.isDismissed = true;
+ this.guardedOperations.CallExtensionPoint(() =>
+ {
+ this.Presenter.Dismissed -= this.Dismissed;
+ this.Presenter.Dismiss();
+ });
+
+ this.Dismissed?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ public void StartOrUpdate(ITrackingSpan applicableToSpan, IEnumerable<object> content)
+ {
+ if (this.isDismissed)
+ {
+ throw new InvalidOperationException($"{nameof(IToolTipPresenter)} is dismissed");
+ }
+
+ this.guardedOperations.CallExtensionPoint(
+ () => this.Presenter.StartOrUpdate(applicableToSpan, content));
+ }
+
+ private void OnDismissed(object sender, EventArgs e) => this.Dismiss();
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/ToolTipAdornment/IViewElementFactoryMetadata.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/ToolTipAdornment/IViewElementFactoryMetadata.cs
new file mode 100644
index 0000000000..69f4889335
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/ToolTipAdornment/IViewElementFactoryMetadata.cs
@@ -0,0 +1,11 @@
+namespace Microsoft.VisualStudio.Text.AdornmentLibrary.ToolTip.Implementation
+{
+ using Microsoft.VisualStudio.Utilities;
+
+ public interface IViewElementFactoryMetadata : IOrderable
+ {
+ string FromFullName { get; }
+
+ string ToFullName { get; }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/ToolTipAdornment/ToolTipService.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/ToolTipAdornment/ToolTipService.cs
new file mode 100644
index 0000000000..079bebbe98
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/ToolTipAdornment/ToolTipService.cs
@@ -0,0 +1,61 @@
+namespace Microsoft.VisualStudio.Text.AdornmentLibrary.ToolTip.Implementation
+{
+ using System;
+ using System.Collections.Generic;
+ using System.ComponentModel.Composition;
+ using Microsoft.VisualStudio.Text.Adornments;
+ using Microsoft.VisualStudio.Text.Editor;
+ using Microsoft.VisualStudio.Threading;
+ using Microsoft.VisualStudio.Utilities;
+
+ [Export(typeof(IToolTipService))]
+ internal sealed class ToolTipService : IToolTipService
+ {
+ private readonly IEnumerable<Lazy<IToolTipPresenterFactory, IOrderable>> unorderedPresenterProviders;
+ private readonly IGuardedOperations guardedOperations;
+ private readonly JoinableTaskContext joinableTaskContext;
+
+ // Lazily initialized.
+ private IEnumerable<Lazy<IToolTipPresenterFactory, IOrderable>> orderedPresenterProviders;
+
+ [ImportingConstructor]
+ public ToolTipService(
+ [ImportMany]IEnumerable<Lazy<IToolTipPresenterFactory, IOrderable>> unorderedPresenterProviders,
+ IGuardedOperations guardedOperations,
+ JoinableTaskContext joinableTaskContext)
+ {
+ this.unorderedPresenterProviders = unorderedPresenterProviders
+ ?? throw new ArgumentNullException(nameof(unorderedPresenterProviders));
+ this.guardedOperations = guardedOperations
+ ?? throw new ArgumentNullException(nameof(guardedOperations));
+ this.joinableTaskContext = joinableTaskContext
+ ?? throw new ArgumentNullException(nameof(joinableTaskContext));
+ }
+
+ private IEnumerable<Lazy<IToolTipPresenterFactory, IOrderable>> OrderedPresenterProviders
+ => this.orderedPresenterProviders ?? (this.orderedPresenterProviders = Orderer.Order(this.unorderedPresenterProviders));
+
+ public IToolTipPresenter CreatePresenter(ITextView textView, ToolTipParameters parameters)
+ {
+ if (!this.joinableTaskContext.IsOnMainThread)
+ {
+ throw new InvalidOperationException("Must be called from UI thread");
+ }
+
+ foreach (var provider in this.OrderedPresenterProviders)
+ {
+ var presenter = this.guardedOperations.CallExtensionPoint(
+ () => provider.Value.Create(textView, parameters ?? ToolTipParameters.Default),
+ valueOnThrow: null);
+
+ if (presenter != null)
+ {
+ // Wrap in a presenter that wraps all calls in a guarded operation.
+ return new GuardedToolTipPresenter(this.guardedOperations, presenter);
+ }
+ }
+
+ throw new InvalidOperationException($"No applicable {nameof(IToolTipPresenterFactory)}");
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/ToolTipAdornment/ViewElementFactoryService.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/ToolTipAdornment/ViewElementFactoryService.cs
new file mode 100644
index 0000000000..258763c0a0
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/ToolTipAdornment/ViewElementFactoryService.cs
@@ -0,0 +1,143 @@
+namespace Microsoft.VisualStudio.Text.AdornmentLibrary.ToolTip.Implementation
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.Immutable;
+ using System.ComponentModel.Composition;
+ using Microsoft.VisualStudio.Text.Adornments;
+ using Microsoft.VisualStudio.Text.Editor;
+ using Microsoft.VisualStudio.Threading;
+ using Microsoft.VisualStudio.Utilities;
+
+ [Export(typeof(IViewElementFactoryService))]
+ internal sealed class ViewElementFactoryService : IViewElementFactoryService
+ {
+ private readonly IEnumerable<Lazy<IViewElementFactory, IViewElementFactoryMetadata>> unorderedViewFactories;
+ private readonly IGuardedOperations guardedOperations;
+ private readonly JoinableTaskContext joinableTaskContext;
+
+ // Lazily computed.
+ private ImmutableDictionary<(Type, Type), Lazy<IViewElementFactory, IViewElementFactoryMetadata>> factoryMap
+ = ImmutableDictionary <(Type, Type), Lazy<IViewElementFactory, IViewElementFactoryMetadata>>.Empty;
+ private IEnumerable<Lazy<IViewElementFactory, IViewElementFactoryMetadata>> orderedFactories;
+
+ [ImportingConstructor]
+ public ViewElementFactoryService(
+ [ImportMany]IEnumerable<Lazy<IViewElementFactory, IViewElementFactoryMetadata>> unorderedViewFactories,
+ IGuardedOperations guardedOperations,
+ JoinableTaskContext joinableTaskContext)
+ {
+ this.joinableTaskContext = joinableTaskContext
+ ?? throw new ArgumentNullException(nameof(joinableTaskContext));
+ this.guardedOperations = guardedOperations
+ ?? throw new ArgumentNullException(nameof(guardedOperations));
+ this.unorderedViewFactories = unorderedViewFactories
+ ?? throw new ArgumentNullException(nameof(unorderedViewFactories));
+ }
+
+ public TView CreateViewElement<TView>(ITextView textView, object model) where TView : class
+ {
+ if (!this.joinableTaskContext.IsOnMainThread)
+ {
+ throw new InvalidOperationException("Must be called on UI thread");
+ }
+
+ if (model == null)
+ {
+ throw new ArgumentNullException(nameof(model));
+ }
+
+ var lazyFactory = this.FindFactory<TView>(model.GetType());
+
+ if (lazyFactory != null)
+ {
+ var factory = this.guardedOperations
+ .InstantiateExtension(this, lazyFactory);
+
+ return this.guardedOperations.CallExtensionPoint(
+ () => factory.CreateViewElement<TView>(textView, model),
+ valueOnThrow: default(TView));
+ }
+
+ return null;
+ }
+
+ private IEnumerable<Lazy<IViewElementFactory, IViewElementFactoryMetadata>> OrderedFactories
+ => this.orderedFactories ?? (this.orderedFactories ?? Orderer.Order(this.unorderedViewFactories));
+
+ private Lazy<IViewElementFactory, IViewElementFactoryMetadata> FindFactory<TView>(Type modelType)
+ {
+ // Do we have this conversion cached?
+ if (this.factoryMap.TryGetValue((modelType, typeof(TView)), out var lazyFactory))
+ {
+ return lazyFactory;
+ }
+
+ // Nope, try and find a suitable converter that matches this type exactly.
+ var exactMatch = this.FindExactMatchingFactory<TView>(modelType);
+ if (exactMatch != null)
+ {
+ return exactMatch;
+ }
+ else
+ {
+ // Try and find a suitable match from our type and interface hierarchy.
+ return this.FindInheritanceMatchingFactory<TView>(modelType);
+ }
+ }
+
+ private Lazy<IViewElementFactory, IViewElementFactoryMetadata> FindExactMatchingFactory<TView>(Type modelType)
+ {
+ var candidate = this.FindMatchingFactory<TView>(modelType, (item) => item.Metadata.FromFullName == modelType.AssemblyQualifiedName);
+ if (candidate != null)
+ {
+ return candidate;
+ }
+
+ // Unknown conversion.
+ return null;
+ }
+
+ private Lazy<IViewElementFactory, IViewElementFactoryMetadata> FindInheritanceMatchingFactory<TView>(Type modelType)
+ {
+ // Try and find a suitable match from the interfaces on this type.
+ foreach (var iface in modelType.GetInterfaces())
+ {
+ var candidate = this.FindMatchingFactory<TView>(modelType, (item) => item.Metadata.FromFullName == iface.AssemblyQualifiedName);
+ if (candidate != null)
+ {
+ return candidate;
+ }
+ }
+
+ // Still no, try and find a suitable converter from among the base types for this class.
+ for (var t = modelType.BaseType; t != null; t = t.BaseType)
+ {
+ var candidate = this.FindMatchingFactory<TView>(modelType, (item) => item.Metadata.FromFullName == t.AssemblyQualifiedName);
+ if (candidate != null)
+ {
+ return candidate;
+ }
+ }
+
+ // Unknown conversion.
+ return null;
+ }
+
+ private Lazy<IViewElementFactory, IViewElementFactoryMetadata> FindMatchingFactory<TView>(
+ Type modelType,
+ Predicate<Lazy<IViewElementFactory, IViewElementFactoryMetadata>> fromSelector)
+ {
+ foreach (var candidate in this.OrderedFactories)
+ {
+ if (candidate.Metadata.ToFullName == typeof(TView).AssemblyQualifiedName && fromSelector.Invoke(candidate))
+ {
+ this.factoryMap = this.factoryMap.Add((modelType, typeof(TView)), candidate);
+ return candidate;
+ }
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/BaseWpfToolTipPresenter.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/BaseWpfToolTipPresenter.cs
new file mode 100644
index 0000000000..9ece9899b9
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/BaseWpfToolTipPresenter.cs
@@ -0,0 +1,176 @@
+namespace Microsoft.VisualStudio.Text.AdornmentLibrary.ToolTip.Implementation
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Linq;
+ using System.Windows;
+ using System.Windows.Media;
+ using Microsoft.VisualStudio.Text;
+ using Microsoft.VisualStudio.Text.Adornments;
+ using Microsoft.VisualStudio.Text.Editor;
+ using MonoDevelop.Components;
+ using Xwt;
+
+ internal abstract class BaseWpfToolTipPresenter : IToolTipPresenter, IObscuringTip
+ {
+ protected readonly IViewElementFactoryService viewElementFactoryService;
+ protected readonly IObscuringTipManager obscuringTipManager;
+ protected readonly ITextView textView;
+ protected readonly ToolTipParameters parameters;
+ protected readonly ToolTipPresenterStyle presenterStyle;
+
+ protected readonly MonoDevelop.Components.XwtPopup popup = new MonoDevelop.Components.XwtPopup (Xwt.PopupWindow.PopupType.Tooltip);
+ protected ITrackingSpan applicableToSpan;
+ protected bool isDismissed;
+
+ public BaseWpfToolTipPresenter(
+ IViewElementFactoryService viewElementFactoryService,
+ IObscuringTipManager obscuringTipManager,
+ ITextView textView,
+ ToolTipParameters parameters,
+ ToolTipPresenterStyle presenterStyle)
+ {
+ this.viewElementFactoryService = viewElementFactoryService
+ ?? throw new ArgumentNullException(nameof(viewElementFactoryService));
+ this.obscuringTipManager = obscuringTipManager
+ ?? throw new ArgumentNullException(nameof(obscuringTipManager));
+ this.textView = textView ?? throw new ArgumentNullException(nameof(textView));
+ this.parameters = parameters ?? throw new ArgumentNullException(nameof(parameters));
+ this.presenterStyle = presenterStyle ?? throw new ArgumentNullException(nameof(presenterStyle));
+ }
+
+ #region IObscuringTip
+
+ public double Opacity => this.popup.Content.Opacity;
+
+ bool IObscuringTip.Dismiss()
+ {
+ bool isDismissed = this.isDismissed;
+ this.Dismiss();
+
+ return isDismissed;
+ }
+
+ public void SetOpacity(double opacity)
+ {
+ var child = this.popup.Content;
+ if (child != null)
+ {
+ child.Opacity = opacity;
+ }
+ }
+
+ #endregion
+
+ #region IToolTipPresenter
+
+ public event EventHandler Dismissed;
+
+ public virtual void Dismiss()
+ {
+ if (this.popup != null)
+ {
+ this.popup.Closed -= this.OnPopupClosed;
+ this.popup.Visible = false;
+ //this.popup.Content = null;
+ this.isDismissed = true;
+ this.obscuringTipManager.RemoveTip(this.textView, this);
+ }
+
+ this.Dismissed?.Invoke(this, EventArgs.Empty);
+ }
+
+ public virtual void StartOrUpdate(ITrackingSpan applicableToSpan, IEnumerable<object> content)
+ {
+ Debug.Assert(!this.isDismissed);
+
+ if (!this.popup.Visible)
+ {
+ this.Start(content);
+ }
+ else
+ {
+ this.Update(content);
+ }
+ }
+
+ #endregion
+
+ #region Impl
+
+ private void Start(IEnumerable<object> content)
+ {
+ Debug.Assert(!this.popup.Visible && !this.isDismissed);
+
+ if (this.PresentationSpan == null)
+ {
+ this.Dismiss();
+ return;
+ }
+
+
+ this.Update(content);
+
+ this.popup.Closed += this.OnPopupClosed;
+
+ this.popup.Visible = true;
+ //todo this.popup.BringIntoView();
+ this.obscuringTipManager.PushTip(this.textView, this);
+ }
+
+ public void Update(IEnumerable<object> content)
+ {
+ // Translate intermediate objects to UIElements.
+ var contentViewElements = content.Select(
+ item => this.viewElementFactoryService.CreateViewElement<Xwt.Widget>(
+ this.textView, item))
+ .Where(item => item != null);
+
+ //var control = new WpfToolTipControl (this.WpfTextView) {
+ // // Translate intermediate to UI.
+ // DataContext = new WpfToolTipViewModel (
+ // this.parameters,
+ // contentViewElements,
+ // this.presenterStyle)
+ //};
+ var vbox = new Xwt.VBox ();
+ foreach (var view in contentViewElements) {
+ vbox.PackStart (view);
+ }
+ this.popup.Content = vbox;
+ }
+
+ protected ITrackingSpan PresentationSpan
+ {
+ get
+ {
+ if (this.applicableToSpan == null)
+ {
+ return null;
+ }
+
+ SpanTrackingMode mode = this.applicableToSpan.TrackingMode;
+ NormalizedSnapshotSpanCollection viewSpans = this.textView.BufferGraph.MapUpToBuffer(
+ this.applicableToSpan.GetSpan(this.applicableToSpan.TextBuffer.CurrentSnapshot),
+ mode,
+ this.textView.TextBuffer);
+ return viewSpans.Count > 0 ? viewSpans[0].Snapshot.CreateTrackingSpan(viewSpans[0], mode) : null;
+ }
+ }
+
+ protected IMdTextView WpfTextView => this.textView as IMdTextView;
+
+ protected Point GetScreenPointFromTextXY(double x, double y)
+ {
+ var view = WpfTextView;
+ Debug.Assert(view != null);
+
+ return view.VisualElement.GetScreenCoordinates(new Gdk.Point((int)(x - view.ViewportLeft), (int)(y - view.ViewportTop))).ToXwtPoint();
+ }
+
+ private void OnPopupClosed(object sender, EventArgs e) => this.Dismiss();
+
+ #endregion
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/DefaultToolTipPresenterStyle.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/DefaultToolTipPresenterStyle.cs
new file mode 100644
index 0000000000..71fcd35f9e
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/DefaultToolTipPresenterStyle.cs
@@ -0,0 +1,18 @@
+namespace Microsoft.VisualStudio.Text.AdornmentLibrary.ToolTip.Implementation
+{
+ using System.ComponentModel.Composition;
+ using Xwt.Drawing;
+ using Microsoft.VisualStudio.Text.Adornments;
+ using Microsoft.VisualStudio.Utilities;
+
+ [Export(typeof(ToolTipPresenterStyle))]
+ [Name("default")]
+ internal sealed class DefaultToolTipPresenterStyle : ToolTipPresenterStyle
+ {
+ public DefaultToolTipPresenterStyle()
+ {
+ this.BorderBrush = Colors.Black;
+ this.BackgroundBrush = Colors.LightGray;
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/Legacy/ToolTipProvider.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/Legacy/ToolTipProvider.cs
new file mode 100644
index 0000000000..5d8a94a48a
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/Legacy/ToolTipProvider.cs
@@ -0,0 +1,104 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+//
+// This file contain implementations details that are subject to change without notice.
+// Use at your own risk.
+//
+namespace Microsoft.VisualStudio.Text.AdornmentLibrary.ToolTip.Implementation
+{
+ using System;
+ using System.Collections.ObjectModel;
+ using System.Diagnostics;
+ using System.Windows;
+ using Microsoft.VisualStudio.Text;
+ using Microsoft.VisualStudio.Text.Adornments;
+ using Microsoft.VisualStudio.Text.Editor;
+ using MonoDevelop.Components;
+
+ /// <summary>
+ /// An adornment provider that can create and display ToolTips taking an arbitrary object as content.
+ /// </summary>
+ internal class ToolTipProvider : IToolTipProvider
+ {
+ #region Private Members
+ private readonly IMdTextView _textView;
+ internal readonly ISpaceReservationManager _spaceReservationManager;
+ internal ISpaceReservationAgent _agent;
+ #endregion
+
+ internal ToolTipProvider(IMdTextView textView)
+ {
+ _textView = textView;
+ _spaceReservationManager = _textView.GetSpaceReservationManager("ToolTip");
+ _spaceReservationManager.AgentChanged += OnAgentChanged;
+ }
+
+ void OnAgentChanged(object sender, SpaceReservationAgentChangedEventArgs e)
+ {
+ if (_agent == e.OldAgent)
+ _agent = null;
+ }
+
+ #region IToolTipProvider Members
+ public void ClearToolTip()
+ {
+ if (_agent != null)
+ {
+ _spaceReservationManager.RemoveAgent(_agent);
+ //_agent should be null (cleared by OnAgentChanged)
+ }
+ }
+
+ public void ShowToolTip(ITrackingSpan span, object toolTipContent, PopupStyles style)
+ {
+ if (span == null)
+ throw new ArgumentNullException("span");
+ if (span.TextBuffer != _textView.TextBuffer)
+ throw new ArgumentException("Invalid span");
+ if (toolTipContent == null)
+ throw new ArgumentNullException("toolTipContent");
+
+ var element = toolTipContent as Control;
+ if (element == null)
+ {
+ string toolTipContentAsString = toolTipContent as string;
+ if (toolTipContentAsString != null)
+ {
+ element = BuildTooltipUIElement(toolTipContentAsString);
+ }
+ else
+ {
+ throw new ArgumentException("Invalid contnet", nameof(toolTipContent));
+ }
+ }
+
+ this.ClearToolTip();
+ _agent = _spaceReservationManager.CreatePopupAgent(span, style, element);
+ _spaceReservationManager.AddAgent(_agent);
+ }
+
+ public void ShowToolTip(ITrackingSpan span, object toolTipContent)
+ {
+ this.ShowToolTip(span, toolTipContent, PopupStyles.None);
+ }
+ #endregion
+
+ internal static Control BuildTooltipUIElement(string toolTipText)
+ {
+ // Make a pretty ToolTip-looking thing, using a border and a TextBlock.
+ //TextBlock txt = new TextBlock();
+ //txt.Text = toolTipText;
+ //txt.Background = SystemColors.InfoBrush;
+ //txt.Foreground = SystemColors.InfoTextBrush;
+ //txt.Padding = new Thickness(1.0);
+
+ //Border border = new Border();
+ //border.BorderBrush = SystemColors.WindowFrameBrush;
+ //border.BorderThickness = new Thickness(1.0);
+ //border.Child = txt;
+ //return border;
+ return new XwtControl(new Xwt.Label(toolTipText));
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/Legacy/ToolTipProviderFactory.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/Legacy/ToolTipProviderFactory.cs
new file mode 100644
index 0000000000..231eb1e2d4
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/Legacy/ToolTipProviderFactory.cs
@@ -0,0 +1,47 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+//
+// This file contain implementations details that are subject to change without notice.
+// Use at your own risk.
+//
+namespace Microsoft.VisualStudio.Text.AdornmentLibrary.ToolTip.Implementation
+{
+ using System;
+ using Microsoft.VisualStudio.Text;
+ using Microsoft.VisualStudio.Text.Adornments;
+ using Microsoft.VisualStudio.Text.Editor;
+ using Microsoft.VisualStudio.Utilities;
+ using System.ComponentModel.Composition;
+
+ [Export(typeof(IToolTipProviderFactory))]
+ internal sealed class ToolTipProviderFactory : IToolTipProviderFactory
+ {
+ //Specify the view layer definitions for the view.
+ [Export]
+ [Name("ToolTip")]
+ [Order()]
+ internal SpaceReservationManagerDefinition tooltipManager;
+
+ #region IToolTipProviderFactory Members
+ public IToolTipProvider GetToolTipProvider(ITextView textView)
+ {
+ var wpfTextView = textView as IMdTextView;
+ if (wpfTextView == null)
+ throw new ArgumentException("Invalid TextView");
+
+ return CreateToolTipProviderInternal(wpfTextView);
+ }
+ #endregion
+
+ internal static ToolTipProvider CreateToolTipProviderInternal(IMdTextView view)
+ {
+ ToolTipProvider toolTipAdornmentProvider = view.Properties.GetOrCreateSingletonProperty<ToolTipProvider>(delegate
+ {
+ return new ToolTipProvider(view);
+ });
+
+ return toolTipAdornmentProvider;
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/MouseTrackingWpfToolTipPresenter.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/MouseTrackingWpfToolTipPresenter.cs
new file mode 100644
index 0000000000..ff69fdf9ba
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/MouseTrackingWpfToolTipPresenter.cs
@@ -0,0 +1,327 @@
+namespace Microsoft.VisualStudio.Text.AdornmentLibrary.ToolTip.Implementation
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using Microsoft.VisualStudio.Text.Adornments;
+ using Microsoft.VisualStudio.Text.Editor;
+ using Microsoft.VisualStudio.Text.Formatting;
+ using UIElement = Xwt.Widget;
+ using MouseEventArgs = Xwt.MouseMovedEventArgs;
+ using Xwt;
+ using Rect = Xwt.Rectangle;
+ using System.Windows.Input;
+
+ internal sealed class MouseTrackingWpfToolTipPresenter : BaseWpfToolTipPresenter
+ {
+ // The tooltip can cause flickering issues if shown on the pixel row immediately below text. This
+ // offset is used to move all tooltips down in order to eliminate the flicker.
+ private const int ToolTipVerticalOffset = 1;
+ private UIElement mouseContainer;
+
+ public MouseTrackingWpfToolTipPresenter(
+ IViewElementFactoryService viewElementFactoryService,
+ IObscuringTipManager obscuringTipManager,
+ ITextView textView,
+ ToolTipParameters parameters,
+ ToolTipPresenterStyle presenterStyle)
+ : base(viewElementFactoryService, obscuringTipManager, textView, parameters, presenterStyle)
+ {
+ }
+
+ public override void Dismiss()
+ {
+ if (this.mouseContainer != null)
+ {
+ this.mouseContainer.MouseExited -= this.OnMouseLeaveContainer;
+ this.mouseContainer.MouseMoved -= this.OnMouseMoveContainer;
+ this.mouseContainer = null;
+ }
+
+ base.Dismiss();
+ }
+
+ public override void StartOrUpdate(ITrackingSpan applicableToSpan, IEnumerable<object> content)
+ {
+ Debug.Assert(this.parameters.TrackMouse,
+ "This tooltip presenter is only valid when TrackMouse is true.");
+
+ this.applicableToSpan = applicableToSpan;
+
+ if (DismissIfMouseOutsideOfVisualSpan())
+ {
+ this.Dismiss();
+ return;
+ }
+
+ if (!this.popup.Visible)
+ {
+ this.SubscribeToContainerUnderMouse();
+
+ Rect? presentationSpanRect = this.GetViewSpanRect(this.PresentationSpan);
+ if (!presentationSpanRect.HasValue)
+ {
+ this.Dismiss();
+ return;
+ }
+
+ this.popup.Location = Xwt.Desktop.MouseLocation;
+ }
+
+ base.StartOrUpdate(applicableToSpan, content);
+ }
+
+ private void SubscribeToContainerUnderMouse()
+ {
+ if (!this.isDismissed
+ && (this.mouseContainer == null)
+ && (((IMdTextView)textView).VisualElement.IsMouseOver () || (popup.IsMouseOver () && popup.Content != null))) {
+ mouseContainer = popup.IsMouseOver () ? popup.Content : Xwt.Toolkit.CurrentEngine.WrapWidget (((IMdTextView)textView).VisualElement);
+ mouseContainer.MouseExited += this.OnMouseLeaveContainer;
+ mouseContainer.MouseMoved += this.OnMouseMoveContainer;
+ }
+ }
+
+ private void UnsubscribeFromMouseContainer()
+ {
+ if (this.mouseContainer != null)
+ {
+ this.mouseContainer.MouseExited -= this.OnMouseLeaveContainer;
+ this.mouseContainer.MouseMoved -= this.OnMouseMoveContainer;
+ this.mouseContainer = null;
+ }
+ }
+
+ private void OnMouseLeaveContainer(object sender, EventArgs e)
+ {
+ Debug.Assert(sender == this.mouseContainer);
+
+ UnsubscribeFromMouseContainer();
+ this.SubscribeToContainerUnderMouse();
+
+ if (this.mouseContainer == null)
+ {
+ this.Dismiss();
+ }
+ else
+ {
+ DismissIfMouseOutsideOfVisualSpan();
+ }
+ }
+
+ private void OnMouseMoveContainer(object sender, EventArgs e)
+ => this.DismissIfMouseOutsideOfVisualSpan();
+
+ private bool DismissIfMouseOutsideOfVisualSpan()
+ {
+ var wpfTextView = this.WpfTextView;
+
+ if (wpfTextView == null || this.ShouldClearToolTipOnMouseMove())
+ {
+ this.Dismiss();
+ return true;
+ }
+
+ return false;
+ }
+
+ private Point GetMousePosRelativeToView()
+ {
+ var view = WpfTextView;
+ if (view == null)
+ {
+ throw new InvalidOperationException("Can't determine the relative position of the mouse without a WpfTextView.");
+ }
+
+ Point mousePoint;
+ //if (e != null)
+ //{
+ // mousePoint = e.GetPosition(view.VisualElement);
+ //}
+ //else
+ //{
+ mousePoint = Mouse.GetPosition(view.VisualElement);
+ //}
+
+ mousePoint.X += view.ViewportLeft;
+ mousePoint.Y += view.ViewportTop;
+
+ return mousePoint;
+ }
+
+ //private CustomPopupPlacement[] PlacePopup(Size popupSize, Size targetSize, Point offset)
+ //{
+ // Debug.Assert(!this.isDismissed);
+
+ // if (WpfTextView.VisualElement == null || PresentationSource.FromVisual(WpfTextView.VisualElement) == null)
+ // {
+ // return new CustomPopupPlacement[] { };
+ // }
+
+ // double zoom = this.WpfTextView.ZoomLevel / 100.0;
+
+ // // We need to provide a placement point relative to the top left corner of the popup placement rectangle.
+ // // X would be the distance from mouse point to the left side of the placement rectangle so that the popup
+ // // is positioned horizontally at mouse position.
+ // Point mouseRelativeToView = GetMousePosRelativeToView();
+ // Point popupTargetOrigin = new Point(mouseRelativeToView.X - this.WpfTextView.ViewportLeft - this.WpfTextView.VisualElement.PointFromScreen(this.popup.PlacementRectangle.BottomLeft).X, 0);
+ // Matrix transformToDevice = PresentationSource.FromVisual(WpfTextView.VisualElement).CompositionTarget.TransformToDevice;
+ // popupTargetOrigin = transformToDevice.Transform(popupTargetOrigin);
+ // popupTargetOrigin.X *= zoom;
+
+ // // Y would be the height of the placement rectangle - so the popup is right below it
+ // popupTargetOrigin.Y = targetSize.Height;
+
+ // return new CustomPopupPlacement[] { new CustomPopupPlacement(popupTargetOrigin, PopupPrimaryAxis.Horizontal) };
+ //}
+
+ private bool ShouldClearToolTipOnMouseMove()
+ {
+ if (this.popup != null)
+ {
+ if (this.popup.IsMouseOver() || this.parameters.KeepOpen)
+ {
+ return false;
+ }
+ else if (this.WpfTextView != null && !this.WpfTextView.VisualElement.IsMouseOver())
+ {
+ // The mouse is not over any interactive content and not over the text view either
+ return true;
+ }
+ }
+
+ return ShouldClearToolTipOnMouseMove(GetMousePosRelativeToView());
+ }
+
+ private bool ShouldClearToolTipOnMouseMove(Point pointRelativeToView)
+ {
+ var view = WpfTextView;
+ if (view == null || view.TextViewLines == null)
+ {
+ return true;
+ }
+
+ // If the point isn't over a valid line, dismiss.
+ ITextViewLine line = view.TextViewLines.GetTextViewLineContainingYCoordinate(pointRelativeToView.Y);
+ if (line == null)
+ {
+ return true;
+ }
+
+ if (this.PresentationSpan == null)
+
+ {
+ return false;
+ }
+
+ SnapshotSpan span = this.PresentationSpan.GetSpan(view.TextSnapshot);
+ SnapshotPoint? position = line.GetBufferPositionFromXCoordinate(pointRelativeToView.X, true);
+
+ //Special case handling for the last line on the buffer: we want to treat the mouse hovering near the end
+ //of the line as a "hit"
+ if ((!position.HasValue) && (line.End == line.Snapshot.Length))
+ {
+ if ((pointRelativeToView.X >= line.TextLeft) && (pointRelativeToView.X < line.TextRight + line.EndOfLineWidth))
+ {
+ position = line.End;
+ }
+ }
+
+ // If the position is valid and within the start/end of the visual span, inclusive, we
+ // don't need to dismiss.
+ if (position.HasValue && (span.Contains(position.Value) || span.End == position.Value))
+ {
+ return false;
+ }
+
+ // No match, dismiss the tooltip.
+ return true;
+ }
+
+ private Rect? GetViewSpanRect(ITrackingSpan viewSpan)
+ {
+ var view = WpfTextView;
+ if (view == null || view.TextViewLines == null || view.IsClosed)
+ {
+ return null;
+ }
+
+ SnapshotSpan visualSpan = viewSpan.GetSpan(view.TextSnapshot);
+
+ Rect? spanRectangle = null;
+ if (visualSpan.Length > 0)
+ {
+ double left = double.MaxValue;
+ double top = double.MaxValue;
+ double right = double.MinValue;
+ double bottom = double.MinValue;
+
+ var bounds = view.TextViewLines.GetNormalizedTextBounds(visualSpan);
+ foreach (var bound in bounds)
+ {
+ left = Math.Min(left, bound.Left);
+ top = Math.Min(top, bound.TextTop);
+ right = Math.Max(right, bound.Right);
+ bottom = Math.Max(bottom, bound.TextBottom + ToolTipVerticalOffset);
+ }
+
+ // If the start of the span lies within the view, use that instead of the left-bound of the span as a whole.
+ // This will cause popups to be left-aligned with the start of their span, if at all possible.
+ var startLine = view.TextViewLines.GetTextViewLineContainingBufferPosition(visualSpan.Start);
+ if (startLine != null)
+ {
+ var startPointBounds = startLine.GetExtendedCharacterBounds(visualSpan.Start);
+ if ((startPointBounds.Left < right) &&
+ (startPointBounds.Left >= view.ViewportLeft) &&
+ (startPointBounds.Left < view.ViewportRight))
+ {
+ left = startPointBounds.Left;
+ }
+ }
+
+ //Special case handling for when the end of the span is at the start of a line.
+ ITextViewLine line = view.TextViewLines.GetTextViewLineContainingBufferPosition(visualSpan.End);
+ if ((line != null) && (line.Start == visualSpan.End))
+ {
+ bottom = Math.Max(bottom, line.TextBottom + ToolTipVerticalOffset);
+ }
+
+ if (left < right)
+ {
+ spanRectangle = new Rect(left, top, right - left, bottom - top);
+ }
+ }
+ else
+ {
+ // visualSpan is zero length so the default MarkerGeometry will be null. Create a custom marker geometry based on
+ // the location.
+ ITextViewLine line = view.TextViewLines.GetTextViewLineContainingBufferPosition(visualSpan.Start);
+ if (line != null)
+ {
+ TextBounds bounds = line.GetCharacterBounds(visualSpan.Start);
+ spanRectangle = new Rect(bounds.Left, bounds.TextTop, 0.0, bounds.TextHeight + ToolTipVerticalOffset);
+ }
+ }
+
+ if (spanRectangle.HasValue && !spanRectangle.Value.IsEmpty)
+ {
+ //Get the portion of the span geometry that is inside the view.
+ Rect viewRect = new Rect(view.ViewportLeft,
+ view.ViewportTop,
+ view.ViewportWidth,
+ view.ViewportHeight);
+
+ Rect spanRect = spanRectangle.Value;
+ spanRect.Intersect(viewRect);
+
+ Rect spanRectInScreenCoordinates =
+ new Rect(this.GetScreenPointFromTextXY(spanRect.Left, spanRect.Top),
+ this.GetScreenPointFromTextXY(spanRect.Right, spanRect.Bottom));
+
+ return spanRectInScreenCoordinates;
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/SpanTrackingWpfToolTipPresenter.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/SpanTrackingWpfToolTipPresenter.cs
new file mode 100644
index 0000000000..b0fb9d3911
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/SpanTrackingWpfToolTipPresenter.cs
@@ -0,0 +1,115 @@
+namespace Microsoft.VisualStudio.Text.AdornmentLibrary.ToolTip.Implementation
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Windows;
+ using System.Windows.Input;
+ using Microsoft.VisualStudio.Text.Adornments;
+ using Microsoft.VisualStudio.Text.Editor;
+ using MonoDevelop.Components;
+
+ internal sealed class SpanTrackingWpfToolTipPresenter : BaseWpfToolTipPresenter
+ {
+ public SpanTrackingWpfToolTipPresenter(
+ IViewElementFactoryService viewElementFactoryService,
+ IObscuringTipManager obscuringTipManager,
+ ITextView textView,
+ ToolTipParameters parameters,
+ ToolTipPresenterStyle presenterStyle)
+ : base(viewElementFactoryService, obscuringTipManager, textView, parameters, presenterStyle)
+ {
+ }
+
+ public override void Dismiss()
+ {
+ if (!this.isDismissed)
+ {
+ this.popup.BoundsChanged -= this.OnLayoutUpdated;
+ this.textView.LayoutChanged -= this.OnTextViewLayoutChanged;
+ this.WpfTextView.Caret.PositionChanged -= this.OnCaretPositionChanged;
+ //this.WpfTextView.ZoomLevelChanged -= this.OnZoomLevelChanged;
+ this.WpfTextView.VisualElement.FocusOutEvent -= this.OnLostKeyboardFocus;
+ }
+
+ base.Dismiss();
+ }
+
+ public override void StartOrUpdate(ITrackingSpan applicableToSpan, IEnumerable<object> content)
+ {
+ this.applicableToSpan = applicableToSpan;
+ this.popup.TransientFor = Xwt.Toolkit.CurrentEngine.WrapWidget (this.WpfTextView.VisualElement).ParentWindow;
+ //this.popup.HorizontalAlignment = HorizontalAlignment.Left;
+ //this.popup.VerticalAlignment = VerticalAlignment.Top;
+
+ if (!this.popup.Visible)
+ {
+ this.popup.BoundsChanged += this.OnLayoutUpdated;
+ this.textView.LayoutChanged += this.OnTextViewLayoutChanged;
+ this.WpfTextView.Caret.PositionChanged += this.OnCaretPositionChanged;
+ //this.WpfTextView.ZoomLevelChanged += this.OnZoomLevelChanged;
+ this.WpfTextView.VisualElement.FocusOutEvent += this.OnLostKeyboardFocus;
+ }
+
+ base.StartOrUpdate(applicableToSpan, content);
+ }
+
+ private void OnLostKeyboardFocus(object sender, Gtk.FocusOutEventArgs e) => this.Dismiss();
+
+ private void OnCaretPositionChanged(object sender, CaretPositionChangedEventArgs e) => this.Dismiss();
+
+ // Refreshes and async addition of controls like those used by Roslyn cause the tooltip
+ // to change size. Recompute position when this happens.
+ private void OnLayoutUpdated(object sender, EventArgs e) => this.UpdatePopupPosition();
+
+ //private void OnZoomLevelChanged(object sender, ZoomLevelChangedEventArgs e) => this.UpdatePopupPosition();
+
+ private void OnTextViewLayoutChanged(object sender, TextViewLayoutChangedEventArgs e)
+ {
+ if (e.NewViewState.EditSnapshot == e.OldViewState.EditSnapshot || this.parameters.IgnoreBufferChange)
+ {
+ this.UpdatePopupPosition();
+ }
+ else
+ {
+ this.Dismiss();
+ }
+ }
+
+ private void UpdatePopupPosition()
+ {
+ var startPoint = this.PresentationSpan.GetStartPoint(this.textView.TextSnapshot);
+ var endPoint = this.PresentationSpan.GetEndPoint(this.textView.TextSnapshot);
+
+ if (!this.textView.IsClosed
+ && this.textView.TextViewLines.FormattedSpan.Contains(endPoint))
+ {
+ var startLine = this.WpfTextView.GetTextViewLineContainingBufferPosition(startPoint);
+ if (startLine != null)
+ {
+ double verticalOffset;
+
+ var endLine = this.WpfTextView.GetTextViewLineContainingBufferPosition(endPoint);
+ if (endLine != null)
+ {
+ verticalOffset = (endLine.Bottom - this.textView.ViewportTop);
+ }
+ else
+ {
+ verticalOffset = (startLine.Bottom - this.textView.ViewportTop);
+ }
+
+ // WPF seems to have a bug where if the offsets are the same, the control doesn't move, even
+ // if its relative target did. Force the popup to move by toggling the offsets.
+ this.popup.Location = ((IMdTextView)textView).VisualElement.GetScreenCoordinates (new Gdk.Point ((int)(startLine.GetCharacterBounds (startPoint).Leading - this.textView.ViewportLeft), (int)verticalOffset)).ToXwtPoint ();
+
+ return;
+ }
+ }
+ else {
+ Console.WriteLine ();
+ }
+
+ this.Dismiss();
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/ToolTipStyleFactory.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/ToolTipStyleFactory.cs
new file mode 100644
index 0000000000..0a35b76c54
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/ToolTipStyleFactory.cs
@@ -0,0 +1,36 @@
+namespace Microsoft.VisualStudio.Text.AdornmentLibrary.ToolTip.Implementation
+{
+ using System;
+ using System.Collections.Generic;
+ using System.ComponentModel.Composition;
+ using System.Linq;
+ using Microsoft.VisualStudio.Text.Adornments;
+ using Microsoft.VisualStudio.Utilities;
+
+ [Export]
+ internal sealed class ToolTipStyleFactory
+ {
+ private ToolTipPresenterStyle style;
+
+ [ImportMany]
+ private List<Lazy<ToolTipPresenterStyle, IOrderable>> unorderedPresenterStyles;
+
+ public ToolTipPresenterStyle Style
+ {
+ get
+ {
+ if (this.style == null)
+ {
+ this.style = Orderer.Order(this.unorderedPresenterStyles).FirstOrDefault()?.Value;
+
+ if (this.style == null)
+ {
+ throw new ArgumentNullException($"No exports of type {nameof(ToolTipPresenterStyle)}");
+ }
+ }
+
+ return this.style;
+ }
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/ViewElementFactories/WpfClassifiedTextElementViewElementFactory.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/ViewElementFactories/WpfClassifiedTextElementViewElementFactory.cs
new file mode 100644
index 0000000000..70964cc113
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/ViewElementFactories/WpfClassifiedTextElementViewElementFactory.cs
@@ -0,0 +1,74 @@
+namespace Microsoft.VisualStudio.Text.AdornmentLibrary.ToolTip.Implementation
+{
+ using System;
+ using System.ComponentModel.Composition;
+ using System.Text;
+ using Microsoft.VisualStudio.Text.Adornments;
+ using Microsoft.VisualStudio.Text.Classification;
+ using Microsoft.VisualStudio.Text.Editor;
+ using Microsoft.VisualStudio.Utilities;
+ using UIElement = Xwt.Widget;
+
+ [Export(typeof(IViewElementFactory))]
+ [Name("default ClassifiedTextElement to UIElement")]
+ [TypeConversion(from: typeof(ClassifiedTextElement), to: typeof(UIElement))]
+ [Order]
+ internal sealed class WpfClassifiedTextElementViewElementFactory : IViewElementFactory
+ {
+ private readonly IClassificationTypeRegistryService classificationTypeRegistryService;
+ private readonly ToolTipStyleFactory styleFactory;
+
+ [ImportingConstructor]
+ public WpfClassifiedTextElementViewElementFactory(
+ IClassificationTypeRegistryService classificationTypeRegistryService,
+ ToolTipStyleFactory styleFactory)
+ {
+ this.classificationTypeRegistryService = classificationTypeRegistryService
+ ?? throw new ArgumentNullException(nameof(classificationTypeRegistryService));
+ this.styleFactory = styleFactory
+ ?? throw new ArgumentNullException(nameof(styleFactory));
+ }
+
+ public TView CreateViewElement<TView>(ITextView textView, object model) where TView : class
+ {
+ // Should never happen if the service's code is correct, but it's good to be paranoid.
+ if (typeof(TView) != typeof(UIElement) || !(model is ClassifiedTextElement element))
+ {
+ throw new ArgumentException($"Invalid type conversion. Unsupported {nameof(model)} or {nameof(TView)} type");
+ }
+
+
+ var textBlock = new Xwt.Label();
+ StringBuilder markup = new StringBuilder ();
+ foreach (var run in element.Runs)
+ {
+ var textRunClassification = this.classificationTypeRegistryService.GetClassificationType(run.ClassificationTypeName);
+
+ //var wpfRun = new Run()
+ //{
+ // // Set colors from the specific classification type's text run properties.
+ // Background = viewTextRunProperties.BackgroundBrush,
+ // BaselineAlignment = viewTextRunProperties.BaselineAlignment,
+ // Foreground = viewTextRunProperties.ForegroundBrush,
+ // Text = run.Text,
+ // TextDecorations = viewTextRunProperties.TextDecorations,
+ // TextEffects = viewTextRunProperties.TextEffects,
+
+ // // Set font properties from Editor Tooltips category so we match other tooltips.
+ // FontSize = tooltipTextRunProperties.FontRenderingEmSize,
+ // FontFamily = tooltipTextRunProperties.Typeface.FontFamily,
+ // FontStretch = tooltipTextRunProperties.Typeface.Stretch,
+ // FontStyle = tooltipTextRunProperties.Typeface.Style,
+ // FontWeight = tooltipTextRunProperties.Typeface.Weight
+ //};
+ var classy = textRunClassification.Classification;
+ var color = classy.GetHashCode ().ToString ("X");
+ color = color.Substring (2);
+ markup.AppendLine ($"<span foreground=\"#{color}\">{run.Text}</span>");
+ }
+ textBlock.Markup = markup.ToString ();
+
+ return textBlock as TView;
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/ViewElementFactories/WpfContainerElementViewElementFactory.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/ViewElementFactories/WpfContainerElementViewElementFactory.cs
new file mode 100644
index 0000000000..759d3ac921
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/ViewElementFactories/WpfContainerElementViewElementFactory.cs
@@ -0,0 +1,58 @@
+namespace Microsoft.VisualStudio.Text.AdornmentLibrary.ToolTip.Implementation
+{
+ using System;
+ using System.ComponentModel.Composition;
+ using System.Text;
+ using UIElement = Xwt.Widget;
+ using Microsoft.VisualStudio.Text.Adornments;
+ using Microsoft.VisualStudio.Text.Editor;
+ using Microsoft.VisualStudio.Utilities;
+ using Xwt;
+
+ [Export(typeof(IViewElementFactory))]
+ [Name("default ContainerElement to UIElement")]
+ [TypeConversion(from: typeof(ContainerElement), to: typeof(UIElement))]
+ [Order]
+ internal sealed class WpfContainerElementViewElementFactory : IViewElementFactory
+ {
+ [Import]
+ internal IViewElementFactoryService viewElementFactoryService;
+
+ public TView CreateViewElement<TView>(ITextView textView, object model) where TView : class
+ {
+ // Should never happen if the service's code is correct, but it's good to be paranoid.
+ if (typeof(TView) != typeof(UIElement) || !(model is ContainerElement container))
+ {
+ throw new ArgumentException($"Invalid type conversion. Unsupported {nameof(model)} or {nameof(TView)} type");
+ }
+
+ VBox containerControl;
+
+ if (container.Style == ContainerElementStyle.Stacked)
+ {
+ containerControl = new VBox();
+ }
+ else
+ {
+ containerControl = new VBox ();//TODO
+ }
+
+ containerControl.HorizontalPlacement = WidgetPlacement.Start;
+ containerControl.VerticalPlacement = WidgetPlacement.Start;
+
+ var automationNameBuffer = new StringBuilder();
+
+ foreach (var element in container.Elements)
+ {
+ var convertedElement = this.viewElementFactoryService.CreateViewElement<UIElement>(textView, element);
+
+ if (convertedElement != null)
+ {
+ containerControl.PackStart(convertedElement);
+ }
+ }
+
+ return containerControl as TView;
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/ViewElementFactories/WpfImageElementViewElementFactory.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/ViewElementFactories/WpfImageElementViewElementFactory.cs
new file mode 100644
index 0000000000..56b4876a43
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/ViewElementFactories/WpfImageElementViewElementFactory.cs
@@ -0,0 +1,39 @@
+namespace Microsoft.VisualStudio.Text.AdornmentLibrary.ToolTip.Implementation
+{
+ using System;
+ using System.ComponentModel.Composition;
+ using System.Windows;
+ using Microsoft.VisualStudio.Core.Imaging;
+ using Microsoft.VisualStudio.Imaging;
+ using Microsoft.VisualStudio.Text.Adornments;
+ using Microsoft.VisualStudio.Text.Editor;
+ using Microsoft.VisualStudio.Utilities;
+ using UIElement = Xwt.Widget;
+
+ [Export(typeof(IViewElementFactory))]
+ [Name("default ImageElement to UIElement")]
+ [TypeConversion(from: typeof(ImageElement), to: typeof(UIElement))]
+ [Order]
+ internal sealed class WpfImageElementViewElementFactory : IViewElementFactory
+ {
+ public TView CreateViewElement<TView>(ITextView textView, object model) where TView : class
+ {
+ // Should never happen if the service's code is correct, but it's good to be paranoid.
+ if (typeof(TView) != typeof(UIElement) || !(model is ImageElement element))
+ {
+ throw new ArgumentException($"Invalid type conversion. Unsupported {nameof(model)} or {nameof(TView)} type");
+ }
+
+ var imageElement = new Xwt.ImageView ();
+
+ imageElement.Image = MonoDevelop.Ide.ImageService.GetIcon ("md-monodevelop");
+
+ // Add a slight margin so we don't contact any ClassifiedTextElements directly following us.
+ imageElement.Margin = new Xwt.WidgetSpacing (4, 4, 4, 4);
+ imageElement.HorizontalPlacement = Xwt.WidgetPlacement.Start;
+ imageElement.HorizontalPlacement = Xwt.WidgetPlacement.Start;
+
+ return imageElement as TView;
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/WpfToolTipPresenterProvider.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/WpfToolTipPresenterProvider.cs
new file mode 100644
index 0000000000..1e8bdf160b
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfToolTipAdornment/WpfToolTipPresenterProvider.cs
@@ -0,0 +1,45 @@
+namespace Microsoft.VisualStudio.Text.AdornmentLibrary.ToolTip.Implementation
+{
+ using System.ComponentModel.Composition;
+ using Microsoft.VisualStudio.Text.Adornments;
+ using Microsoft.VisualStudio.Text.Editor;
+ using Microsoft.VisualStudio.Utilities;
+
+ [Export(typeof(IToolTipPresenterFactory))]
+ [Name("default")]
+ [ContentType("text")]
+ [Order]
+ internal sealed class WpfToolTipPresenterProvider : IToolTipPresenterFactory
+ {
+ [Import]
+ internal IViewElementFactoryService viewElementFactoryService;
+
+ [Import]
+ internal IObscuringTipManager obscuringTipManager;
+
+ [Import]
+ internal ToolTipStyleFactory styleFactory;
+
+ public IToolTipPresenter Create(ITextView textView, ToolTipParameters parameters)
+ {
+ if (parameters.TrackMouse)
+ {
+ return new MouseTrackingWpfToolTipPresenter(
+ this.viewElementFactoryService,
+ this.obscuringTipManager,
+ textView,
+ parameters,
+ this.styleFactory.Style);
+ }
+ else
+ {
+ return new SpanTrackingWpfToolTipPresenter(
+ this.viewElementFactoryService,
+ this.obscuringTipManager,
+ textView,
+ parameters,
+ this.styleFactory.Style);
+ }
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfView/PopupAgent.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfView/PopupAgent.cs
new file mode 100644
index 0000000000..e6ad239284
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfView/PopupAgent.cs
@@ -0,0 +1,794 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+//
+// This file contain implementations details that are subject to change without notice.
+// Use at your own risk.
+//
+namespace Microsoft.VisualStudio.Text.Editor.Implementation
+{
+ using System;
+ using System.Windows;
+ using System.Windows.Media;
+ using Microsoft.VisualStudio.Text;
+ using Microsoft.VisualStudio.Text.Adornments;
+ using Microsoft.VisualStudio.Text.Editor;
+ using Microsoft.VisualStudio.Text.Formatting;
+ using Microsoft.VisualStudio.Text.Outlining;
+ using Microsoft.VisualStudio.Text.Utilities;
+ using System.Windows.Input;
+ using System.Collections.Generic;
+ using Xwt;
+ using Mono.TextEditor;
+ using MonoDevelop.Components;
+ using Rect = Xwt.Rectangle;
+
+ class PopupAgent : ISpaceReservationAgent
+ {
+ internal readonly Mono.TextEditor.MonoTextEditor _textView;
+ internal readonly ISpaceReservationManager _manager;
+ internal ITrackingSpan _visualSpan;
+ internal PopupStyles _style;
+ internal Widget _mouseContainer;
+ internal readonly PopupOrWindowContainer _popup;
+ private const int MaxPopupCacheSize = 10;
+ private const double BelowTheLineBufferHint = 3.0;
+
+ public PopupAgent(Mono.TextEditor.MonoTextEditor textView, ISpaceReservationManager manager, ITrackingSpan visualSpan, PopupStyles style, Widget content)
+ {
+ if (textView == null)
+ throw new ArgumentNullException("textView");
+ if (manager == null)
+ throw new ArgumentNullException("manager");
+ if (visualSpan == null)
+ throw new ArgumentNullException("visualSpan");
+ if (((int)style & ~(0xff)) != 0) //Union of all the legal style bits.
+ throw new ArgumentOutOfRangeException("style");
+ if (content == null)
+ throw new ArgumentNullException("content");
+ if ((style & PopupStyles.DismissOnMouseLeaveText) != 0 && (style & PopupStyles.DismissOnMouseLeaveTextOrContent) != 0)
+ throw new ArgumentException("Can't specify both PopupStyles.DismissOnMouseLeaveText and PopupStyles.DismissOnMouseLeaveTextOrContent", "style");
+
+ _textView = textView;
+ _manager = manager;
+ _visualSpan = visualSpan;
+ _style = style;
+
+ var popupCache = textView.Properties.GetOrCreateSingletonProperty<Dictionary<WeakReferenceForDictionaryKey, PopupOrWindowContainer>>(
+ () => new Dictionary<WeakReferenceForDictionaryKey, PopupOrWindowContainer>(MaxPopupCacheSize));
+
+ if (!popupCache.TryGetValue(new WeakReferenceForDictionaryKey(content), out _popup))
+ {
+ _popup = PopupOrWindowContainer.Create(content, _textView.VisualElement);
+
+ if (popupCache.Count == MaxPopupCacheSize)
+ {
+ popupCache.Clear();
+ }
+ popupCache.Add(new WeakReferenceForDictionaryKey(content), _popup);
+ }
+ }
+
+ public void SetVisualSpan(ITrackingSpan visualSpan)
+ {
+ _visualSpan = visualSpan;
+ }
+
+ #region ISpaceReservationAgent Members
+ public Geometry PositionAndDisplay(Geometry reservedSpace)
+ {
+ //This method should only be called from the popup manager (which should never call it when the containing
+ //view is hidden or in the middle of a layout).
+
+ //This method does not support virtual whitespace positioning. An attempt to support it by using caret position was introduced, but
+ //regressed the behavior that popup should stay in place as the caret moves. If in the future we need to support virtual whitespace,
+ //consider using virtual span instead.
+
+ //Update the visual span to the current snapshot.
+ SnapshotSpan visualSpan = _visualSpan.GetSpan(_textView.TextSnapshot);
+
+ // If the style indicates that we should dismiss when the mouse leaves the span of the text, then we should make an
+ // initial check to make sure the mouse starts-off in the span. If not, we should fail to position.
+ if ((_style & (PopupStyles.DismissOnMouseLeaveText | PopupStyles.DismissOnMouseLeaveTextOrContent)) != 0)
+ {
+ _textView.VisualElement.GetPointer(out int x, out int y);
+ if (this.ShouldClearToolTipOnMouseMove(new Point(x, y)))
+ return null;
+ }
+
+ Rect? spanRectangle = null;
+ if (visualSpan.Length > 0)
+ {
+ double left = double.MaxValue;
+ double top = double.MaxValue;
+ double right = double.MinValue;
+ double bottom = double.MinValue;
+
+ var bounds = _textView.TextViewLines.GetNormalizedTextBounds(visualSpan);
+ foreach (var bound in bounds)
+ {
+ left = Math.Min(left, bound.Left);
+ top = Math.Min(top, bound.TextTop);
+ right = Math.Max(right, bound.Right);
+ bottom = Math.Max(bottom, bound.TextBottom);
+ }
+
+ // If the start of the span lies within the view, use that instead of the left-bound of the span as a whole.
+ // This will cause popups to be left-aligned with the start of their span, if at all possible.
+ var startLine = _textView.TextViewLines.GetTextViewLineContainingBufferPosition(visualSpan.Start);
+ if (startLine != null)
+ {
+ var startPointBounds = startLine.GetExtendedCharacterBounds(visualSpan.Start);
+ if ((startPointBounds.Left < right) && (startPointBounds.Left >= _textView.ViewportLeft) && (startPointBounds.Left < _textView.ViewportRight))
+ {
+ left = startPointBounds.Left;
+ }
+ }
+
+ //Special case handling for when the end of the span is at the start of a line.
+ ITextViewLine line = _textView.TextViewLines.GetTextViewLineContainingBufferPosition(visualSpan.End);
+ if ((line != null) && (line.Start == visualSpan.End))
+ {
+ bottom = Math.Max(bottom, line.TextBottom);
+ }
+
+ if (left < right)
+ {
+ spanRectangle = new Rect(left, top, right - left, bottom - top);
+ }
+ }
+ else
+ {
+ //visualSpan is zero length so the default MarkerGeometry will be null. Create a custom marker geometry based on the location.
+ ITextViewLine line = _textView.TextViewLines.GetTextViewLineContainingBufferPosition(visualSpan.Start);
+ if (line != null)
+ {
+ TextBounds bounds = line.GetCharacterBounds(visualSpan.Start);
+ spanRectangle = new Rect(bounds.Left, bounds.TextTop, 0.0, bounds.TextHeight);
+ }
+ }
+
+ if (spanRectangle.HasValue)
+ {
+ //Get the portion of the span geometry that is inside the view.
+ Rect viewRect = new Rect(_textView.ViewportLeft, _textView.ViewportTop, _textView.ViewportWidth, _textView.ViewportHeight);
+
+ Rect spanRect = spanRectangle.Value;
+ spanRect = spanRect.Intersect(viewRect);
+
+ if (spanRect != default(Rect))
+ {
+ // Determine two different rectangles for the span. One is the span in its raw form. The other is a "guess" at
+ // what the already-reserved space around the span will be. We have a very-prevalent space reservation agent (the
+ // current line agent) that reserves the current line plus a 3-pixel buffer below the line. We'll optimistically
+ // guess that this agent might be in-play and attempt to avoid it.
+ Rect spanRectWithBuffer = new Rect(spanRect.Left, spanRect.Top, spanRect.Right - spanRect.Left, spanRect.Bottom - spanRect.Top + BelowTheLineBufferHint);
+
+ //Some of the text associated with the popup is visible, show the popup.
+ Rect spanRectInScreenCoordinates = new Rect(this.GetScreenPointFromTextXY(spanRect.Left, spanRect.Top),
+ this.GetScreenPointFromTextXY(spanRect.Right, spanRect.Bottom));
+ Rect spanRectWithBufferInScreenCoordinates = new Rect(this.GetScreenPointFromTextXY(spanRectWithBuffer.Left, spanRectWithBuffer.Top),
+ this.GetScreenPointFromTextXY(spanRectWithBuffer.Right, spanRectWithBuffer.Bottom));
+ Rect screenRect = Xwt.Desktop.GetScreenAtLocation(spanRectInScreenCoordinates.TopLeft).Bounds;//TODO: Check if we should use VisualBounds
+
+ Size desiredSize = _popup.Size;
+ //The popup size specified in deivice pixels. Convert these to logical
+ //pixels for purposes of calculating the actual size of the popup.
+ //TODO desiredSize = new Size (desiredSize.Width / WpfHelper.DeviceScaleX, desiredSize.Height / WpfHelper.DeviceScaleY);
+ desiredSize = new Size(desiredSize.Width, desiredSize.Height);
+
+ PopupStyles alternateStyle = _style ^ PopupStyles.PreferLeftOrTopPosition;
+
+ Rect reservedRect = reservedSpace.Bounds;
+ Point topLeft = new Point(Math.Min(spanRectInScreenCoordinates.Left, reservedRect.Left),
+ Math.Min(spanRectInScreenCoordinates.Top, reservedRect.Top));
+ Point bottomRight = new Point(Math.Max(spanRectInScreenCoordinates.Right, reservedRect.Right),
+ Math.Max(spanRectInScreenCoordinates.Bottom, reservedRect.Bottom));
+ reservedRect = new Rect(topLeft, bottomRight);
+
+ //There are 6 possible locations for the popup. The order of preference is determined by the presence of the
+ //'PositionClosest' PopupStyle.
+ //
+ // Without 'PositionClosest':
+ // 1 .. On the desired side of the span.
+ // 2 .. On the desired side of the span with a bit of buffer.
+ // 3 .. on the desired side of the reserved rectangle.
+ // 4 .. On the alternate side of the span.
+ // 5 .. On the alternate side of the span with a bit of buffer.
+ // 6 .. on the alternate side of the reserved rectangle.
+ //
+ // With 'PositionClosest':
+ // 1 .. On the desired side of the span.
+ // 2 .. On the desired side of the span with a bit of buffer.
+ // 3 .. On the alternate side of the span.
+ // 4 .. On the alternate side of the span with a bit of buffer.
+ // 5 .. on the desired side of the reserved rectangle.
+ // 6 .. on the alternate side of the reserved rectangle.
+ //
+ //Try each location till we get a winner.
+ // A location is a winner if it is disjoint from the original reserved rect and
+ // the edges of the screen.
+
+ Tuple<PopupStyles, Rect>[] positionChoices;
+ if (reservedRect != default(Rect))
+ {
+ if ((_style & PopupStyles.PositionClosest) == 0)
+ {
+ positionChoices = new Tuple<PopupStyles, Rect>[]
+ {
+ new Tuple<PopupStyles,Rect>(_style, spanRectInScreenCoordinates),
+ new Tuple<PopupStyles,Rect>(_style, spanRectWithBufferInScreenCoordinates),
+ new Tuple<PopupStyles,Rect>(_style, reservedRect),
+ new Tuple<PopupStyles,Rect>(alternateStyle, spanRectInScreenCoordinates),
+ new Tuple<PopupStyles,Rect>(alternateStyle, spanRectWithBufferInScreenCoordinates),
+ new Tuple<PopupStyles,Rect>(alternateStyle, reservedRect),
+ };
+ }
+ else
+ {
+ positionChoices = new Tuple<PopupStyles, Rect>[]
+ {
+ new Tuple<PopupStyles,Rect>(_style, spanRectInScreenCoordinates),
+ new Tuple<PopupStyles,Rect>(_style, spanRectWithBufferInScreenCoordinates),
+ new Tuple<PopupStyles,Rect>(alternateStyle, spanRectInScreenCoordinates),
+ new Tuple<PopupStyles,Rect>(alternateStyle, spanRectWithBufferInScreenCoordinates),
+ new Tuple<PopupStyles,Rect>(_style, reservedRect),
+ new Tuple<PopupStyles,Rect>(alternateStyle, reservedRect),
+ };
+ }
+ }
+ else
+ {
+ positionChoices = new Tuple<PopupStyles, Rect>[]
+ {
+ new Tuple<PopupStyles,Rect>(_style, spanRectInScreenCoordinates),
+ new Tuple<PopupStyles,Rect>(_style, spanRectWithBufferInScreenCoordinates),
+ new Tuple<PopupStyles,Rect>(alternateStyle, spanRectInScreenCoordinates),
+ new Tuple<PopupStyles,Rect>(alternateStyle, spanRectWithBufferInScreenCoordinates),
+ };
+ }
+
+ Rect location = Rect.Zero;
+ foreach (var choice in positionChoices)
+ {
+ Rect locationToTry = GetLocation(choice.Item1, desiredSize, spanRectInScreenCoordinates, choice.Item2, screenRect);
+ if (DisjointWithPadding(reservedSpace, locationToTry) && ContainsWithPadding(screenRect, locationToTry))
+ {
+ location = locationToTry;
+ _style = choice.Item1;
+ break;
+ }
+ }
+
+ // If we couldn't locate a place to live, tell the manager we want to go away.
+ if (location == Rect.Zero)
+ return null;
+
+ if (!_popup.IsVisible)
+ this.RegisterForEvents();
+
+ _popup.DisplayAt(location.TopLeft);
+
+ GeometryGroup requestedSpace = new GeometryGroup();
+ requestedSpace.Children.Add(new RectangleGeometry(spanRectInScreenCoordinates));
+ requestedSpace.Children.Add(new RectangleGeometry(location));
+
+ return requestedSpace;
+ }
+ }
+
+ //The text associated with the popup visualSpan is not visible: tell the manager we want to go away.
+ return null;
+ }
+
+ public bool IsMouseOver
+ {
+ get
+ {
+ Gdk.Display.Default.GetPointer(out int x, out int y);
+ return _popup.IsVisible ? _popup.Content.ScreenBounds.Contains(x, y) : false;
+ }
+ }
+
+ public void Hide()
+ {
+ if (_popup.IsVisible)
+ {
+ _popup.Hide();
+
+ this.UnregisterForEvents();
+ }
+ }
+
+ public bool HasFocus
+ {
+ get
+ {
+ return _popup.IsVisible && _popup.IsKeyboardFocusWithin;
+ }
+ }
+
+ public event EventHandler LostFocus;
+ public event EventHandler GotFocus;
+
+ #endregion
+
+ private void RegisterForEvents()
+ {
+ // For tooltips with style DismissOnMouseLeave
+ if ((_style & (PopupStyles.DismissOnMouseLeaveText | PopupStyles.DismissOnMouseLeaveTextOrContent)) != 0)
+ {
+ _textView.VisualElement.MotionNotifyEvent += this.OnMouseMove;
+
+ //TODO: This used to be Mouse.DirectlyOver, is it ok just use popup.Content?
+ _mouseContainer = _popup.Content;
+ if (_mouseContainer != null)
+ {
+ _mouseContainer.MouseExited += this.OnMouseLeave;
+ }
+ }
+
+ _textView.LostAggregateFocus += this.OnViewFocusLost;
+ _popup.Content.LostFocus += this.OnContentLostFocus;
+ _popup.Content.GotFocus += this.OnContentGotFocus;
+
+ var content = _popup.Content as Widget;
+ if (content != null)
+ {
+ content.BoundsChanged += this.OnContentSizeChanged;
+ }
+
+ // So we can dismiss the tooltip on window move / sizing.
+ var hostWindow = MonoDevelop.Ide.IdeApp.Workbench.RootWindow;
+ if (hostWindow != null) // for tests
+ {
+ hostWindow.ConfigureEvent += OnLocationChanged;
+ }
+
+ // Register to be notified when outlining regions are collapsed.
+ if (this.OutliningManager != null)
+ {
+ this.OutliningManager.RegionsCollapsed += this.OnOutliningManager_RegionsCollapsed;
+ }
+ }
+
+ private void UnregisterForEvents()
+ {
+ // For tooltips with style DismissOnMouseLeave
+ if ((_style & (PopupStyles.DismissOnMouseLeaveText | PopupStyles.DismissOnMouseLeaveTextOrContent)) != 0)
+ {
+ _textView.VisualElement.MotionNotifyEvent -= this.OnMouseMove;
+ if (_mouseContainer != null)
+ {
+ _mouseContainer.MouseExited -= this.OnMouseLeave;
+ }
+ }
+
+ _textView.LostAggregateFocus -= this.OnViewFocusLost;
+ _popup.Content.LostFocus -= this.OnContentLostFocus;
+ _popup.Content.GotFocus -= this.OnContentGotFocus;
+
+ var content = _popup.Content as Xwt.Widget;
+ if (content != null)
+ {
+ content.BoundsChanged -= this.OnContentSizeChanged;
+ }
+
+ var hostWindow = MonoDevelop.Ide.IdeApp.Workbench.RootWindow;
+ if (hostWindow != null) // for tests
+ {
+ hostWindow.ConfigureEvent -= OnLocationChanged;
+ }
+
+ if (this.OutliningManager != null)
+ {
+ this.OutliningManager.RegionsCollapsed -= this.OnOutliningManager_RegionsCollapsed;
+ }
+ }
+
+ private IOutliningManager OutliningManager
+ {
+ get
+ {
+ if ((_textView.TextViewModel != null) && (_textView.ComponentContext.OutliningManagerService != null))
+ {
+ return _textView.ComponentContext.OutliningManagerService.GetOutliningManager(_textView);
+ }
+
+ return null;
+ }
+ }
+
+ #region Event handlers
+ void OnMouseMove(object sender, Gtk.MotionNotifyEventArgs e)
+ {
+ if (_popup.IsVisible)
+ {
+ Point mousePt = new Point(e.Event.X, e.Event.Y); //TODO: Check if we have to move to screen cordinate system
+
+ if (this.ShouldClearToolTipOnMouseMove(mousePt))
+ {
+ _manager.RemoveAgent(this);
+ }
+ }
+ }
+
+ void OnMouseLeave(object sender, EventArgs e)
+ {
+ //TODO: This method, well whole MouseLeave logic is much simplefied in our case
+ //We just support on Popup mouse leave while WPF supports also DismissOnMouseLeaveText
+ if (_mouseContainer != null)
+ {
+ _mouseContainer.MouseExited -= this.OnMouseLeave;
+ _mouseContainer = null;
+ }
+
+ Widget newContainer = null;//e.MouseDevice.Target;
+ bool shouldRemoveAgent = false;
+
+ // First, check to see if the mouse left the view entirely.
+ if ((newContainer == null) || !_textView.IsMouseOverViewOrAdornments)
+ {
+ //Mouse moved over a non-WPF element or a WPF element not associated with the view.
+ //Remove the agent.
+ shouldRemoveAgent = true;
+ }
+ else if ((_style & PopupStyles.DismissOnMouseLeaveText) != 0)
+ {
+ // The mouse left the element over which it was originally positioned. This may or
+ // may not mean that the mouse left the span of text to which the popup is bound.
+ _textView.VisualElement.GetPointer (out int x, out int y);
+ if (this.ShouldClearToolTipOnMouseMove(new Point(x,y)))
+ {
+ shouldRemoveAgent = true;
+ }
+ }
+
+ // If we determined that we should remove the popup agent for any reason, do so.
+ // Otherwise, re-subscribe to MouseLeave events on the new mouse-over target.
+ if (shouldRemoveAgent)
+ {
+ _manager.RemoveAgent(this);
+ }
+ else
+ {
+ _mouseContainer = newContainer;
+ _mouseContainer.MouseExited += this.OnMouseLeave;
+ }
+ }
+
+ void OnContentLostFocus(object sender, EventArgs e)
+ {
+ EventHandler lostFocus = this.LostFocus;
+ if (lostFocus != null)
+ lostFocus(sender, e);
+ }
+
+ void OnContentGotFocus(object sender, EventArgs e)
+ {
+ EventHandler gotFocus = this.GotFocus;
+ if (gotFocus != null)
+ gotFocus(sender, e);
+ }
+
+ void OnContentSizeChanged(object sender, EventArgs e)
+ {
+ _textView.QueueSpaceReservationStackRefresh();
+ }
+
+ /// <summary>
+ /// Handle focus change events for TextView and tooltip popup window. If they both lose
+ /// focus, then dismiss the tooltip.
+ /// </summary>
+ void OnViewFocusLost(object sender, EventArgs e)
+ {
+ if (_popup.IsVisible)
+ {
+ _manager.RemoveAgent(this);
+ }
+ }
+
+ /// <summary>
+ /// Handle the LocationChanged event for the window hosting the view and dismiss the
+ /// tooltip.
+ /// </summary>
+ void OnLocationChanged(object sender, EventArgs e)
+ {
+ _manager.RemoveAgent(this);
+ }
+
+ void OnOutliningManager_RegionsCollapsed(object sender, RegionsCollapsedEventArgs e)
+ {
+ if (_popup.IsVisible)
+ {
+ foreach (ICollapsed collapsed in e.CollapsedRegions)
+ {
+ if (_visualSpan.TextBuffer != collapsed.Extent.TextBuffer)
+ continue;
+
+ ITextSnapshot snapshot = _visualSpan.TextBuffer.CurrentSnapshot;
+ SnapshotSpan visualSnapSpan = _visualSpan.GetSpan(snapshot);
+ SnapshotSpan collapsedSnapSpan = collapsed.Extent.GetSpan(snapshot);
+ if (visualSnapSpan.IntersectsWith(collapsedSnapSpan))
+ {
+ _manager.RemoveAgent(this);
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ internal bool ShouldClearToolTipOnMouseMove(Point mousePt)
+ {
+ _textView.VisualElement.GetPointer(out int x, out int y);
+ var topLeft = _textView.VisualElement.GetScreenCoordinates(new Gdk.Point(0, 0));
+ var bottomRight = _textView.VisualElement.GetScreenCoordinates(new Gdk.Point(_textView.VisualElement.WidthRequest, _textView.VisualElement.HeightRequest));
+ //TODO: Test if this is correct
+ if (!new Rect(topLeft.ToXwtPoint(), bottomRight.ToXwtPoint()).Contains(x, y))
+ return true;
+
+ return this.InnerShouldClearToolTipOnMouseMove(mousePt);
+ }
+
+ internal bool InnerShouldClearToolTipOnMouseMove(Point mousePt)
+ {
+ if ((mousePt.X >= 0.0) && (mousePt.X < _textView.ViewportWidth) &&
+ (mousePt.Y >= 0.0) && (mousePt.Y < _textView.ViewportHeight))
+ {
+ ITextViewLine line = _textView.TextViewLines.GetTextViewLineContainingYCoordinate(mousePt.Y + _textView.ViewportTop);
+ if (line != null)
+ {
+ SnapshotSpan span = _visualSpan.GetSpan(_textView.TextSnapshot);
+ if (span.IntersectsWith(line.ExtentIncludingLineBreak))
+ {
+ double x = mousePt.X + _textView.ViewportLeft;
+
+ //The mouse could be over text in the line ... see if it is over the tip
+ //This code essentially duplicates the logic in WpfTextView with respect
+ //to determining the logical position of the hover event.
+ int? position = line.GetBufferPositionFromXCoordinate(x, true);
+ if ((!position.HasValue) && (line.LineBreakLength == 0) && line.IsLastTextViewLineForSnapshotLine)
+ {
+ //For purposes of clearing tips, pretend the last line in the buffer
+ //actually is padded by the EndOfLineWidth (even though it is not).
+ if ((line.TextRight <= x) && (x < line.TextRight + line.EndOfLineWidth))
+ {
+ //position is at the end of the buffer. Return true if the span
+ //doesn't extend to the end of the buffer.
+ return (span.End < _textView.TextSnapshot.Length);
+ }
+ }
+
+ if (position.HasValue)
+ {
+ //A span that ends at the end of the buffer contains a position at the end of the buffer.
+ return !span.Contains(position.Value);
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ internal Point GetScreenPointFromTextXY(double x, double y)
+ {
+ return _textView.VisualElement.GetScreenCoordinates(new Gdk.Point((int)(x - _textView.ViewportLeft), (int)(y - _textView.ViewportTop))).ToXwtPoint();
+ }
+
+ #region Static positioning helpers
+ internal static Rect GetLocation(PopupStyles style, Size desiredSize, Rect spanRectInScreenCoordinates, Rect reservedRect, Rect screenRect)
+ {
+ if ((style & PopupStyles.PositionLeftOrRight) != 0)
+ {
+ //Position left or right
+ double xPosition = ((style & PopupStyles.PreferLeftOrTopPosition) != 0)
+ ? (reservedRect.Left - desiredSize.Width) //To the left
+ : reservedRect.Right; //To the right
+
+ double yPosition = ((style & PopupStyles.RightOrBottomJustify) != 0)
+ ? (spanRectInScreenCoordinates.Bottom - desiredSize.Height) //Bottom justified
+ : spanRectInScreenCoordinates.Top; //Top justified
+
+ return ShiftVerticallyToFitScreen(xPosition, yPosition,
+ desiredSize, screenRect);
+ }
+ else
+ {
+ //Position above or below.
+ double xPosition = ((style & PopupStyles.RightOrBottomJustify) != 0)
+ ? (spanRectInScreenCoordinates.Right - desiredSize.Width) //Right justified
+ : spanRectInScreenCoordinates.Left; //Left justified
+
+ double yPosition = ((style & PopupStyles.PreferLeftOrTopPosition) != 0)
+ ? (reservedRect.Top - desiredSize.Height) //Above
+ : reservedRect.Bottom; //Below
+
+ return ShiftHorizontallyToFitScreen(xPosition, yPosition,
+ desiredSize, screenRect);
+ }
+ }
+
+ internal static Rect ShiftHorizontallyToFitScreen(double x, double y, Size desiredSize, Rect screenRect)
+ {
+ if ((x + desiredSize.Width) > screenRect.Right)
+ x = screenRect.Right - desiredSize.Width;
+
+ if (x < screenRect.Left)
+ x = screenRect.Left;
+
+ return new Rect(x, y, desiredSize.Width, desiredSize.Height);
+ }
+
+ internal static Rect ShiftVerticallyToFitScreen(double x, double y, Size desiredSize, Rect screenRect)
+ {
+ if ((y + desiredSize.Height) > screenRect.Bottom)
+ y = screenRect.Bottom - desiredSize.Height;
+
+ if (y < screenRect.Top)
+ y = screenRect.Top;
+
+ return new Rect(x, y, desiredSize.Width, desiredSize.Height);
+ }
+
+ internal static bool DisjointWithPadding(Geometry reserved, Rect location)
+ {
+ double left = location.Left + 0.1;
+ double top = location.Top + 0.1;
+ double width = location.Right - 0.1 - left;
+ double height = location.Bottom - 0.1 - top;
+
+ if ((width > 0.0) && (height > 0.0))
+ {
+ Geometry insetLocation = new RectangleGeometry(new Rect(left, top, width, height));
+ return reserved.Bounds.IntersectsWith(insetLocation.Bounds);//TODO: This was simpliefied
+ }
+ else
+ return true;
+ }
+
+ internal static bool ContainsWithPadding(Rect outer, Rect inner)
+ {
+ return (outer.Left - 0.01 <= inner.Left) && (inner.Right <= outer.Right + 0.01) &&
+ (outer.Top - 0.01 <= inner.Top) && (inner.Bottom <= outer.Bottom + 0.01);
+ }
+ #endregion
+
+ internal abstract class PopupOrWindowContainer
+ {
+ private Widget _content;
+ protected Mono.TextEditor.MonoTextEditor _placementTarget;
+
+ public static PopupOrWindowContainer Create(Widget content, Mono.TextEditor.MonoTextEditor placementTarget)
+ {
+ return new PopUpContainer(content, placementTarget);
+ }
+
+ public PopupOrWindowContainer(Widget content, Mono.TextEditor.MonoTextEditor placementTarget)
+ {
+ _content = content;
+ _placementTarget = placementTarget;
+ }
+
+ public abstract bool IsVisible { get; }
+
+ public Widget Content { get { return _content; } }
+
+ public abstract bool IsKeyboardFocusWithin { get; }
+
+ public abstract void DisplayAt(Point point);
+ public abstract void Hide();
+ public abstract Size Size { get; }
+ }
+
+ private class PopUpContainer : PopupOrWindowContainer
+ {
+#if WINDOWS
+ private class NoTopmostPopup : XwtThemedPopup
+ {
+ protected override void OnShown ()
+ {
+ WpfHelper.SetNoTopmost(this.Child);
+ base.OnShown ();
+ }
+ }
+
+ public static void SetNoTopmost(Visual visual)
+ {
+ if (visual != null)
+ {
+ HwndSource source = PresentationSource.FromVisual(visual) as HwndSource;
+ if (source != null)
+ {
+ const int SWP_NOMOVE = 0x02;
+ const int SWP_NOSIZE = 0x01;
+ const int SWP_NOACTIVATE = 0x10;
+ const int HWND_NOTOPMOST = -2;
+ NativeMethods.SetWindowPos(source.Handle, (IntPtr)HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+ }
+ }
+ }
+ XwtThemedPopup _popup = new NoTopmostPopup ();
+#else
+ XwtThemedPopup _popup = new XwtThemedPopup();
+#endif
+
+ // WPF popup doesn't detach its child from the visual tree when the popup is not open,
+ // even if we assign Child property to null. That prevents reusing of the popup content.
+ // And assiging popup's Child to null when the popup is still open is expensive
+ // as it requires full subtree traversal to update relevant properties, see
+ // FrameworkElement.ChangeLogicalParent() sources.
+ // The solution is to have a neutral control as the popup's child and attach the real content
+ // to that control instead of popup itself. Then we can safely (and more effectively)
+ // detach popup content when popup has been closed and so significantly speed up
+ // popup closing.
+ FrameBox _popupContentContainer = new FrameBox();
+
+ public PopUpContainer(Widget content, Mono.TextEditor.MonoTextEditor placementTarget)
+ : base(content, placementTarget)
+ {
+ WindowTransparencyDecorator.Attach(_popup);//TODO: not sure we want this on all popus?
+ _popup.Content = _popupContentContainer;
+ _popup.Hidden += OnPopupClosed;
+ }
+
+ private void OnPopupClosed(object sender, EventArgs e)
+ {
+ _popupContentContainer.Content = null;
+ }
+
+ public override void DisplayAt(Point point)
+ {
+ //The horizontal and verical offsets are specified in terms of device pixels
+ //so convert logical pixel position in point to device pixels.
+ //_popup.HorizontalOffset = point.X * WpfHelper.DeviceScaleX;
+ //_popup.VerticalOffset = point.Y * WpfHelper.DeviceScaleY;
+ _popup.Location = point;
+
+ if (base.Content != _popupContentContainer.Content)
+ {
+ if (base.Content.Parent == null)
+ {
+ _popupContentContainer.Content = base.Content;
+ _popup.Show();
+ }
+ else
+ {
+ //The intended content still has a parent. Clear out the old content and hope things are better next time around.
+ _popupContentContainer.Content = null;
+ }
+ }
+ }
+
+ public override void Hide()
+ {
+ _popup.Hide();
+ }
+
+ public override bool IsVisible
+ {
+ get
+ {
+ return _popupContentContainer.Content != null;
+ }
+ }
+
+ public override Size Size
+ {
+ get
+ {
+ return ((IWidgetSurface)base.Content).GetPreferredSize();
+ }
+ }
+
+ public override bool IsKeyboardFocusWithin
+ {
+ get
+ {
+ return _popup.HasFocus;
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfView/SpaceReservationManager.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfView/SpaceReservationManager.cs
new file mode 100644
index 0000000000..49dee36724
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfView/SpaceReservationManager.cs
@@ -0,0 +1,244 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+//
+// This file contain implementations details that are subject to change without notice.
+// Use at your own risk.
+//
+namespace Microsoft.VisualStudio.Text.Editor.Implementation
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Windows;
+ using System.Windows.Media;
+ using Microsoft.VisualStudio.Text;
+ using Microsoft.VisualStudio.Text.Adornments;
+ using Microsoft.VisualStudio.Text.Editor;
+ using MonoDevelop.Components;
+
+ internal class SpaceReservationManager : ISpaceReservationManager
+ {
+ public readonly string Name;
+ public readonly int Rank;
+ private readonly Mono.TextEditor.MonoTextEditor _view;
+ private bool _hasAggregateFocus;
+ internal IList<ISpaceReservationAgent> _agents = new List<ISpaceReservationAgent>();
+
+ public SpaceReservationManager(string name, int rank, Mono.TextEditor.MonoTextEditor view)
+ {
+ this.Name = name;
+ this.Rank = rank;
+ _view = view;
+ _view.Closed += this.OnViewClosed;
+ }
+
+ #region ISpaceReservationManager Members
+ public ISpaceReservationAgent CreatePopupAgent(ITrackingSpan visualSpan, PopupStyles styles, Xwt.Widget content)
+ {
+ return new PopupAgent(_view, this, visualSpan, styles, content);
+ }
+
+ public void UpdatePopupAgent(ISpaceReservationAgent agent, ITrackingSpan visualSpan, PopupStyles styles)
+ {
+ if (agent == null)
+ throw new ArgumentNullException("agent");
+ if (visualSpan == null)
+ throw new ArgumentNullException("visualSpan");
+
+ PopupAgent popupAgent = agent as PopupAgent;
+ if (popupAgent == null)
+ throw new ArgumentException("The agent is not a PopupAgent", "agent");
+
+ popupAgent.SetVisualSpan(visualSpan);
+ popupAgent._style = styles;
+ this.CheckFocusChange();
+ _view.QueueSpaceReservationStackRefresh();
+ }
+
+ public ReadOnlyCollection<ISpaceReservationAgent> Agents
+ {
+ get { return new ReadOnlyCollection<ISpaceReservationAgent>(_agents); }
+ }
+
+ public void AddAgent(ISpaceReservationAgent agent)
+ {
+ if (agent == null)
+ throw new ArgumentNullException("agent");
+
+ _agents.Add(agent);
+ this.ChangeAgents(null, agent);
+ this.CheckFocusChange();
+ _view.QueueSpaceReservationStackRefresh();
+ }
+
+ public bool RemoveAgent(ISpaceReservationAgent agent)
+ {
+ if (agent == null)
+ throw new ArgumentNullException("agent");
+
+ if (_agents.Remove(agent))
+ {
+ this.ChangeAgents(agent, null);
+ this.CheckFocusChange();
+ _view.QueueSpaceReservationStackRefresh();
+
+ return true;
+ }
+
+ return false;
+ }
+
+ public event EventHandler<SpaceReservationAgentChangedEventArgs> AgentChanged;
+
+ public bool IsMouseOver
+ {
+ get
+ {
+ foreach (var agent in _agents)
+ {
+ if (agent.IsMouseOver)
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ public bool HasAggregateFocus
+ {
+ get
+ {
+ //We can't uses _hasAggregateFocus (the got focus event may not have reached the agent yet)
+ foreach (var agent in _agents)
+ {
+ if (agent.HasFocus)
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ public event EventHandler LostAggregateFocus;
+ public event EventHandler GotAggregateFocus;
+ #endregion
+
+ internal void ChangeAgents(ISpaceReservationAgent oldAgent, ISpaceReservationAgent newAgent)
+ {
+ if (oldAgent != null)
+ {
+ oldAgent.LostFocus -= OnAgentLostFocus;
+ oldAgent.GotFocus -= OnAgentGotFocus;
+ oldAgent.Hide();
+ }
+
+ EventHandler<SpaceReservationAgentChangedEventArgs> agentChanged = this.AgentChanged;
+ if (agentChanged != null)
+ agentChanged(this, new SpaceReservationAgentChangedEventArgs(oldAgent, newAgent));
+
+ if (newAgent != null)
+ {
+ newAgent.LostFocus += OnAgentLostFocus;
+ newAgent.GotFocus += OnAgentGotFocus;
+ }
+
+ _view.QueueSpaceReservationStackRefresh();
+ }
+
+ void OnAgentLostFocus(object sender, EventArgs e)
+ {
+ if (_hasAggregateFocus)
+ {
+ foreach (var agent in _agents)
+ {
+ if (agent.HasFocus)
+ return;
+ }
+
+ _hasAggregateFocus = false;
+ EventHandler lostAggregateFocus = this.LostAggregateFocus;
+ if (lostAggregateFocus != null)
+ lostAggregateFocus(sender, e);
+ }
+ }
+
+ void OnAgentGotFocus(object sender, EventArgs e)
+ {
+ if (!_hasAggregateFocus)
+ {
+ _hasAggregateFocus = true;
+ EventHandler gotAggregateFocus = this.GotAggregateFocus;
+ if (gotAggregateFocus != null)
+ gotAggregateFocus(sender, e);
+ }
+ }
+
+ /// <summary>
+ /// Handle the close event for TextView. If the view is closed, then dismiss all agents.
+ /// </summary>
+ void OnViewClosed(object sender, EventArgs e)
+ {
+ List<ISpaceReservationAgent> agentsToRemove = new List<ISpaceReservationAgent>();
+ agentsToRemove.AddRange (_agents);
+
+ foreach (ISpaceReservationAgent agent in agentsToRemove)
+ {
+ this.RemoveAgent (agent);
+ }
+
+ _view.Closed -= this.OnViewClosed;
+ }
+
+ internal void PositionAndDisplay(GeometryGroup reservedGeometry)
+ {
+ _view.GuardedOperations.CallExtensionPoint(this,
+ () =>
+ {
+ if (_agents.Count != 0)
+ {
+ if (_view.Visible)
+ {
+ for (int i = _agents.Count - 1; (i >= 0); --i)
+ {
+ ISpaceReservationAgent agent = _agents[i];
+
+ Geometry requestedGeometry = agent.PositionAndDisplay(reservedGeometry);
+ if (requestedGeometry == null)
+ {
+ _agents.RemoveAt(i);
+ this.ChangeAgents(agent, null);
+ }
+ else if (!requestedGeometry.IsEmpty())
+ reservedGeometry.Children.Add(requestedGeometry);
+ }
+ }
+ else
+ {
+ for (int i = _agents.Count - 1; (i >= 0); --i)
+ {
+ ISpaceReservationAgent agent = _agents[i];
+ _agents.RemoveAt(i);
+ this.ChangeAgents(agent, null);
+ }
+ }
+
+ this.CheckFocusChange();
+ }
+ });
+ }
+
+ private void CheckFocusChange()
+ {
+ bool newFocus = this.HasAggregateFocus;
+ if (_hasAggregateFocus != newFocus)
+ {
+ _hasAggregateFocus = newFocus;
+ EventHandler focusChangeHandler = _hasAggregateFocus ? this.GotAggregateFocus : this.LostAggregateFocus;
+
+ if (focusChangeHandler != null)
+ focusChangeHandler(this, new EventArgs());
+ }
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfView/SpaceReservationStack.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfView/SpaceReservationStack.cs
new file mode 100644
index 0000000000..fa403acdfe
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Impl/WpfView/SpaceReservationStack.cs
@@ -0,0 +1,129 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+//
+// This file contain implementations details that are subject to change without notice.
+// Use at your own risk.
+//
+namespace Microsoft.VisualStudio.Text.Editor.Implementation
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Windows.Media;
+ using Microsoft.VisualStudio.Text.Editor;
+
+ internal class SpaceReservationStack
+ {
+ #region Private Members
+ internal Dictionary<string, int> _orderedManagerDefinitions;
+
+ internal readonly Mono.TextEditor.MonoTextEditor _view;
+ internal readonly List<SpaceReservationManager> _managers = new List<SpaceReservationManager>();
+ bool _hasAggregateFocus;
+
+ void OnManagerLostFocus(object sender, EventArgs e)
+ {
+ if (_hasAggregateFocus)
+ {
+ foreach (var manager in _managers)
+ {
+ if (manager.HasAggregateFocus)
+ return;
+ }
+
+ _hasAggregateFocus = false;
+ _view.QueueAggregateFocusCheck();
+ }
+ }
+
+ void OnManagerGotFocus(object sender, EventArgs e)
+ {
+ _hasAggregateFocus = true;
+ _view.QueueAggregateFocusCheck();
+ }
+
+ #endregion // Private Members
+
+ public SpaceReservationStack(Dictionary<string, int> orderedManagerDefinitions, Mono.TextEditor.MonoTextEditor view)
+ {
+ _orderedManagerDefinitions = orderedManagerDefinitions;
+ _view = view;
+ }
+
+ public ISpaceReservationManager GetOrCreateManager(string name)
+ {
+ foreach (SpaceReservationManager manager in _managers)
+ {
+ if (manager.Name == name)
+ return manager;
+ }
+
+ int rank;
+ if (_orderedManagerDefinitions.TryGetValue(name, out rank))
+ {
+ SpaceReservationManager manager = new SpaceReservationManager(name, rank, _view);
+
+ int position = 0;
+ while (position < _managers.Count)
+ {
+ SpaceReservationManager existing = _managers[position];
+ if (existing.Rank > rank)
+ break;
+
+ ++position;
+ }
+
+ _managers.Insert(position, manager);
+ manager.LostAggregateFocus += OnManagerLostFocus;
+ manager.GotAggregateFocus += OnManagerGotFocus;
+
+ return manager;
+ }
+
+ return null;
+ }
+
+ public void Refresh()
+ {
+ GeometryGroup reservedGeometry = new GeometryGroup();
+
+ //Make a copy just in case some one in PositionAndDisplay attempts to create a new manager.
+ //We don't need to queue a new refresh because adding an empty manager won't affect what is displayed
+ //(& adding a agent to a manager will queue a refresh).
+ List<SpaceReservationManager> managers = new List<SpaceReservationManager>(_managers);
+ foreach (SpaceReservationManager manager in managers)
+ {
+ manager.PositionAndDisplay(reservedGeometry);
+ }
+ }
+
+ public bool IsMouseOver
+ {
+ get
+ {
+ foreach (var manager in _managers)
+ {
+ if (manager.IsMouseOver)
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ public bool HasAggregateFocus
+ {
+ get
+ {
+ //We can't uses _hasAggregateFocus (the got focus event may not have reached the manager yet)
+ foreach (var manager in _managers)
+ {
+ if (manager.HasAggregateFocus)
+ return true;
+ }
+
+ return false;
+ }
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Util/TextDataUtil/MappingHelper.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Util/TextDataUtil/MappingHelper.cs
new file mode 100644
index 0000000000..add43f2550
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/Text/Util/TextDataUtil/MappingHelper.cs
@@ -0,0 +1,275 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+//
+namespace Microsoft.VisualStudio.Text.Utilities
+{
+ using System;
+ using System.Collections.Generic;
+ using Microsoft.VisualStudio.Text.Projection;
+
+ internal static class MappingHelper
+ {
+ //These two methods are nearly duplicates of one another but delegates can be expensive and this is inner loop code.
+ internal static ITextSnapshot FindCorrespondingSnapshot(ITextSnapshot sourceSnapshot, ITextBuffer targetBuffer)
+ {
+ if (sourceSnapshot.TextBuffer == targetBuffer)
+ {
+ // simple case: single buffer
+ return sourceSnapshot;
+ }
+ else
+ {
+ IProjectionSnapshot2 projSnap = sourceSnapshot as IProjectionSnapshot2;
+ if (projSnap != null)
+ {
+ return projSnap.GetMatchingSnapshotInClosure(targetBuffer);
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+
+ internal static ITextSnapshot FindCorrespondingSnapshot(ITextSnapshot sourceSnapshot, Predicate<ITextBuffer> match)
+ {
+ if (match(sourceSnapshot.TextBuffer))
+ {
+ // simple case: single buffer
+ return sourceSnapshot;
+ }
+ else
+ {
+ IProjectionSnapshot2 projSnap = sourceSnapshot as IProjectionSnapshot2;
+ if (projSnap != null)
+ {
+ return projSnap.GetMatchingSnapshotInClosure(match);
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+
+ internal static NormalizedSnapshotSpanCollection MapDownToBufferNoTrack(SnapshotSpan sourceSpan, ITextBuffer targetBuffer, bool mapByContentType = false)
+ {
+ FrugalList<SnapshotSpan> mappedSpans = new FrugalList<SnapshotSpan>();
+
+ MapDownToFirstMatchNoTrack(sourceSpan, (ITextBuffer b) => { return b == targetBuffer; }, mappedSpans, mapByContentType);
+
+ return new NormalizedSnapshotSpanCollection(mappedSpans);
+ }
+
+ internal static void MapDownToBufferNoTrack(SnapshotSpan sourceSpan, ITextBuffer targetBuffer, IList<SnapshotSpan> mappedSpans, bool mapByContentType = false)
+ {
+ // Most of the time, the sourceSpan will map to the targetBuffer as a single span, rather than being split.
+ // Since this method is called a lot, we'll assume first that we'll get a single span and don't need to
+ // allocate a stack to keep track of unmapped spans. If that fails we'll fall back on the more expensive approach.
+ // Scroll around for a while and this saves a bunch of allocations.
+ SnapshotSpan mappedSpan = sourceSpan;
+ while (true)
+ {
+ if (mappedSpan.Snapshot.TextBuffer == targetBuffer)
+ {
+ mappedSpans.Add(mappedSpan);
+ return;
+ }
+ else
+ {
+ IProjectionSnapshot mappedSpanProjectionSnapshot = mappedSpan.Snapshot as IProjectionSnapshot;
+ if (mappedSpanProjectionSnapshot != null &&
+ (!mapByContentType || mappedSpanProjectionSnapshot.ContentType.IsOfType("projection")))
+ {
+ var mappedDownSpans = mappedSpanProjectionSnapshot.MapToSourceSnapshots(mappedSpan);
+ if (mappedDownSpans.Count == 1)
+ {
+ mappedSpan = mappedDownSpans[0];
+ continue;
+ }
+ else if (mappedDownSpans.Count == 0)
+ {
+ return;
+ }
+ else
+ {
+ // the projection mapping resulted in more than one span
+ FrugalList<SnapshotSpan> unmappedSpans = new FrugalList<SnapshotSpan>(mappedDownSpans);
+ SplitMapDownToBufferNoTrack(unmappedSpans, targetBuffer, mappedSpans, mapByContentType);
+ return;
+ }
+ }
+ else
+ {
+ // either it's a projection buffer we can't look through, or it's
+ // an ordinary buffer that didn't match
+ return;
+ }
+ }
+ }
+ }
+
+ private static void SplitMapDownToBufferNoTrack(FrugalList<SnapshotSpan> unmappedSpans, ITextBuffer targetBuffer, IList<SnapshotSpan> mappedSpans, bool mapByContentType)
+ {
+ while (unmappedSpans.Count > 0)
+ {
+ SnapshotSpan span = unmappedSpans[unmappedSpans.Count - 1];
+ unmappedSpans.RemoveAt(unmappedSpans.Count - 1);
+
+ if (span.Snapshot.TextBuffer == targetBuffer)
+ {
+ mappedSpans.Add(span);
+ }
+ else
+ {
+ IProjectionSnapshot spanSnapshotAsProjection = span.Snapshot as IProjectionSnapshot;
+ if (spanSnapshotAsProjection != null &&
+ (!mapByContentType || span.Snapshot.TextBuffer.ContentType.IsOfType("projection")))
+ {
+ unmappedSpans.AddRange(spanSnapshotAsProjection.MapToSourceSnapshots(span));
+ }
+ }
+ }
+ }
+
+ internal static void MapDownToFirstMatchNoTrack(SnapshotSpan sourceSpan, Predicate<ITextBuffer> match, IList<SnapshotSpan> mappedSpans, bool mapByContentType = false)
+ {
+ // Most of the time, the sourceSpan will map to the targetBuffer as a single span, rather than being split.
+ // Since this method is called a lot, we'll assume first that we'll get a single span and don't need to
+ // allocate a stack to keep track of unmapped spans. If that fails we'll fall back on the more expensive approach.
+ // Scroll around for a while and this saves a bunch of allocations.
+ SnapshotSpan mappedSpan = sourceSpan;
+ while (true)
+ {
+ if (match(mappedSpan.Snapshot.TextBuffer))
+ {
+ mappedSpans.Add(mappedSpan);
+ return;
+ }
+ else
+ {
+ IProjectionSnapshot mappedSpanProjectionSnapshot = mappedSpan.Snapshot as IProjectionSnapshot;
+ if (mappedSpanProjectionSnapshot != null &&
+ (!mapByContentType || mappedSpanProjectionSnapshot.ContentType.IsOfType("projection")))
+ {
+ var mappedDownSpans = mappedSpanProjectionSnapshot.MapToSourceSnapshots(mappedSpan);
+ if (mappedDownSpans.Count == 1)
+ {
+ mappedSpan = mappedDownSpans[0];
+ continue;
+ }
+ else if (mappedDownSpans.Count == 0)
+ {
+ return;
+ }
+ else
+ {
+ // the projection mapping resulted in more than one span
+ FrugalList<SnapshotSpan> unmappedSpans = new FrugalList<SnapshotSpan>(mappedDownSpans);
+ SplitMapDownToFirstMatchNoTrack(unmappedSpans, match, mappedSpans, mapByContentType);
+ return;
+ }
+ }
+ else
+ {
+ // either it's a projection buffer we can't look through, or it's
+ // an ordinary buffer that didn't match
+ return;
+ }
+ }
+ }
+ }
+
+ private static void SplitMapDownToFirstMatchNoTrack(FrugalList<SnapshotSpan> unmappedSpans, Predicate<ITextBuffer> match, IList<SnapshotSpan> mappedSpans, bool mapByContentType)
+ {
+ ITextSnapshot matchingSnapshot = null;
+
+ while (unmappedSpans.Count > 0)
+ {
+ SnapshotSpan span = unmappedSpans[unmappedSpans.Count - 1];
+ unmappedSpans.RemoveAt(unmappedSpans.Count - 1);
+
+ if (span.Snapshot == matchingSnapshot)
+ {
+ mappedSpans.Add(span);
+ }
+ else if (match(span.Snapshot.TextBuffer))
+ {
+ mappedSpans.Add(span);
+ matchingSnapshot = span.Snapshot;
+ }
+ else
+ {
+ IProjectionSnapshot spanSnapshotAsProjection = span.Snapshot as IProjectionSnapshot;
+ if (spanSnapshotAsProjection != null &&
+ (!mapByContentType || span.Snapshot.TextBuffer.ContentType.IsOfType("projection")))
+ {
+ unmappedSpans.AddRange(spanSnapshotAsProjection.MapToSourceSnapshots(span));
+ }
+ }
+ }
+ }
+
+ internal static SnapshotPoint? MapDownToBufferNoTrack(SnapshotPoint position, ITextBuffer targetBuffer, PositionAffinity affinity)
+ {
+ while (position.Snapshot.TextBuffer != targetBuffer)
+ {
+ IProjectionSnapshot projSnap = position.Snapshot as IProjectionSnapshot;
+ if ((projSnap == null) || (projSnap.SourceSnapshots.Count == 0))
+ {
+ return null;
+ }
+
+ position = projSnap.MapToSourceSnapshot(position, affinity);
+ }
+ return position;
+ }
+
+ internal static SnapshotPoint? MapDownToFirstMatchNoTrack(SnapshotPoint position, Predicate<ITextBuffer> match, PositionAffinity affinity)
+ {
+ while (!match(position.Snapshot.TextBuffer))
+ {
+ IProjectionSnapshot projSnap = position.Snapshot as IProjectionSnapshot;
+ if ((projSnap == null) || (projSnap.SourceSnapshots.Count == 0))
+ {
+ return null;
+ }
+
+ position = projSnap.MapToSourceSnapshot(position, affinity);
+ }
+ return position;
+ }
+
+ internal static SnapshotPoint? MapDownToBufferNoTrack(SnapshotPoint position, ITextBuffer targetBuffer)
+ {
+ while (position.Snapshot.TextBuffer != targetBuffer)
+ {
+ IProjectionSnapshot projSnap = position.Snapshot as IProjectionSnapshot;
+ if ((projSnap == null) || (projSnap.SourceSnapshots.Count == 0))
+ {
+ return null;
+ }
+
+ position = projSnap.MapToSourceSnapshot(position);
+ }
+
+ return position;
+ }
+
+ internal static SnapshotPoint? MapDownToFirstMatchNoTrack(SnapshotPoint position, Predicate<ITextBuffer> match)
+ {
+ while (!match(position.Snapshot.TextBuffer))
+ {
+ IProjectionSnapshot projSnap = position.Snapshot as IProjectionSnapshot;
+ if ((projSnap == null) || (projSnap.SourceSnapshots.Count == 0))
+ {
+ return null;
+ }
+
+ position = projSnap.MapToSourceSnapshot(position);
+ }
+ return position;
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TextCaret.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TextCaret.cs
deleted file mode 100644
index 14d856875b..0000000000
--- a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TextCaret.cs
+++ /dev/null
@@ -1,256 +0,0 @@
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License. See License.txt in the project root for license information.
-//
-// This file contain implementations details that are subject to change without notice.
-// Use at your own risk.
-//
-using System;
-using Microsoft.VisualStudio.Text;
-using Microsoft.VisualStudio.Text.Editor;
-using Microsoft.VisualStudio.Text.Formatting;
-using Mono.TextEditor;
-using MonoDevelop.Ide.Editor;
-
-namespace Microsoft.VisualStudio.Text.Editor.Implementation
-{
- internal class TextCaret : ITextCaret
- {
- private TextEditor _textEditor;
- private ITextView _textView;
-
- private VirtualSnapshotPoint _insertionPoint;
- private PositionAffinity _caretAffinity;
-
- public TextCaret(TextEditor textEditor, ITextView textView)
- {
- _textEditor = textEditor;
- _textView = textView;
-
- // Set up initial values
- _caretAffinity = PositionAffinity.Successor;
- _insertionPoint = new VirtualSnapshotPoint(new SnapshotPoint(_textView.TextSnapshot, 0));
-
- textEditor.Carets[0].PositionChanged += ImmediateCaretPositionChanged;
- }
-
- void ImmediateCaretPositionChanged(object sender, CaretLocationEventArgs args)
- {
- // MD doesn't fire textEditor.CaretPositionChanged until after the command has gone completely through the command chain.
- // Too much VS stuff depends on it getting updated earlier, so we'll use this event which fires earlier.
- int position = _textEditor.CaretOffset;
- VirtualSnapshotPoint vsp = new VirtualSnapshotPoint(_textView.TextSnapshot, position);
-
- _insertionPoint = vsp;
- if (args.CaretChangeReason == CaretChangeReason.Movement)
- {
- SnapshotPoint snapshotPoint = new SnapshotPoint(_textView.TextSnapshot, position);
- IMappingPoint mappingPoint = _textView.BufferGraph.CreateMappingPoint(snapshotPoint, PointTrackingMode.Positive);
- CaretPosition newCaretPosition = new CaretPosition(vsp, mappingPoint, _caretAffinity);
- CaretPositionChangedEventArgs eventArgs = new CaretPositionChangedEventArgs(_textView, Position, newCaretPosition);
-
- PositionChanged?.Invoke(this, eventArgs);
- }
- }
-
- public double Bottom
- {
- get
- {
- throw new NotImplementedException();
- }
- }
-
- public ITextViewLine ContainingTextViewLine
- {
- get
- {
- throw new NotImplementedException();
- }
- }
-
- public double Height
- {
- get
- {
- throw new NotImplementedException();
- }
- }
-
- public bool InVirtualSpace
- {
- get
- {
- ITextSnapshotLine snapshotLine = _textView.TextBuffer.CurrentSnapshot.GetLineFromPosition(Position.BufferPosition);
-
- return _textEditor.CaretColumn > snapshotLine.Length + 1;
- }
- }
-
- public bool IsHidden
- {
- get
- {
- throw new NotImplementedException();
- }
-
- set
- {
- throw new NotImplementedException();
- }
- }
-
- public double Left
- {
- get
- {
- throw new NotImplementedException();
- }
- }
-
- public bool OverwriteMode
- {
- get
- {
- throw new NotImplementedException();
- }
- }
-
- public CaretPosition Position
- {
- get
- {
-#if TARGET_VS
- //In theory the _insertion point is always at the same snapshot at the _wpfTextView but there could be cases
- //where someone is using the position in a classifier that is using the caret position in the classificaiton changed event.
- //In that case return the old insertion point.
- return new CaretPosition(_insertionPoint,
- _textView.BufferGraph.CreateMappingPoint(_insertionPoint.Position, PointTrackingMode.Positive),
- _caretAffinity);
-#else
- // MD doesn't update the caret location until after the command has gone completely through the command chain. Too much VS stuff depends on it getting updated earlier.
- // Thus, I'm going to ensure this is returning a position based on the current snapshot, ignoring/breaking the scenario outlined in the above comment.
- VirtualSnapshotPoint insertionPointInLatestSnapshot = _insertionPoint.TranslateTo(_insertionPoint.Position.Snapshot.TextBuffer.CurrentSnapshot, PointTrackingMode.Positive);
- return new CaretPosition(insertionPointInLatestSnapshot,
- _textView.BufferGraph.CreateMappingPoint(insertionPointInLatestSnapshot.Position, PointTrackingMode.Positive),
- _caretAffinity);
-#endif
- }
- }
-
- public double Right
- {
- get
- {
- throw new NotImplementedException();
- }
- }
-
- public double Top
- {
- get
- {
- throw new NotImplementedException();
- }
- }
-
- public double Width
- {
- get
- {
- throw new NotImplementedException();
- }
- }
-
- public event EventHandler<CaretPositionChangedEventArgs> PositionChanged;
-
- public void EnsureVisible()
- {
- _textEditor.ScrollTo(_textEditor.CaretLocation);
- }
-
- public CaretPosition MoveTo(SnapshotPoint bufferPosition)
- {
- this.InternalMoveTo(new VirtualSnapshotPoint(bufferPosition), PositionAffinity.Successor, true, true, true);
-
- return this.Position;
- }
-
- public CaretPosition MoveTo(VirtualSnapshotPoint bufferPosition)
- {
- this.InternalMoveTo(bufferPosition, PositionAffinity.Successor, true, true, true);
-
- return this.Position;
- }
-
- public CaretPosition MoveTo(ITextViewLine textLine)
- {
- throw new NotImplementedException();
- }
-
- public CaretPosition MoveTo(SnapshotPoint bufferPosition, PositionAffinity caretAffinity)
- {
- throw new NotImplementedException();
- }
-
- public CaretPosition MoveTo(VirtualSnapshotPoint bufferPosition, PositionAffinity caretAffinity)
- {
- throw new NotImplementedException();
- }
-
- public CaretPosition MoveTo(ITextViewLine textLine, double xCoordinate)
- {
- throw new NotImplementedException();
- }
-
- public CaretPosition MoveTo(VirtualSnapshotPoint bufferPosition, PositionAffinity caretAffinity, bool captureHorizontalPosition)
- {
- throw new NotImplementedException();
- }
-
- public CaretPosition MoveTo(SnapshotPoint bufferPosition, PositionAffinity caretAffinity, bool captureHorizontalPosition)
- {
- throw new NotImplementedException();
- }
-
- public CaretPosition MoveTo(ITextViewLine textLine, double xCoordinate, bool captureHorizontalPosition)
- {
- throw new NotImplementedException();
- }
-
- public CaretPosition MoveToNextCaretPosition()
- {
- throw new NotImplementedException();
- }
-
- public CaretPosition MoveToPreferredCoordinates()
- {
- throw new NotImplementedException();
- }
-
- public CaretPosition MoveToPreviousCaretPosition()
- {
- throw new NotImplementedException();
- }
-
- private void InternalMoveTo(VirtualSnapshotPoint bufferPosition, PositionAffinity caretAffinity, bool captureHorizontalPosition, bool captureVerticalPosition, bool raiseEvent)
- {
- int requestedPosition = bufferPosition.Position;
- ITextSnapshotLine snapshotLine = _textView.TextSnapshot.GetLineFromPosition(requestedPosition);
- int line = snapshotLine.LineNumber + 1;
-
- int col;
- if (bufferPosition.IsInVirtualSpace)
- {
- col = bufferPosition.VirtualSpaces;
- }
- else
- {
- col = requestedPosition - snapshotLine.Start + 1;
- }
-
- _textEditor.SetCaretLocation(line, col, false, false);
- }
- }
-}
-
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TextEditorFactoryService.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TextEditorFactoryService.cs
index eaa19073d2..f72a7c18db 100644
--- a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TextEditorFactoryService.cs
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TextEditorFactoryService.cs
@@ -31,7 +31,7 @@ namespace Microsoft.VisualStudio.Text.Editor.Implementation
/// Provides a VisualStudio Service that aids in creation of Editor Views
/// </summary>
[Export(typeof(ITextEditorFactoryService))]
- internal sealed class TextEditorFactoryService : ITextEditorFactoryService
+ internal sealed class TextEditorFactoryService : ITextEditorFactoryService, IPartImportsSatisfiedNotification
{
[Import]
internal GuardedOperations GuardedOperations { get; set; }
@@ -100,6 +100,14 @@ namespace Microsoft.VisualStudio.Text.Editor.Implementation
PredefinedTextViewRoles.Structured,
PredefinedTextViewRoles.Zoomable);
+ public ITextView CreateTextView (ITextBuffer textBuffer)
+ {
+ MonoDevelop.Ide.Editor.ITextDocument textDocument = textBuffer.GetTextEditor();
+ TextEditor textEditor = textDocument as TextEditor;
+
+ return CreateTextView(textEditor);
+ }
+
public ITextView CreateTextView (MonoDevelop.Ide.Editor.TextEditor textEditor, ITextViewRoleSet roles = null, IEditorOptions parentOptions = null)
{
if (textEditor == null)
@@ -123,7 +131,8 @@ namespace Microsoft.VisualStudio.Text.Editor.Implementation
this.GuardedOperations,
this) ?? new VacuousTextViewModel(dataModel);
- TextView view = new TextView(textEditor, viewModel, roles ?? this.DefaultRoles, parentOptions ?? this.EditorOptionsFactoryService.GlobalOptions, this);
+ var view = ((MonoDevelop.SourceEditor.SourceEditorView)textEditor.Implementation).TextEditor;
+ view.Initialize(viewModel, roles, parentOptions ?? this.EditorOptionsFactoryService.GlobalOptions, this);
view.Properties.AddProperty(typeof(MonoDevelop.Ide.Editor.TextEditor), textEditor);
this.TextViewCreated?.Invoke(this, new TextViewCreatedEventArgs(view));
@@ -164,5 +173,18 @@ namespace Microsoft.VisualStudio.Text.Editor.Implementation
{
return new TextViewRoleSet(roles);
}
+
+ [ImportMany]
+ private List<Lazy<SpaceReservationManagerDefinition, IOrderable>> _spaceReservationManagerDefinitions = null;
+ internal Dictionary<string, int> OrderedSpaceReservationManagerDefinitions = new Dictionary<string, int>();
+
+ public void OnImportsSatisfied()
+ {
+ IList<Lazy<SpaceReservationManagerDefinition, IOrderable>> orderedManagers = Orderer.Order(_spaceReservationManagerDefinitions);
+ for (int i = 0; (i < orderedManagers.Count); ++i)
+ {
+ this.OrderedSpaceReservationManagerDefinitions.Add(orderedManagers[i].Metadata.Name, i);
+ }
+ }
}
}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TextSelection.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TextSelection.cs
index 1814107000..47dc46170c 100644
--- a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TextSelection.cs
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TextSelection.cs
@@ -17,13 +17,13 @@ namespace Microsoft.VisualStudio.Text.Editor.Implementation
{
internal class TextSelection : ITextSelection
{
- private TextEditor _textEditor;
+ private Mono.TextEditor.MonoTextEditor _textEditor;
private ITextView _textView;
- public TextSelection(TextEditor textEditor, ITextView textView)
+ public TextSelection(Mono.TextEditor.MonoTextEditor textArea)
{
- _textEditor = textEditor;
- _textView = textView;
+ _textEditor = textArea;
+ _textView = textArea;
_textEditor.SelectionChanged += OnSelectionChanged;
}
@@ -51,7 +51,7 @@ namespace Microsoft.VisualStudio.Text.Editor.Implementation
{
get
{
- int offset = _textEditor.SelectionLeadOffset;
+ int offset = _textEditor.SelectionLead;
if (offset == -1)
offset = _textEditor.SelectionRange.Offset; // Selection is empty
@@ -66,7 +66,7 @@ namespace Microsoft.VisualStudio.Text.Editor.Implementation
{
get
{
- int offset = _textEditor.SelectionAnchorOffset;
+ int offset = _textEditor.SelectionAnchor;
if (offset == -1)
offset = _textEditor.SelectionRange.Offset; // Selection is empty
@@ -147,7 +147,7 @@ namespace Microsoft.VisualStudio.Text.Editor.Implementation
else
{
IList<SnapshotSpan> spans = new List<SnapshotSpan>();
- foreach (MonoDevelop.Ide.Editor.Selection curSelection in _textEditor.Selections)
+ foreach (MonoDevelop.Ide.Editor.Selection curSelection in new MonoDevelop.Ide.Editor.Selection[] { _textEditor.MainSelection })
{
for (int curLineIndex = curSelection.MinLine; curLineIndex <= curSelection.MaxLine; curLineIndex++)
{
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TextView.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TextView.cs
deleted file mode 100644
index ae3e4740d2..0000000000
--- a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TextView.cs
+++ /dev/null
@@ -1,562 +0,0 @@
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License. See License.txt in the project root for license information.
-//
-// This file contain implementations details that are subject to change without notice.
-// Use at your own risk.
-//
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using Microsoft.VisualStudio.Text;
-using Microsoft.VisualStudio.Text.Classification;
-using Microsoft.VisualStudio.Text.Editor;
-using Microsoft.VisualStudio.Text.Editor.Implementation;
-using Microsoft.VisualStudio.Text.Formatting;
-using Microsoft.VisualStudio.Text.Projection;
-using Microsoft.VisualStudio.Text.Utilities;
-using Microsoft.VisualStudio.Utilities;
-using MonoDevelop.Ide;
-using MonoDevelop.Ide.Editor;
-using Microsoft.VisualStudio.Platform;
-
-namespace Microsoft.VisualStudio.Text.Editor.Implementation
-{
- internal class TextView : ITextView
- {
- #region Private Members
- private TextEditor _textEditor;
-
- ITextBuffer _textBuffer;
- // ITextSnapshot _textSnapshot;
-
- // ITextBuffer _visualBuffer;
- // ITextSnapshot _visualSnapshot;
-
- IBufferGraph _bufferGraph;
- ITextViewRoleSet _roles;
-
- ConnectionManager _connectionManager;
-
- TextEditorFactoryService _factoryService;
-
- // IEditorFormatMap _editorFormatMap;
-
- private bool _hasInitializeBeenCalled = false;
-
- private ITextSelection _selection;
-
- private bool _hasAggregateFocus;
-
- private IEditorOptions _editorOptions;
-
- private List<Lazy<ITextViewCreationListener, IDeferrableContentTypeAndTextViewRoleMetadata>> _deferredTextViewListeners;
-
- private ITextCaret _caret;
-
- bool _isClosed = false;
-
- private PropertyCollection _properties = new PropertyCollection();
-
- //Only one view at a time will have aggregate focus, so keep track of it so that (when sending aggregate focus changed events)
- //we give a view that had focus the chance to send its lost focus message before we claim aggregate focus.
- [ThreadStatic]
- static TextView ViewWithAggregateFocus = null;
-#if DEBUG
- [ThreadStatic]
- static bool SettingAggregateFocus = false;
-#endif
-
-#endregion // Private Members
-
- /// <summary>
- /// Text View constructor.
- /// </summary>
- /// <param name="textViewModel">The text view model that provides the text to visualize.</param>
- /// <param name="roles">Roles for this view.</param>
- /// <param name="parentOptions">Parent options for this view.</param>
- /// <param name="factoryService">Our handy text editor factory service.</param>
- internal TextView(TextEditor textEditor, ITextViewModel textViewModel, ITextViewRoleSet roles, IEditorOptions parentOptions, TextEditorFactoryService factoryService, bool initialize = true)
- {
- _textEditor = textEditor;
-
- _roles = roles;
-
- _factoryService = factoryService;
-
- this.TextDataModel = textViewModel.DataModel;
- this.TextViewModel = textViewModel;
-
- _textBuffer = textViewModel.EditBuffer;
- // _visualBuffer = textViewModel.VisualBuffer;
-
- // _textSnapshot = _textBuffer.CurrentSnapshot;
- // _visualSnapshot = _visualBuffer.CurrentSnapshot;
-
- _editorOptions = _factoryService.EditorOptionsFactoryService.GetOptions(this);
- _editorOptions.Parent = parentOptions;
-
- if (initialize)
- this.Initialize();
- }
-
- internal bool IsTextViewInitialized { get { return _hasInitializeBeenCalled; } }
-
- // This method should only be called once (it is normally called from the ctor unless we're using
- // ITextEditorFactoryService2.CreateTextViewWithoutInitialization on the factory to delay initialization).
- internal void Initialize()
- {
- if (_hasInitializeBeenCalled)
- throw new InvalidOperationException("Attempted to Initialize a WpfTextView twice");
-
- _bufferGraph = _factoryService.BufferGraphFactoryService.CreateBufferGraph(this.TextViewModel.VisualBuffer);
-
- //_editorFormatMap = _factoryService.EditorFormatMapService.GetEditorFormatMap(this);
-
- _selection = new TextSelection(_textEditor, this);
-
- // Create caret
- _caret = new TextCaret(_textEditor, this);
-
- // this.Loaded += OnLoaded;
-
- // TODO: *Someone* needs to call this to execute UndoHistoryRegistry.RegisterHistory -- VS does this via the ShimCompletionControllerFactory.
- _factoryService.EditorOperationsProvider.GetEditorOperations (this);
-
- _connectionManager = new ConnectionManager(this, _factoryService.TextViewConnectionListeners, _factoryService.GuardedOperations);
-
- SubscribeToEvents();
-
- // Binding content type specific assets includes calling out to content-type
- // specific view creation listeners. We need to do this as late as possible.
- this.BindContentTypeSpecificAssets(null, TextViewModel.DataModel.ContentType);
-
- //Subscribe now so that there is no chance that a layout could be forced by a text change.
- //_visualBuffer.ChangedLowPriority += OnVisualBufferChanged;
- //_visualBuffer.ContentTypeChanged += OnVisualBufferContentTypeChanged;
-
- _hasInitializeBeenCalled = true;
- }
-
- public ITextCaret Caret
- {
- get
- {
- return _caret;
- }
- }
-
- public bool HasAggregateFocus
- {
- get
- {
- return _hasAggregateFocus;
- }
- }
-
- public bool InLayout
- {
- get
- {
- return false;
- }
- }
-
- public bool IsClosed { get { return _isClosed; } }
-
- public bool IsMouseOverViewOrAdornments
- {
- get
- {
- throw new NotImplementedException();
- }
- }
-
- public double LineHeight
- {
- get
- {
- throw new NotImplementedException();
- }
- }
-
- public double MaxTextRightCoordinate
- {
- get
- {
- throw new NotImplementedException();
- }
- }
-
- public IEditorOptions Options
- {
- get { return _editorOptions; }
- }
-
- public PropertyCollection Properties
- {
- get
- {
- return _properties;
- }
- }
-
- public ITrackingSpan ProvisionalTextHighlight
- {
- get
- {
- throw new NotImplementedException();
- }
-
- set
- {
- throw new NotImplementedException();
- }
- }
-
- public ITextSelection Selection
- {
- get
- {
- return _selection;
- }
- }
-
- public ITextViewRoleSet Roles
- {
- get
- {
- return _roles;
- }
- }
-
- /// <summary>
- /// Gets the text buffer whose text, this text editor renders
- /// </summary>
- public ITextBuffer TextBuffer
- {
- get
- {
- return _textBuffer;
- }
- }
-
- public IBufferGraph BufferGraph
- {
- get
- {
- return _bufferGraph;
- }
- }
-
- public ITextSnapshot TextSnapshot
- {
- get
- {
- // TODO: MONO: WpfTextView has a much more complex calculation of this
- return TextBuffer.CurrentSnapshot;
- // return _textSnapshot;
- }
- }
-
- public ITextSnapshot VisualSnapshot
- {
- get
- {
- return TextBuffer.CurrentSnapshot;
- // return _visualSnapshot;
- }
- }
-
- public ITextDataModel TextDataModel { get; private set; }
- public ITextViewModel TextViewModel { get; private set; }
-
-#if TARGET_VS
- public ITextViewLineCollection TextViewLines
- {
- get
- {
- return _textViewLinesCollection;
- }
- }
-#endif
-
- public double ViewportBottom
- {
- get
- {
- throw new NotImplementedException();
- }
- }
-
- public double ViewportHeight
- {
- get
- {
- throw new NotImplementedException();
- }
- }
-
- public double ViewportLeft
- {
- get
- {
- throw new NotImplementedException();
- }
-
- set
- {
- throw new NotImplementedException();
- }
- }
-
- public double ViewportRight
- {
- get
- {
- throw new NotImplementedException();
- }
- }
-
- public double ViewportTop
- {
- get
- {
- throw new NotImplementedException();
- }
- }
-
- public double ViewportWidth
- {
- get
- {
- throw new NotImplementedException();
- }
- }
-
- public IViewScroller ViewScroller
- {
- get
- {
- throw new NotImplementedException();
- }
- }
-
- public ITextViewLineCollection TextViewLines
- {
- get
- {
- throw new NotImplementedException();
- }
- }
-
- public event EventHandler Closed;
- public event EventHandler GotAggregateFocus;
- public event EventHandler LostAggregateFocus;
-#pragma warning disable CS0067
- public event EventHandler<TextViewLayoutChangedEventArgs> LayoutChanged;
- public event EventHandler ViewportLeftChanged;
- public event EventHandler ViewportHeightChanged;
- public event EventHandler ViewportWidthChanged;
- public event EventHandler<MouseHoverEventArgs> MouseHover;
-#pragma warning restore CS0067
-
- public void Close()
- {
- if (_isClosed)
- throw new InvalidOperationException();//Strings.TextViewClosed);
-
- if (_hasAggregateFocus)
- {
- //Silently lose aggregate focus (to preserve Dev11 compatibility which did not raise a focus changed event when the view was closed).
- Debug.Assert(TextView.ViewWithAggregateFocus == this);
- TextView.ViewWithAggregateFocus = null;
- _hasAggregateFocus = false;
- }
-
- UnsubscribeFromEvents();
-
- _connectionManager.Close();
-
- TextViewModel.Dispose();
- TextViewModel = null;
-
- _isClosed = true;
-
- _factoryService.GuardedOperations.RaiseEvent(this, this.Closed);
- }
-
- public void DisplayTextLineContainingBufferPosition(SnapshotPoint bufferPosition, double verticalDistance, ViewRelativePosition relativeTo)
- {
- throw new NotImplementedException();
- }
-
- public void DisplayTextLineContainingBufferPosition(SnapshotPoint bufferPosition, double verticalDistance, ViewRelativePosition relativeTo, double? viewportWidthOverride, double? viewportHeightOverride)
- {
- throw new NotImplementedException();
- }
-
- public SnapshotSpan GetTextElementSpan(SnapshotPoint point)
- {
- throw new NotImplementedException();
- }
-
- public ITextViewLine GetTextViewLineContainingBufferPosition(SnapshotPoint bufferPosition)
- {
- throw new NotImplementedException();
- }
-
- public void QueueSpaceReservationStackRefresh()
- {
- throw new NotImplementedException();
- }
-
- /// <remarks>
- /// If you add an event subscription to this method, be sure to add the corresponding unsubscription to
- /// UnsubscribeFromEvents()
- /// </remarks>
- private void SubscribeToEvents()
- {
- if (IdeApp.IsInitialized)
- IdeApp.Workbench.ActiveDocumentChanged += Workbench_ActiveDocumentChanged;
- }
-
- void Workbench_ActiveDocumentChanged(object sender, EventArgs e)
- {
- QueueAggregateFocusCheck();
- }
-
- private void UnsubscribeFromEvents()
- {
- if (IdeApp.IsInitialized)
- IdeApp.Workbench.ActiveDocumentChanged -= Workbench_ActiveDocumentChanged;
- }
-
- private void BindContentTypeSpecificAssets(IContentType beforeContentType, IContentType afterContentType)
- {
- // Notify the Text view creation listeners
- var extensions = UIExtensionSelector.SelectMatchingExtensions(_factoryService.TextViewCreationListeners, afterContentType, beforeContentType, _roles);
- foreach (var extension in extensions)
- {
- string deferOptionName = extension.Metadata.OptionName;
- if (!string.IsNullOrEmpty(deferOptionName) && Options.IsOptionDefined(deferOptionName, false))
- {
- object value = Options.GetOptionValue(deferOptionName);
- if (value is bool)
- {
- if (!(bool)value)
- {
- if (_deferredTextViewListeners == null)
- {
- _deferredTextViewListeners = new List<Lazy<ITextViewCreationListener, IDeferrableContentTypeAndTextViewRoleMetadata>>();
- }
- _deferredTextViewListeners.Add(extension);
- continue;
- }
- }
- }
-
- var instantiatedExtension = _factoryService.GuardedOperations.InstantiateExtension(extension, extension);
- if (instantiatedExtension != null)
- {
- _factoryService.GuardedOperations.CallExtensionPoint(instantiatedExtension,
- () => instantiatedExtension.TextViewCreated(this));
- }
- }
- }
-
- /// <summary>
- /// Handles the Classification changed event that comes from the Classifier aggregator
- /// </summary>
- void OnClassificationChanged(object sender, ClassificationChangedEventArgs e)
- {
- if (!_isClosed)
- {
- // When classifications change, we just invalidate the lines. That invalidation will
- // create new lines based on the new classifications.
-
- // Map the classification change (from the edit buffer) to the visual buffer
- Span span = Span.FromBounds(
- TextViewModel.GetNearestPointInVisualSnapshot(e.ChangeSpan.Start, VisualSnapshot, PointTrackingMode.Negative),
- TextViewModel.GetNearestPointInVisualSnapshot(e.ChangeSpan.End, VisualSnapshot, PointTrackingMode.Positive));
-
- //Classifications changes invalidate only the characters contained in the span so a zero length change
- //will have no effect.
- if (span.Length > 0)
- {
- //IsLineInvalid will invalidate a line if it intersects the end. The result is that any call to InvalidateSpan() implicitly
- //invalidates any line that starts at the end of the invalidated span, which we do not want here. Reduce the length of the classification
- //change span one so -- if someone invalidated an entire line including the line break -- the next line will not be invalidated.
- span = new Span(span.Start, span.Length - 1);
-
-// MONO: TODO: this
-
- //lock (_invalidatedSpans)
- //{
- // if ((_attachedLineCache.Count > 0) || (_unattachedLineCache.Count > 0))
- // {
- // _reclassifiedSpans.Add(span);
- // this.QueueLayout();
- // }
- //}
- }
- }
- }
-
- internal void QueueAggregateFocusCheck(bool checkForFocus = true)
- {
-#if DEBUG
- if (TextView.SettingAggregateFocus)
- {
- Debug.Fail("WpfTextView.SettingAggregateFocus");
- }
-#endif
-
- if (!_isClosed)
- {
- bool newHasAggregateFocus = (IdeApp.Workbench.ActiveDocument?.Editor == _textEditor);
- if (newHasAggregateFocus != _hasAggregateFocus)
- {
- _hasAggregateFocus = newHasAggregateFocus;
-
- if (_hasAggregateFocus)
- {
- //Got focus so make sure that the view that had focus (which wasn't us since we didn't have focus before) raises its
- //lost focus event before we raise our got focus event. This will potentially do bad things if someone changes focus
- //if the lost aggregate focus handler.
- Debug.Assert(TextView.ViewWithAggregateFocus != this);
- if (TextView.ViewWithAggregateFocus != null)
- {
- TextView.ViewWithAggregateFocus.QueueAggregateFocusCheck(checkForFocus: false);
- }
- Debug.Assert(TextView.ViewWithAggregateFocus == null);
- TextView.ViewWithAggregateFocus = this;
- }
- else
- {
- //Lost focus (which means we were the view with focus).
- Debug.Assert(TextView.ViewWithAggregateFocus == this);
- TextView.ViewWithAggregateFocus = null;
- }
-
- EventHandler handler = _hasAggregateFocus ? this.GotAggregateFocus : this.LostAggregateFocus;
-
-#if DEBUG
- try
- {
- TextView.SettingAggregateFocus = true;
-#endif
- _factoryService.GuardedOperations.RaiseEvent(this, handler);
-#if DEBUG
- }
- finally
- {
- TextView.SettingAggregateFocus = false;
- }
-#endif
- }
- }
- }
- }
-}
-
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/VisualStudio/Impl/ViewAdapter/TipManager.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/VisualStudio/Impl/ViewAdapter/TipManager.cs
new file mode 100644
index 0000000000..6d733fd1c5
--- /dev/null
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/VisualStudio/Impl/ViewAdapter/TipManager.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+
+using System.ComponentModel.Composition;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Utilities;
+
+namespace Microsoft.VisualStudio.Editor.Implementation
+{
+ [Export (typeof (IObscuringTipManager))]
+ public class TipManager : IObscuringTipManager
+ {
+ public void PushTip (ITextView view, IObscuringTip tip)
+ {
+ }
+
+ public void RemoveTip (ITextView view, IObscuringTip tip)
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.csproj b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.csproj
index 0e03990c59..c81c5529ac 100644
--- a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.csproj
+++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.csproj
@@ -118,6 +118,10 @@
<HintPath>..\..\..\..\build\bin\Microsoft.CodeAnalysis.dll</HintPath>
<Private>False</Private>
</Reference>
+ <Reference Include="Microsoft.VisualStudio.Text.UI">
+ <HintPath>..\..\..\..\build\bin\Microsoft.VisualStudio.Text.UI.dll</HintPath>
+ <Private>False</Private>
+ </Reference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="icons\added-overlay-16.png">
diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Shared.projitems b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Shared.projitems
index 957dd9b2eb..003ef36856 100644
--- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Shared.projitems
+++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Shared.projitems
@@ -79,6 +79,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Mono.TextEditor\UrlMarker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Mono.TextEditor\TextEditorData.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Mono.TextEditor\DefaultIndentationTracker.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Mono.TextEditor\CaretImpl.ITextCaret.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="$(MSBuildThisFileDirectory)Mono.TextEditor\" />
diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/ClipboardActions.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/ClipboardActions.cs
index 5aeeb1eb38..c0894e600b 100644
--- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/ClipboardActions.cs
+++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/ClipboardActions.cs
@@ -435,12 +435,17 @@ namespace Mono.TextEditor
data.Document.CommitLineUpdate (data.GetLineByOffset (insertionOffset));
return result;
}
-
+
public static void Paste (TextEditorData data)
{
+ PasteWithResult (data);
+ }
+
+ public static bool PasteWithResult (TextEditorData data)
+ {
if (!data.CanEditSelection)
- return;
- PasteFrom (Clipboard.Get (CopyOperation.CLIPBOARD_ATOM), data, false, data.IsSomethingSelected ? data.SelectionRange.Offset : data.Caret.Offset);
+ return false;
+ return PasteFrom (Clipboard.Get (CopyOperation.CLIPBOARD_ATOM), data, false, data.IsSomethingSelected ? data.SelectionRange.Offset : data.Caret.Offset) > 0;
}
public static string GetClipboardContent()
diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/MiscActions.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/MiscActions.cs
index 5bf609cd3a..ce678e062b 100644
--- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/MiscActions.cs
+++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/MiscActions.cs
@@ -242,9 +242,16 @@ namespace Mono.TextEditor
{
using (var undo = data.OpenUndoGroup ()) {
data.EnsureCaretIsNotVirtual ();
+
+ var oldCaretLine = data.Caret.Location.Line;
+
string indentString = data.GetIndentationString (data.Caret.Location);
data.InsertAtCaret (data.EolMarker);
- data.InsertAtCaret (indentString);
+
+ // Don't insert the indent string if the EOL insertion modified the caret location in an unexpected fashion
+ // (This likely means someone has custom logic regarding insertion of the EOL)
+ if (data.Caret.Location.Line == oldCaretLine + 1 && data.Caret.Location.Column == 1)
+ data.InsertAtCaret (indentString);
}
}
diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/CaretImpl.ITextCaret.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/CaretImpl.ITextCaret.cs
new file mode 100644
index 0000000000..386a85f6c1
--- /dev/null
+++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/CaretImpl.ITextCaret.cs
@@ -0,0 +1,236 @@
+//
+// CaretImpl.ITextCaret.cs
+//
+// Author:
+// Mike Krüger <mikkrg@microsoft.com>
+//
+// Copyright (c) 2018 Microsoft Corporation. All rights reserved.
+//
+// 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.Core.Text;
+using MonoDevelop.Ide.Editor;
+using System;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Formatting;
+using Mono.TextEditor;
+using MonoDevelop.Ide.Editor;
+
+namespace Mono.TextEditor
+{
+ partial class CaretImpl : ITextCaret
+ {
+ MonoTextEditor TextEditor => TextEditorData.Parent;
+
+ VirtualSnapshotPoint insertionPoint;
+ PositionAffinity _caretAffinity;
+
+ ITextViewLine ITextCaret.ContainingTextViewLine => TextEditor.GetTextViewLineContainingBufferPosition (((ITextCaret)this).Position.VirtualBufferPosition.Position);
+
+ double ITextCaret.Left => TextEditor.TextArea.TextViewMargin.caretX;
+
+ double ITextCaret.Width => TextEditor.TextArea.TextViewMargin.charWidth;
+
+ double ITextCaret.Right => TextEditor.TextArea.TextViewMargin.caretX + TextEditor.TextArea.TextViewMargin.charWidth;
+
+ double ITextCaret.Top => TextEditor.TextArea.TextViewMargin.caretY;
+
+ double ITextCaret.Height => TextEditor.LineHeight;
+
+ double ITextCaret.Bottom => TextEditor.TextArea.TextViewMargin.caretY + TextEditor.LineHeight;
+
+ CaretPosition ITextCaret.Position {
+ get {
+#if TARGET_VS
+ //In theory the _insertion point is always at the same snapshot at the _wpfTextView but there could be cases
+ //where someone is using the position in a classifier that is using the caret position in the classificaiton changed event.
+ //In that case return the old insertion point.
+ return new CaretPosition(_insertionPoint,
+ _textView.BufferGraph.CreateMappingPoint(_insertionPoint.Position, PointTrackingMode.Positive),
+ _caretAffinity);
+#else
+ // MD doesn't update the caret location until after the command has gone completely through the command chain. Too much VS stuff depends on it getting updated earlier.
+ // Thus, I'm going to ensure this is returning a position based on the current snapshot, ignoring/breaking the scenario outlined in the above comment.
+ VirtualSnapshotPoint insertionPointInLatestSnapshot = insertionPoint.TranslateTo (insertionPoint.Position.Snapshot.TextBuffer.CurrentSnapshot, PointTrackingMode.Positive);
+ return new CaretPosition (insertionPointInLatestSnapshot,
+ TextEditor.BufferGraph.CreateMappingPoint (insertionPointInLatestSnapshot.Position, PointTrackingMode.Positive),
+ _caretAffinity);
+#endif
+ }
+ }
+
+
+ bool ITextCaret.OverwriteMode => !IsInInsertMode;
+
+ bool ITextCaret.InVirtualSpace {
+ get {
+ var snapshotLine = TextEditor.TextBuffer.CurrentSnapshot.GetLineFromPosition (((ITextCaret)this).Position.BufferPosition);
+
+ return TextEditor.Caret.Column > snapshotLine.Length + 1;
+ }
+ }
+
+ bool ITextCaret.IsHidden {
+ get {
+ return !IsVisible;
+ }
+ set {
+ IsVisible = !value;
+ }
+ }
+
+ DocumentLocation oldCaretLocation;
+ void PositionChanged_ITextCaret (CaretLocationEventArgs args)
+ {
+ //Some unit tests don't initialize full UI representation of MonoTextEditor
+ //which means they don't depend on ITextCaret implementation, so we can return here
+ //If something is using MonoTextEditor directly(e.g. DiffView) and is not initializing ITextView
+ //TextBuffer is null, in that case don't depend on ITextCaret implementation, so we can return here
+ if (TextEditor?.TextBuffer == null)
+ return;
+ // MD doesn't fire textEditor.CaretPositionChanged until after the command has gone completely through the command chain.
+ // Too much VS stuff depends on it getting updated earlier, so we'll use this event which fires earlier.
+ int position = TextEditor.Caret.Offset;
+ VirtualSnapshotPoint vsp = new VirtualSnapshotPoint (TextEditor.TextSnapshot, position);
+
+ insertionPoint = vsp;
+ if (args.CaretChangeReason == CaretChangeReason.Movement) {
+ oldCaretLocation = args.Location;
+ var oldOffset = TextEditor.LocationToOffset (args.Location);
+ var snapshotPoint = new SnapshotPoint (TextEditor.TextSnapshot, oldOffset);
+ var mappingPoint = TextEditor.BufferGraph.CreateMappingPoint (snapshotPoint, PointTrackingMode.Positive);
+ var oldCaretPosition = new CaretPosition (vsp, mappingPoint, _caretAffinity);
+ var eventArgs = new CaretPositionChangedEventArgs (TextEditor, oldCaretPosition, ((ITextCaret)this).Position);
+
+ ITextCaret_PositionChanged?.Invoke (this, eventArgs);
+ }
+ }
+
+ event EventHandler<CaretPositionChangedEventArgs> ITextCaret_PositionChanged;
+ event EventHandler<CaretPositionChangedEventArgs> ITextCaret.PositionChanged {
+ add { ITextCaret_PositionChanged += value; }
+ remove { ITextCaret_PositionChanged -= value; }
+ }
+
+ void ITextCaret.EnsureVisible ()
+ {
+ TextEditor.ScrollToCaret ();
+ }
+
+
+ CaretPosition ITextCaret.MoveTo (ITextViewLine textLine, double xCoordinate)
+ {
+ Line = textLine.Start;
+ // TODO: xCoordinate - is that just visual or does it have an impact on the column ?
+ return ((ITextCaret)this).Position;
+ }
+
+ CaretPosition ITextCaret.MoveTo (ITextViewLine textLine, double xCoordinate, bool captureHorizontalPosition)
+ {
+ Line = textLine.Start;
+ // TODO: xCoordinate - is that just visual or does it have an impact on the column ?
+ return ((ITextCaret)this).Position;
+ }
+
+ CaretPosition ITextCaret.MoveTo (ITextViewLine textLine)
+ {
+ Line = textLine.Start;
+ return ((ITextCaret)this).Position;
+ }
+
+ CaretPosition ITextCaret.MoveTo (SnapshotPoint bufferPosition)
+ {
+ this.InternalMoveTo (new VirtualSnapshotPoint (bufferPosition), PositionAffinity.Successor, true, true, true);
+
+ return ((ITextCaret)this).Position;
+ }
+
+ CaretPosition ITextCaret.MoveTo (SnapshotPoint bufferPosition, PositionAffinity caretAffinity)
+ {
+ this.InternalMoveTo (new VirtualSnapshotPoint (bufferPosition), caretAffinity, true, true, true);
+
+ return ((ITextCaret)this).Position;
+ }
+
+ CaretPosition ITextCaret.MoveTo (SnapshotPoint bufferPosition, PositionAffinity caretAffinity, bool captureHorizontalPosition)
+ {
+ this.InternalMoveTo (new VirtualSnapshotPoint (bufferPosition), caretAffinity, captureHorizontalPosition, true, true);
+
+ return ((ITextCaret)this).Position;
+ }
+
+ CaretPosition ITextCaret.MoveTo (VirtualSnapshotPoint bufferPosition)
+ {
+ this.InternalMoveTo (bufferPosition, PositionAffinity.Successor, true, true, true);
+
+ return ((ITextCaret)this).Position;
+ }
+
+ CaretPosition ITextCaret.MoveTo (VirtualSnapshotPoint bufferPosition, PositionAffinity caretAffinity)
+ {
+ this.InternalMoveTo (bufferPosition, caretAffinity, true, true, true);
+
+ return ((ITextCaret)this).Position;
+ }
+
+ CaretPosition ITextCaret.MoveTo (VirtualSnapshotPoint bufferPosition, PositionAffinity caretAffinity, bool captureHorizontalPosition)
+ {
+ this.InternalMoveTo (bufferPosition, caretAffinity, captureHorizontalPosition, true, true);
+
+ return ((ITextCaret)this).Position;
+ }
+
+ CaretPosition ITextCaret.MoveToNextCaretPosition ()
+ {
+ // TODO: Implement me - not sure if we've a 'next' position. What should this be ?
+ return ((ITextCaret)this).Position;
+ }
+
+ CaretPosition ITextCaret.MoveToPreferredCoordinates ()
+ {
+ TextEditor.GetTextEditorData ().FixVirtualIndentation ();
+ return ((ITextCaret)this).Position;
+ }
+
+ CaretPosition ITextCaret.MoveToPreviousCaretPosition ()
+ {
+ Location = oldCaretLocation;
+ return ((ITextCaret)this).Position;
+ }
+
+ void InternalMoveTo (VirtualSnapshotPoint bufferPosition, PositionAffinity caretAffinity, bool captureHorizontalPosition, bool captureVerticalPosition, bool raiseEvent)
+ {
+ int requestedPosition = bufferPosition.Position;
+ ITextSnapshotLine snapshotLine = TextEditor.TextSnapshot.GetLineFromPosition (requestedPosition);
+ int line = snapshotLine.LineNumber + 1;
+
+ int col;
+ if (bufferPosition.IsInVirtualSpace) {
+ col = bufferPosition.VirtualSpaces;
+ } else {
+ col = requestedPosition - snapshotLine.Start + 1;
+ }
+
+ TextEditor.SetCaretTo (line, col, false, false);
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/CaretImpl.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/CaretImpl.cs
index d27baf33ae..9822b5a151 100644
--- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/CaretImpl.cs
+++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/CaretImpl.cs
@@ -27,12 +27,13 @@
using System;
using System.Linq;
+using Microsoft.VisualStudio.Text;
using MonoDevelop.Core.Text;
using MonoDevelop.Ide.Editor;
namespace Mono.TextEditor
{
- class CaretImpl : MonoDevelop.Ide.Editor.Caret
+ partial class CaretImpl : MonoDevelop.Ide.Editor.Caret
{
bool isInInsertMode = true;
bool autoScrollToCaret = true;
@@ -196,6 +197,10 @@ namespace Mono.TextEditor
AllowCaretBehindLineEnd = false;
DesiredColumn = DocumentLocation.MinColumn;
AutoUpdatePosition = true;
+
+ // Set up initial values
+ _caretAffinity = PositionAffinity.Successor;
+ insertionPoint = new VirtualSnapshotPoint (new SnapshotPoint (editor.Document.TextBuffer.CurrentSnapshot, 0));
}
/// <summary>
@@ -324,6 +329,7 @@ namespace Mono.TextEditor
{
TextEditorData.Document.EnsureOffsetIsUnfolded (Offset);
base.OnPositionChanged (args);
+ PositionChanged_ITextCaret (args);
}
protected virtual void OnModeChanged ()
diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/TextDocument.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/TextDocument.cs
index 3d39f9b743..05a1c1db71 100644
--- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/TextDocument.cs
+++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/TextDocument.cs
@@ -35,6 +35,7 @@ using System.ComponentModel;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
+using MonoDevelop.Ide.Composition;
using MonoDevelop.Core.Text;
using MonoDevelop.Ide.Editor;
using MonoDevelop.Core;
@@ -42,6 +43,7 @@ using System.IO;
using MonoDevelop.Ide.Editor.Highlighting;
using Microsoft.VisualStudio.Platform;
using Microsoft.VisualStudio.Text.Tagging;
+using Microsoft.VisualStudio.Utilities;
namespace Mono.TextEditor
{
@@ -69,7 +71,7 @@ namespace Mono.TextEditor
return PlatformCatalog.Instance.MimeToContentTypeRegistryService.GetMimeType(snapshot.ContentType) ?? snapshot.ContentType.TypeName;
}
set {
- var newContentType = value != null ? GetContentTypeFromMimeType(value) : PlatformCatalog.Instance.ContentTypeRegistryService.UnknownContentType;
+ var newContentType = value != null ? GetContentTypeFromMimeType(null, value) : PlatformCatalog.Instance.ContentTypeRegistryService.UnknownContentType;
if (this.TextBuffer.CurrentSnapshot.ContentType != newContentType) {
this.TextBuffer.ChangeContentType(newContentType, null);
@@ -77,9 +79,20 @@ namespace Mono.TextEditor
}
}
- private static Microsoft.VisualStudio.Utilities.IContentType GetContentTypeFromMimeType(string mimeType)
+ private static Microsoft.VisualStudio.Utilities.IContentType GetContentTypeFromMimeType(string filePath, string mimeType)
{
- Microsoft.VisualStudio.Utilities.IContentType contentType = PlatformCatalog.Instance.MimeToContentTypeRegistryService.GetContentType(mimeType);
+ if (filePath != null)
+ {
+ IFilePathRegistryService filePathRegistryService = CompositionManager.GetExportedValue<IFilePathRegistryService> ();
+
+ IContentType contentTypeFromPath = filePathRegistryService.GetContentTypeForPath (filePath);
+ if (contentTypeFromPath != PlatformCatalog.Instance.ContentTypeRegistryService.UnknownContentType)
+ {
+ return contentTypeFromPath;
+ }
+ }
+
+ IContentType contentType = PlatformCatalog.Instance.MimeToContentTypeRegistryService.GetContentType (mimeType);
if (contentType == null)
{
// fallback 1: see if there is a content tyhpe with the same name
@@ -147,7 +160,7 @@ namespace Mono.TextEditor
void SyntaxMode_HighlightingStateChanged (object sender, MonoDevelop.Ide.Editor.LineEventArgs e)
{
- CommitDocumentUpdate ();
+ CommitMultipleLineUpdate (e.Line.LineNumber, e.Line.LineNumber);
}
void OnSyntaxModeChanged (SyntaxModeChangeEventArgs e)
@@ -213,6 +226,7 @@ namespace Mono.TextEditor
this.TextBuffer.Properties.AddProperty(typeof(ITextDocument), this);
this.TextBuffer.Changed += this.OnTextBufferChanged;
+ (this.TextBuffer as Microsoft.VisualStudio.Text.Implementation.BaseBuffer).ChangedImmediate += OnTextBufferChangedImmediate;
this.TextBuffer.ContentTypeChanged += this.OnTextBufferContentTypeChanged;
this.VsTextDocument.FileActionOccurred += this.OnTextDocumentFileActionOccured;
@@ -230,7 +244,7 @@ namespace Mono.TextEditor
SyntaxMode = null;
}
- void OnTextBufferChanged(object sender, Microsoft.VisualStudio.Text.TextContentChangedEventArgs args)
+ private void OnTextBufferChangedImmediate (object sender, Microsoft.VisualStudio.Text.TextContentChangedEventArgs args)
{
if (args.Changes == null)
return;
@@ -239,14 +253,25 @@ namespace Mono.TextEditor
changes.Add (new TextChange (change.OldPosition, change.NewPosition, change.OldText, change.NewText));
EnsureSegmentIsUnfolded(change.OldPosition, change.NewLength);
}
- bool endUndo = false;
- UndoOperation operation = null;
var textChange = new TextChangeEventArgs(changes);
InterruptFoldWorker();
TextChanging?.Invoke(this, textChange);
// After TextChanging notification has been sent, we can update the cached snapshot
this.currentSnapshot = args.After;
+ }
+
+ void OnTextBufferChanged(object sender, Microsoft.VisualStudio.Text.TextContentChangedEventArgs args)
+ {
+ if (args.Changes == null)
+ return;
+ var changes = new List<TextChange> ();
+ foreach (var change in args.Changes) {
+ changes.Add (new TextChange (change.OldPosition, change.NewPosition, change.OldText, change.NewText));
+ }
+ bool endUndo = false;
+ UndoOperation operation = null;
+ var textChange = new TextChangeEventArgs(changes);
if (!isInUndo) {
operation = new UndoOperation(args);
@@ -294,11 +319,11 @@ namespace Mono.TextEditor
public TextDocument (string fileName, string mimeType)
{
- var contentType = GetContentTypeFromMimeType (mimeType);
+ var contentType = (mimeType == null) ? PlatformCatalog.Instance.TextBufferFactoryService.InertContentType : GetContentTypeFromMimeType(fileName, mimeType);
Encoding enc;
var text = TextFileUtility.GetText (fileName, out enc);
var buffer = PlatformCatalog.Instance.TextBufferFactoryService.CreateTextBuffer (text ?? string.Empty,
- PlatformCatalog.Instance.TextBufferFactoryService.InertContentType);
+ contentType);
this.VsTextDocument = PlatformCatalog.Instance.TextDocumentFactoryService.CreateTextDocument (buffer, fileName);
this.VsTextDocument.Encoding = enc;
@@ -306,13 +331,14 @@ namespace Mono.TextEditor
this.Initialize();
}
- public TextDocument (string text = null)
+ public TextDocument (string text = null, string fileName = null, string mimeType = null)
{
- var buffer = PlatformCatalog.Instance.TextBufferFactoryService.CreateTextBuffer(text ?? string.Empty,
- PlatformCatalog.Instance.TextBufferFactoryService.InertContentType);
+ var contentType = (mimeType == null) ? PlatformCatalog.Instance.TextBufferFactoryService.InertContentType : GetContentTypeFromMimeType(fileName, mimeType);
+ var buffer = PlatformCatalog.Instance.TextBufferFactoryService.CreateTextBuffer (text ?? string.Empty,
+ contentType);
- this.VsTextDocument = PlatformCatalog.Instance.TextDocumentFactoryService.CreateTextDocument(buffer, string.Empty);
- this.VsTextDocument.Encoding = MonoDevelop.Core.Text.TextFileUtility.DefaultEncoding;
+ this.VsTextDocument = PlatformCatalog.Instance.TextDocumentFactoryService.CreateTextDocument(buffer, fileName ?? string.Empty);
+ this.VsTextDocument.Encoding = TextFileUtility.DefaultEncoding;
this.Initialize();
}
diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/TextEditorData.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/TextEditorData.cs
index dfd3a1e2c6..1609832a40 100644
--- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/TextEditorData.cs
+++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/TextEditorData.cs
@@ -206,12 +206,13 @@ namespace Mono.TextEditor
{
LineHeight = 16;
- caret = new CaretImpl (this);
- caret.PositionChanged += CaretPositionChanged;
-
options = TextEditorOptions.DefaultOptions;
document = doc;
AttachDocument ();
+
+ caret = new CaretImpl (this);
+ caret.PositionChanged += CaretPositionChanged;
+
SearchEngine = new BasicSearchEngine ();
HeightTree = new HeightTree (this);
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/RemoteBuildEngineManager.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/RemoteBuildEngineManager.cs
index ea8f0b4273..c9373afb86 100644
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/RemoteBuildEngineManager.cs
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/RemoteBuildEngineManager.cs
@@ -1,4 +1,4 @@
-//
+//
// RemoteBuildEngineManager.cs
//
// Author:
@@ -609,12 +609,12 @@ namespace MonoDevelop.Projects.MSBuild
spid = spid.Substring (0, i);
int pid;
if (int.TryParse (Path.GetFileName (spid), out pid)) {
- try {
- // If there is a process running with this id it means the builder is still being used
- if (Process.GetProcessById (pid) != null)
+ try {
+ // If there is a process running with this id it means the builder is still being used
+ if (Process.GetProcessById (pid) != null)
continue;
} catch {
- // Ignore
+ // Ignore
}
// No process for this id, it should be safe to delete the folder
try {
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/SdkResolution.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/SdkResolution.cs
index 46768a95f0..6b0b36c660 100644
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/SdkResolution.cs
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/SdkResolution.cs
@@ -115,7 +115,7 @@ namespace MonoDevelop.Projects.MSBuild
resolvers.AddRange (assembly.ExportedTypes
.Select (type => new { type, info = type.GetTypeInfo () })
- .Where (t => t.info.IsClass && t.info.IsPublic && typeof (SdkResolver).IsAssignableFrom (t.type))
+ .Where (t => t.info.IsClass && t.info.IsPublic && !t.info.IsAbstract && typeof (SdkResolver).IsAssignableFrom (t.type))
.Select (t => (SdkResolver)Activator.CreateInstance (t.type)));
} catch (Exception e) {
logger.LogWarning (e.Message);
diff --git a/main/src/core/MonoDevelop.Core/packages.config b/main/src/core/MonoDevelop.Core/packages.config
index b71392f474..e32f14c0a0 100644
--- a/main/src/core/MonoDevelop.Core/packages.config
+++ b/main/src/core/MonoDevelop.Core/packages.config
@@ -1,90 +1,91 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
- <package id="Humanizer.Core" version="2.2.0" targetFramework="net461" />
- <package id="ManagedEsent" version="1.9.4" targetFramework="net461" />
- <package id="Microsoft.CodeAnalysis" version="2.7.0-beta3-62509-03" targetFramework="net461" />
- <package id="Microsoft.CodeAnalysis.Analyzers" version="1.2.0-beta2" targetFramework="net461" />
- <package id="Microsoft.CodeAnalysis.Common" version="2.7.0-beta3-62509-03" targetFramework="net461" />
- <package id="Microsoft.CodeAnalysis.CSharp" version="2.7.0-beta3-62509-03" targetFramework="net461" />
- <package id="Microsoft.CodeAnalysis.CSharp.Features" version="2.7.0-beta3-62509-03" targetFramework="net461" />
- <package id="Microsoft.CodeAnalysis.CSharp.Workspaces" version="2.7.0-beta3-62509-03" targetFramework="net461" />
- <package id="Microsoft.CodeAnalysis.EditorFeatures" version="2.7.0-beta3-62509-03" targetFramework="net461" />
- <package id="Microsoft.CodeAnalysis.EditorFeatures.Text" version="2.7.0-beta3-62509-03" targetFramework="net461" />
- <package id="Microsoft.CodeAnalysis.Elfie" version="1.0.0-rc9" targetFramework="net461" />
- <package id="Microsoft.CodeAnalysis.Features" version="2.7.0-beta3-62509-03" targetFramework="net461" />
- <package id="Microsoft.CodeAnalysis.VisualBasic" version="2.7.0-beta3-62509-03" targetFramework="net461" />
- <package id="Microsoft.CodeAnalysis.VisualBasic.Features" version="2.7.0-beta3-62509-03" targetFramework="net461" />
- <package id="Microsoft.CodeAnalysis.VisualBasic.Workspaces" version="2.7.0-beta3-62509-03" targetFramework="net461" />
- <package id="Microsoft.CodeAnalysis.Workspaces.Common" version="2.7.0-beta3-62509-03" targetFramework="net461" />
- <package id="Microsoft.Composition" version="1.0.30" targetFramework="net461" />
- <package id="Microsoft.VisualStudio.Composition" version="15.3.38" targetFramework="net461" />
- <package id="Microsoft.VisualStudio.CoreUtility" version="15.6.161-preview" targetFramework="net45" />
- <package id="Microsoft.VisualStudio.Language.Intellisense" version="15.6.161-preview" targetFramework="net45" />
- <package id="Microsoft.VisualStudio.Language.StandardClassification" version="15.6.161-preview" targetFramework="net45" />
- <package id="Microsoft.VisualStudio.Text.Data" version="15.6.161-preview" targetFramework="net45" />
- <package id="Microsoft.VisualStudio.Text.Internal" version="15.6.161-preview" targetFramework="net45" />
- <package id="Microsoft.VisualStudio.Text.Logic" version="15.6.161-preview" targetFramework="net45" />
- <package id="Microsoft.VisualStudio.Text.UI" version="15.6.161-preview" targetFramework="net45" />
- <package id="Microsoft.VisualStudio.Text.UI.Wpf" version="15.6.161-preview" targetFramework="net45" />
- <package id="Microsoft.VisualStudio.Text.Implementation" version="15.0.7-pre" targetFramework="net45" />
- <package id="Microsoft.VisualStudio.Threading" version="15.4.4" targetFramework="net461" />
- <package id="Microsoft.VisualStudio.Validation" version="15.3.15" targetFramework="net461" />
- <package id="Mono.Cecil" version="0.10.0-beta6" targetFramework="net45" />
- <package id="Newtonsoft.Json" version="10.0.3" targetFramework="net461" />
- <package id="SharpZipLib" version="0.86.0" targetFramework="net45" />
- <package id="SQLitePCLRaw.bundle_e_sqlite3" version="1.1.6" targetFramework="net461" />
- <package id="SQLitePCLRaw.core" version="1.1.6" targetFramework="net461" />
- <package id="SQLitePCLRaw.lib.e_sqlite3.linux" version="1.1.6" targetFramework="net461" />
- <package id="SQLitePCLRaw.lib.e_sqlite3.osx" version="1.1.6" targetFramework="net461" />
- <package id="SQLitePCLRaw.lib.e_sqlite3.v110_xp" version="1.1.6" targetFramework="net461" />
- <package id="SQLitePCLRaw.provider.e_sqlite3.net45" version="1.1.6" targetFramework="net461" />
- <package id="System.AppContext" version="4.3.0" targetFramework="net461" />
- <package id="System.Collections" version="4.3.0" targetFramework="net461" />
- <package id="System.Collections.Concurrent" version="4.3.0" targetFramework="net461" />
- <package id="System.Collections.Immutable" version="1.3.1" targetFramework="net461" />
- <package id="System.Composition.AttributedModel" version="1.0.31" targetFramework="net461" />
- <package id="System.Composition.Hosting" version="1.0.31" targetFramework="net461" />
- <package id="System.Composition.Runtime" version="1.0.31" targetFramework="net461" />
- <package id="System.Composition.TypedParts" version="1.0.31" targetFramework="net461" />
- <package id="System.Console" version="4.3.0" targetFramework="net461" />
- <package id="System.Diagnostics.Contracts" version="4.3.0" targetFramework="net461" />
- <package id="System.Diagnostics.Debug" version="4.3.0" targetFramework="net461" />
- <package id="System.Diagnostics.FileVersionInfo" version="4.3.0" targetFramework="net461" />
- <package id="System.Diagnostics.StackTrace" version="4.3.0" targetFramework="net461" />
- <package id="System.Diagnostics.Tools" version="4.3.0" targetFramework="net461" />
- <package id="System.Dynamic.Runtime" version="4.3.0" targetFramework="net461" />
- <package id="System.Globalization" version="4.3.0" targetFramework="net461" />
- <package id="System.IO.Compression" version="4.3.0" targetFramework="net461" />
- <package id="System.IO.FileSystem" version="4.3.0" targetFramework="net461" />
- <package id="System.IO.FileSystem.Primitives" version="4.3.0" targetFramework="net461" />
- <package id="System.Linq" version="4.3.0" targetFramework="net461" />
- <package id="System.Linq.Expressions" version="4.3.0" targetFramework="net461" />
- <package id="System.Linq.Parallel" version="4.3.0" targetFramework="net461" />
- <package id="System.ObjectModel" version="4.3.0" targetFramework="net461" />
- <package id="System.Reflection" version="4.3.0" targetFramework="net461" />
- <package id="System.Reflection.Metadata" version="1.4.2" targetFramework="net461" />
- <package id="System.Resources.ResourceManager" version="4.3.0" targetFramework="net461" />
- <package id="System.Runtime" version="4.3.0" targetFramework="net461" />
- <package id="System.Runtime.Extensions" version="4.3.0" targetFramework="net461" />
- <package id="System.Runtime.InteropServices" version="4.3.0" targetFramework="net461" />
- <package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net461" />
- <package id="System.Runtime.Numerics" version="4.3.0" targetFramework="net461" />
- <package id="System.Security.Cryptography.Algorithms" version="4.3.0" targetFramework="net461" />
- <package id="System.Security.Cryptography.Encoding" version="4.3.0" targetFramework="net461" />
- <package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net461" />
- <package id="System.Security.Cryptography.X509Certificates" version="4.3.0" targetFramework="net461" />
- <package id="System.Text.Encoding" version="4.3.0" targetFramework="net461" />
- <package id="System.Text.Encoding.CodePages" version="4.3.0" targetFramework="net461" />
- <package id="System.Text.Encoding.Extensions" version="4.3.0" targetFramework="net461" />
- <package id="System.Text.RegularExpressions" version="4.3.0" targetFramework="net461" />
- <package id="System.Threading" version="4.3.0" targetFramework="net461" />
- <package id="System.Threading.Tasks" version="4.3.0" targetFramework="net461" />
- <package id="System.Threading.Tasks.Parallel" version="4.3.0" targetFramework="net461" />
- <package id="System.Threading.Thread" version="4.3.0" targetFramework="net461" />
- <package id="System.ValueTuple" version="4.4.0" targetFramework="net461" />
- <package id="System.Xml.ReaderWriter" version="4.3.0" targetFramework="net461" />
- <package id="System.Xml.XDocument" version="4.3.0" targetFramework="net461" />
- <package id="System.Xml.XmlDocument" version="4.3.0" targetFramework="net461" />
- <package id="System.Xml.XPath" version="4.3.0" targetFramework="net461" />
- <package id="System.Xml.XPath.XDocument" version="4.3.0" targetFramework="net461" />
-</packages>
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="Humanizer.Core" version="2.2.0" targetFramework="net461" />
+ <package id="ManagedEsent" version="1.9.4" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis" version="2.7.0-beta3-62509-03" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis.Analyzers" version="1.2.0-beta2" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis.Common" version="2.7.0-beta3-62509-03" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis.CSharp" version="2.7.0-beta3-62509-03" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis.CSharp.Features" version="2.7.0-beta3-62509-03" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis.CSharp.Workspaces" version="2.7.0-beta3-62509-03" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis.EditorFeatures" version="2.7.0-beta3-62509-03" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis.EditorFeatures.Text" version="2.7.0-beta3-62509-03" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis.Elfie" version="1.0.0-rc9" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis.Features" version="2.7.0-beta3-62509-03" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis.VisualBasic" version="2.7.0-beta3-62509-03" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis.VisualBasic.Features" version="2.7.0-beta3-62509-03" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis.VisualBasic.Workspaces" version="2.7.0-beta3-62509-03" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis.Workspaces.Common" version="2.7.0-beta3-62509-03" targetFramework="net461" />
+ <package id="Microsoft.Composition" version="1.0.30" targetFramework="net461" />
+ <package id="Microsoft.VisualStudio.Composition" version="15.3.38" targetFramework="net461" />
+ <package id="Microsoft.VisualStudio.CoreUtility" version="15.6.241-preview" targetFramework="net45" />
+ <package id="Microsoft.VisualStudio.Language" version="15.6.241-preview" targetFramework="net461" />
+ <package id="Microsoft.VisualStudio.Language.Intellisense" version="15.6.241-preview" targetFramework="net45" />
+ <package id="Microsoft.VisualStudio.Language.StandardClassification" version="15.6.241-preview" targetFramework="net45" />
+ <package id="Microsoft.VisualStudio.Text.Data" version="15.6.241-preview" targetFramework="net45" />
+ <package id="Microsoft.VisualStudio.Text.Internal" version="15.6.241-preview" targetFramework="net45" />
+ <package id="Microsoft.VisualStudio.Text.Logic" version="15.6.241-preview" targetFramework="net45" />
+ <package id="Microsoft.VisualStudio.Text.UI" version="15.6.241-preview" targetFramework="net45" />
+ <package id="Microsoft.VisualStudio.Text.UI.Wpf" version="15.6.241-preview" targetFramework="net45" />
+ <package id="Microsoft.VisualStudio.Text.Implementation" version="15.1.0-pre" targetFramework="net461" />
+ <package id="Microsoft.VisualStudio.Threading" version="15.4.4" targetFramework="net461" />
+ <package id="Microsoft.VisualStudio.Validation" version="15.3.15" targetFramework="net461" />
+ <package id="Mono.Cecil" version="0.10.0-beta6" targetFramework="net45" />
+ <package id="Newtonsoft.Json" version="10.0.3" targetFramework="net461" />
+ <package id="SharpZipLib" version="0.86.0" targetFramework="net45" />
+ <package id="SQLitePCLRaw.bundle_e_sqlite3" version="1.1.6" targetFramework="net461" />
+ <package id="SQLitePCLRaw.core" version="1.1.6" targetFramework="net461" />
+ <package id="SQLitePCLRaw.lib.e_sqlite3.linux" version="1.1.6" targetFramework="net461" />
+ <package id="SQLitePCLRaw.lib.e_sqlite3.osx" version="1.1.6" targetFramework="net461" />
+ <package id="SQLitePCLRaw.lib.e_sqlite3.v110_xp" version="1.1.6" targetFramework="net461" />
+ <package id="SQLitePCLRaw.provider.e_sqlite3.net45" version="1.1.6" targetFramework="net461" />
+ <package id="System.AppContext" version="4.3.0" targetFramework="net461" />
+ <package id="System.Collections" version="4.3.0" targetFramework="net461" />
+ <package id="System.Collections.Concurrent" version="4.3.0" targetFramework="net461" />
+ <package id="System.Collections.Immutable" version="1.3.1" targetFramework="net461" />
+ <package id="System.Composition.AttributedModel" version="1.0.31" targetFramework="net461" />
+ <package id="System.Composition.Hosting" version="1.0.31" targetFramework="net461" />
+ <package id="System.Composition.Runtime" version="1.0.31" targetFramework="net461" />
+ <package id="System.Composition.TypedParts" version="1.0.31" targetFramework="net461" />
+ <package id="System.Console" version="4.3.0" targetFramework="net461" />
+ <package id="System.Diagnostics.Contracts" version="4.3.0" targetFramework="net461" />
+ <package id="System.Diagnostics.Debug" version="4.3.0" targetFramework="net461" />
+ <package id="System.Diagnostics.FileVersionInfo" version="4.3.0" targetFramework="net461" />
+ <package id="System.Diagnostics.StackTrace" version="4.3.0" targetFramework="net461" />
+ <package id="System.Diagnostics.Tools" version="4.3.0" targetFramework="net461" />
+ <package id="System.Dynamic.Runtime" version="4.3.0" targetFramework="net461" />
+ <package id="System.Globalization" version="4.3.0" targetFramework="net461" />
+ <package id="System.IO.Compression" version="4.3.0" targetFramework="net461" />
+ <package id="System.IO.FileSystem" version="4.3.0" targetFramework="net461" />
+ <package id="System.IO.FileSystem.Primitives" version="4.3.0" targetFramework="net461" />
+ <package id="System.Linq" version="4.3.0" targetFramework="net461" />
+ <package id="System.Linq.Expressions" version="4.3.0" targetFramework="net461" />
+ <package id="System.Linq.Parallel" version="4.3.0" targetFramework="net461" />
+ <package id="System.ObjectModel" version="4.3.0" targetFramework="net461" />
+ <package id="System.Reflection" version="4.3.0" targetFramework="net461" />
+ <package id="System.Reflection.Metadata" version="1.4.2" targetFramework="net461" />
+ <package id="System.Resources.ResourceManager" version="4.3.0" targetFramework="net461" />
+ <package id="System.Runtime" version="4.3.0" targetFramework="net461" />
+ <package id="System.Runtime.Extensions" version="4.3.0" targetFramework="net461" />
+ <package id="System.Runtime.InteropServices" version="4.3.0" targetFramework="net461" />
+ <package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net461" />
+ <package id="System.Runtime.Numerics" version="4.3.0" targetFramework="net461" />
+ <package id="System.Security.Cryptography.Algorithms" version="4.3.0" targetFramework="net461" />
+ <package id="System.Security.Cryptography.Encoding" version="4.3.0" targetFramework="net461" />
+ <package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net461" />
+ <package id="System.Security.Cryptography.X509Certificates" version="4.3.0" targetFramework="net461" />
+ <package id="System.Text.Encoding" version="4.3.0" targetFramework="net461" />
+ <package id="System.Text.Encoding.CodePages" version="4.3.0" targetFramework="net461" />
+ <package id="System.Text.Encoding.Extensions" version="4.3.0" targetFramework="net461" />
+ <package id="System.Text.RegularExpressions" version="4.3.0" targetFramework="net461" />
+ <package id="System.Threading" version="4.3.0" targetFramework="net461" />
+ <package id="System.Threading.Tasks" version="4.3.0" targetFramework="net461" />
+ <package id="System.Threading.Tasks.Parallel" version="4.3.0" targetFramework="net461" />
+ <package id="System.Threading.Thread" version="4.3.0" targetFramework="net461" />
+ <package id="System.ValueTuple" version="4.4.0" targetFramework="net461" />
+ <package id="System.Xml.ReaderWriter" version="4.3.0" targetFramework="net461" />
+ <package id="System.Xml.XDocument" version="4.3.0" targetFramework="net461" />
+ <package id="System.Xml.XmlDocument" version="4.3.0" targetFramework="net461" />
+ <package id="System.Xml.XPath" version="4.3.0" targetFramework="net461" />
+ <package id="System.Xml.XPath.XDocument" version="4.3.0" targetFramework="net461" />
+</packages>
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 4423f6f512..e9da2928c4 100644
--- a/main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml
+++ b/main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml
@@ -401,6 +401,8 @@
</Extension>
<Extension path="/MonoDevelop/Ide/Composition">
+ <Assembly file="Microsoft.CodeAnalysis.CSharp.EditorFeatures.dll"/>
+ <Assembly file="Microsoft.CodeAnalysis.EditorFeatures.dll"/>
<Assembly file="Microsoft.CodeAnalysis.Features.dll" />
<Assembly file="Microsoft.CodeAnalysis.Workspaces.dll" />
<Assembly file="Microsoft.CodeAnalysis.Workspaces.Desktop.dll" />
@@ -410,6 +412,8 @@
<Assembly file="Microsoft.CodeAnalysis.VisualBasic.Workspaces.dll" />
<Assembly file="MonoDevelop.Ide.dll"/>
<Assembly file="Microsoft.VisualStudio.Text.Implementation.dll"/>
+ <Assembly file="Microsoft.VisualStudio.Text.Logic.dll"/>
+ <Assembly file="Microsoft.VisualStudio.Text.UI.dll"/>
<Assembly file="Microsoft.VisualStudio.Language.StandardClassification.dll"/>
<Assembly file="Microsoft.CodeAnalysis.CSharp.EditorFeatures.dll" />
</Extension>
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuExtensionsMac.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuExtensionsMac.cs
index 30706dedee..34f8345a9c 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuExtensionsMac.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuExtensionsMac.cs
@@ -157,7 +157,8 @@ namespace MonoDevelop.Components
public NSContextMenuItem (string label, ContextMenuItem item) : base (label)
{
contextMenu = new WeakReference<ContextMenuItem> (item);
- this.Activated += OnActivated;
+ if (item.SubMenu == null || item.SubMenu.Items.Count == 0)
+ this.Activated += OnActivated;
}
static void OnActivated (object sender, EventArgs args)
@@ -195,6 +196,7 @@ namespace MonoDevelop.Components
public NSLocationAwareMenu (ContextMenu menu, Action closeHandler, NSLocationAwareMenu parent)
{
WeakDelegate = new ContextMenuDelegate (menu) { CloseHandler = closeHandler };
+
Parent = parent != null ? new WeakReference<NSLocationAwareMenu> (parent) : null;
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionController.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionController.cs
index 1263f0f5a7..00e5de7e0c 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionController.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionController.cs
@@ -1,4 +1,4 @@
-//
+//
// CodeCompletionSession.cs
//
// Author:
@@ -32,6 +32,7 @@ using Gtk;
using MonoDevelop.Ide.Editor.Extension;
using Xwt.Drawing;
using MonoDevelop.Core;
+using MonoDevelop.Ide.Editor;
namespace MonoDevelop.Ide.CodeCompletion
{
@@ -205,7 +206,6 @@ namespace MonoDevelop.Ide.CodeCompletion
initialWordLength = CompletionWidget.SelectedLength > 0 ? 0 : text.Length;
StartOffset = CompletionWidget.CaretOffset - initialWordLength;
- HideWhenWordDeleted = initialWordLength != 0;
ResetSizes ();
UpdateWordSelection ();
@@ -259,7 +259,6 @@ namespace MonoDevelop.Ide.CodeCompletion
{
usingPreviewEntry = false;
previewCompletionEntryText = "";
- HideWhenWordDeleted = false;
SelectedItemCompletionText = null;
ResetViewState();
}
@@ -505,10 +504,6 @@ namespace MonoDevelop.Ide.CodeCompletion
get { return initialWordLength; }
}
- bool HideWhenWordDeleted {
- get; set;
- }
-
public CompletionTextEditorExtension Extension {
get;
set;
@@ -710,15 +705,7 @@ namespace MonoDevelop.Ide.CodeCompletion
set;
}
- int startOffset;
- internal int StartOffset {
- get {
- return startOffset;
- }
- set {
- startOffset = value;
- }
- }
+ internal int StartOffset { get; set; }
public int EndOffset {
get;
@@ -1058,13 +1045,10 @@ namespace MonoDevelop.Ide.CodeCompletion
public KeyActions PostProcessKey (KeyDescriptor descriptor)
{
- if (CompletionWidget == null || StartOffset > CompletionWidget.CaretOffset) {// CompletionWidget == null may happen in unit tests.
+ if (CompletionWidget == null) {// CompletionWidget == null may happen in unit tests.
return KeyActions.CloseWindow | KeyActions.Process;
}
- if (HideWhenWordDeleted && StartOffset >= CompletionWidget.CaretOffset) {
- return KeyActions.CloseWindow | KeyActions.Process;
- }
switch (descriptor.SpecialKey) {
case SpecialKey.BackSpace:
ResetSizes ();
@@ -1166,7 +1150,7 @@ namespace MonoDevelop.Ide.CodeCompletion
void UpdateLastWordChar ()
{
if (CompletionWidget != null)
- EndOffset = CompletionWidget.CaretOffset;
+ EndOffset = Math.Max (StartOffset, CompletionWidget.CaretOffset);
}
void SelectEntry (string s)
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 df602402bb..eac7135294 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionWindowManager.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionWindowManager.cs
@@ -28,6 +28,7 @@ using System;
using MonoDevelop.Core;
using MonoDevelop.Ide.Gui.Content;
using MonoDevelop.Ide.Editor.Extension;
+using MonoDevelop.Ide.Editor;
namespace MonoDevelop.Ide.CodeCompletion
{
@@ -176,15 +177,26 @@ namespace MonoDevelop.Ide.CodeCompletion
return wnd.PreProcessKeyEvent (descriptor);
}
+ static bool isInUpdate;
public static void UpdateCursorPosition ()
{
if (!IsVisible)
return;
- if (wnd.IsInCompletion || isShowing)
+ if (wnd.IsInCompletion || isShowing || isInUpdate)
return;
- var caretOffset = wnd.CompletionWidget.CaretOffset;
- if (caretOffset < wnd.StartOffset || caretOffset > wnd.EndOffset + 1) {
- HideWindow ();
+ isInUpdate = true;
+ try {
+ ITextEditorImpl impl = wnd.CompletionWidget as ITextEditorImpl;
+ if (impl != null)
+ impl.EnsureCaretIsNotVirtual ();
+ var caretOffset = wnd.CompletionWidget.CaretOffset;
+ if (caretOffset < wnd.StartOffset || caretOffset > wnd.EndOffset + 1) {
+ HideWindow ();
+ }
+ if (impl != null)
+ impl.FixVirtualIndentation ();
+ } finally {
+ isInUpdate = false;
}
}
@@ -213,8 +225,9 @@ namespace MonoDevelop.Ide.CodeCompletion
public static void HideWindow ()
{
isShowing = false;
- if (IsVisible)
+ if (IsVisible) {
wnd.HideWindow ();
+ }
}
static void HandleWndVisibleChanged (object sender, EventArgs args)
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/InlineRenameService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/InlineRenameService.cs
new file mode 100644
index 0000000000..a13d665518
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/InlineRenameService.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Editor;
+using Microsoft.CodeAnalysis.Text;
+
+namespace MonoDevelop.Ide.Composition
+{
+ [Export(typeof(IInlineRenameService))]
+ internal class InlineRenameService : IInlineRenameService
+ {
+ public IInlineRenameSession ActiveSession => null;
+
+ public InlineRenameSessionInfo StartInlineSession (Document document, TextSpan triggerSpan, CancellationToken cancellationToken = default (CancellationToken))
+ {
+ throw new NotImplementedException ();
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/PlatformCatalog.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/PlatformCatalog.cs
index c0148e3a65..2e8163e388 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/PlatformCatalog.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/PlatformCatalog.cs
@@ -94,7 +94,7 @@ namespace Microsoft.VisualStudio.Platform
return mimeType;
}
- return null;
+ return (ContentTypeRegistryService as IContentTypeRegistryService2).GetMimeType (type);
}
public IContentType GetContentType(string type)
@@ -105,7 +105,7 @@ namespace Microsoft.VisualStudio.Platform
return contentType;
}
- return null;
+ return (ContentTypeRegistryService as IContentTypeRegistryService2).GetContentTypeForMimeType (type);
}
public void LinkTypes(string mimeType, IContentType contentType)
@@ -136,14 +136,6 @@ namespace Microsoft.VisualStudio.Platform
{
LinkTypes ("text/plain", "text");
LinkTypes ("text/x-csharp", "csharp");
-
- if (this.ContentTypeRegistryService.GetContentType ("css") != null) {
- LinkTypes ("text/x-css", "css");
- LinkTypes ("text/x-less-web", "LESS");
- LinkTypes ("text/x-scss-web", "SCSS");
- LinkTypes ("text/x-html", "htmlx");
- LinkTypes ("text/x-json", "JSON");
- }
}
Tuple<ImmutableDictionary<string, IContentType>, ImmutableDictionary<IContentType, string>> maps = Tuple.Create(ImmutableDictionary<string, IContentType>.Empty, ImmutableDictionary<IContentType, string>.Empty);
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Desktop/PlatformService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Desktop/PlatformService.cs
index 486c780048..3355502f17 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Desktop/PlatformService.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Desktop/PlatformService.cs
@@ -34,6 +34,9 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Text.RegularExpressions;
+using Microsoft.VisualStudio.Platform;
+using Microsoft.VisualStudio.Utilities;
+
using Mono.Addins;
using MonoDevelop.Core;
using Mono.Unix;
@@ -41,8 +44,8 @@ using MonoDevelop.Ide.Extensions;
using MonoDevelop.Core.Execution;
using MonoDevelop.Components;
using MonoDevelop.Components.MainToolbar;
-
-
+using MonoDevelop.Ide.Composition;
+
namespace MonoDevelop.Ide.Desktop
{
public abstract class PlatformService
@@ -296,6 +299,19 @@ namespace MonoDevelop.Ide.Desktop
MimeTypeNode FindMimeTypeForFile (string fileName)
{
+ IFilePathRegistryService filePathRegistryService = CompositionManager.GetExportedValue<IFilePathRegistryService> ();
+
+ IContentType contentType = filePathRegistryService.GetContentTypeForPath (fileName);
+ if (contentType != PlatformCatalog.Instance.ContentTypeRegistryService.UnknownContentType) {
+ string mimeType = PlatformCatalog.Instance.MimeToContentTypeRegistryService.GetMimeType(contentType);
+ if (mimeType != null) {
+ MimeTypeNode mt = FindMimeType(mimeType);
+ if (mt != null) {
+ return mt;
+ }
+ }
+ }
+
foreach (MimeTypeNode mt in mimeTypeNodes) {
if (mt.SupportsFile (fileName))
return mt;
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/EditActions.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/EditActions.cs
index 9fdeca3319..ea6fe11b15 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/EditActions.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/EditActions.cs
@@ -37,129 +37,129 @@ namespace MonoDevelop.Ide.Editor
{
public static void MoveCaretDown (TextEditor editor)
{
- editor.EditorActionHost.MoveCaretDown ();
+ editor.EditorOperations.MoveLineDown (false);
}
public static void MoveCaretUp (TextEditor editor)
{
- editor.EditorActionHost.MoveCaretUp ();
+ editor.EditorOperations.MoveLineUp (false);
}
public static void MoveCaretRight (TextEditor editor)
{
- editor.EditorActionHost.MoveCaretRight ();
+ editor.EditorOperations.MoveToPreviousCharacter (false);
}
public static void MoveCaretLeft (TextEditor editor)
{
- editor.EditorActionHost.MoveCaretLeft ();
+ editor.EditorOperations.MoveToNextCharacter (false);
}
public static void MoveCaretToLineEnd (TextEditor editor)
{
- editor.EditorActionHost.MoveCaretToLineEnd ();
+ editor.EditorOperations.MoveToEndOfLine (false);
}
public static void MoveCaretToLineStart (TextEditor editor)
{
- editor.EditorActionHost.MoveCaretToLineStart ();
+ editor.EditorOperations.MoveToStartOfLine (false);
}
public static void MoveCaretToDocumentStart (TextEditor editor)
{
- editor.EditorActionHost.MoveCaretToDocumentStart ();
+ editor.EditorOperations.MoveToStartOfDocument (false);
}
public static void MoveCaretToDocumentEnd (TextEditor editor)
{
- editor.EditorActionHost.MoveCaretToDocumentEnd ();
+ editor.EditorOperations.MoveToEndOfDocument (false);
}
public static void Backspace (TextEditor editor)
{
- editor.EditorActionHost.Backspace ();
+ editor.EditorOperations.Backspace ();
}
public static void Delete (TextEditor editor)
{
- editor.EditorActionHost.Delete ();
+ editor.EditorOperations.Delete ();
}
public static void ClipboardCopy (TextEditor editor)
{
- editor.EditorActionHost.ClipboardCopy ();
+ editor.EditorOperations.CopySelection ();
}
public static void ClipboardCut (TextEditor editor)
{
- editor.EditorActionHost.ClipboardCut ();
+ editor.EditorOperations.CutSelection ();
}
public static void ClipboardPaste (TextEditor editor)
{
- editor.EditorActionHost.ClipboardPaste ();
+ editor.EditorOperations.Paste ();
}
public static void SelectAll (TextEditor editor)
{
- editor.EditorActionHost.SelectAll ();
+ editor.EditorOperations.SelectAll ();
}
public static void NewLine (TextEditor editor)
{
- editor.EditorActionHost.NewLine ();
+ editor.EditorOperations.InsertNewLine ();
}
public static void PageUp (TextEditor textEditor)
{
- textEditor.EditorActionHost.PageUp ();
+ textEditor.EditorOperations.PageUp (false);
}
public static void PageDown (TextEditor textEditor)
{
- textEditor.EditorActionHost.PageDown ();
+ textEditor.EditorOperations.PageDown (false);
}
public static void Undo (TextEditor editor)
{
- editor.EditorActionHost.Undo ();
+ ((IMonoDevelopEditorOperations)editor.EditorOperations).Undo ();
}
public static void Redo (TextEditor editor)
{
- editor.EditorActionHost.Redo ();
+ ((IMonoDevelopEditorOperations)editor.EditorOperations).Redo ();
}
public static void DeleteCurrentLine (TextEditor textEditor)
{
- textEditor.EditorActionHost.DeleteCurrentLine ();
+ textEditor.EditorOperations.DeleteFullLine ();
}
public static void DeleteCurrentLineToEnd (TextEditor textEditor)
{
- textEditor.EditorActionHost.DeleteCurrentLineToEnd ();
+ textEditor.EditorOperations.DeleteToEndOfLine ();
}
public static void ScrollLineUp (TextEditor textEditor)
{
- textEditor.EditorActionHost.ScrollLineUp ();
+ textEditor.EditorOperations.ScrollLineTop ();
}
public static void ScrollLineDown (TextEditor textEditor)
{
- textEditor.EditorActionHost.ScrollLineDown ();
+ textEditor.EditorOperations.ScrollLineBottom ();
}
public static void ScrollPageUp (TextEditor textEditor)
{
- textEditor.EditorActionHost.ScrollPageUp ();
+ textEditor.EditorOperations.ScrollPageUp ();
}
public static void ScrollPageDown (TextEditor textEditor)
{
- textEditor.EditorActionHost.ScrollPageDown ();
+ textEditor.EditorOperations.ScrollPageDown ();
}
public static void GotoMatchingBrace (TextEditor textEditor)
@@ -171,27 +171,27 @@ namespace MonoDevelop.Ide.Editor
public static void MovePrevWord (TextEditor textEditor)
{
- textEditor.EditorActionHost.MovePrevWord ();
+ textEditor.EditorOperations.MoveToPreviousWord (false);
}
public static void MoveNextWord (TextEditor textEditor)
{
- textEditor.EditorActionHost.MoveNextWord ();
+ textEditor.EditorOperations.MoveToNextWord (false);
}
public static void MovePrevSubWord (TextEditor textEditor)
{
- textEditor.EditorActionHost.MovePrevSubWord ();
+ ((IMonoDevelopEditorOperations)textEditor.EditorOperations).MoveToPrevSubWord ();
}
public static void MoveNextSubWord (TextEditor textEditor)
{
- textEditor.EditorActionHost.MoveNextSubWord ();
+ ((IMonoDevelopEditorOperations)textEditor.EditorOperations).MoveToNextSubWord ();
}
public static void ShowQuickInfo (TextEditor textEditor)
{
- textEditor.EditorActionHost.ShowQuickInfo ();
+ ((IMonoDevelopEditorOperations)textEditor.EditorOperations).ShowQuickInfo ();
}
@@ -268,37 +268,37 @@ namespace MonoDevelop.Ide.Editor
public static void JoinLines (TextEditor textEditor)
{
- textEditor.EditorActionHost.JoinLines ();
+ ((IMonoDevelopEditorOperations)textEditor.EditorOperations).JoinLines ();
}
public static void RecenterEditor (TextEditor textEditor)
{
- textEditor.EditorActionHost.RecenterEditor ();
+ textEditor.EditorOperations.ScrollLineCenter ();
}
public static void StartCaretPulseAnimation (TextEditor textEditor)
{
- textEditor.EditorActionHost.StartCaretPulseAnimation ();
+ ((IMonoDevelopEditorOperations)textEditor.EditorOperations).StartCaretPulseAnimation ();
}
public static void DeleteNextSubword (TextEditor textEditor)
{
- textEditor.EditorActionHost.DeleteNextSubword ();
+ ((IMonoDevelopEditorOperations)textEditor.EditorOperations).DeleteNextSubword ();
}
public static void DeletePreviousSubword (TextEditor textEditor)
{
- textEditor.EditorActionHost.DeletePreviousSubword ();
+ ((IMonoDevelopEditorOperations)textEditor.EditorOperations).DeletePreviousSubword ();
}
public static void DeleteNextWord (TextEditor textEditor)
{
- textEditor.EditorActionHost.DeleteNextWord ();
+ textEditor.EditorOperations.DeleteWordToRight ();
}
public static void DeletePreviousWord (TextEditor textEditor)
{
- textEditor.EditorActionHost.DeletePreviousWord ();
+ textEditor.EditorOperations.DeleteWordToLeft ();
}
public static void InsertNewLinePreserveCaretPosition (TextEditor textEditor)
@@ -324,47 +324,47 @@ namespace MonoDevelop.Ide.Editor
public static void InsertNewLine (TextEditor textEditor)
{
- textEditor.EditorActionHost.InsertNewLine ();
+ textEditor.EditorOperations.InsertNewLine ();
}
public static void RemoveTab (TextEditor textEditor)
{
- textEditor.EditorActionHost.RemoveTab ();
+ textEditor.EditorOperations.Untabify ();
}
public static void InsertTab (TextEditor textEditor)
{
- textEditor.EditorActionHost.InsertTab ();
+ textEditor.EditorOperations.Tabify ();
}
public static void SwitchCaretMode (TextEditor textEditor)
{
- textEditor.EditorActionHost.SwitchCaretMode ();
+ ((IMonoDevelopEditorOperations)textEditor.EditorOperations).SwitchCaretMode ();
}
public static void MoveBlockUp (TextEditor textEditor)
{
- textEditor.EditorActionHost.MoveBlockUp ();
+ ((IMonoDevelopEditorOperations)textEditor.EditorOperations).MoveBlockUp ();
}
public static void MoveBlockDown (TextEditor textEditor)
{
- textEditor.EditorActionHost.MoveBlockDown ();
+ ((IMonoDevelopEditorOperations)textEditor.EditorOperations).MoveBlockDown ();
}
public static void ToggleBlockSelectionMode (TextEditor textEditor)
{
- textEditor.EditorActionHost.ToggleBlockSelectionMode ();
+ ((IMonoDevelopEditorOperations)textEditor.EditorOperations).ToggleBlockSelectionMode ();
}
public static void IndentSelection (TextEditor editor)
{
- editor.EditorActionHost.IndentSelection ();
+ editor.EditorOperations.Indent ();
}
public static void UnIndentSelection (TextEditor editor)
{
- editor.EditorActionHost.UnIndentSelection ();
+ editor.EditorOperations.Unindent ();
}
#region SelectionActions
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
deleted file mode 100644
index 81fccd8303..0000000000
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/IEditorActionHost.cs
+++ /dev/null
@@ -1,125 +0,0 @@
-//
-// 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 ();
-
- void ShowQuickInfo ();
- }
-} \ No newline at end of file
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/ValidCodeAction.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/IMonoDevelopEditorOperations.cs
index 803cd429be..3819dc85ab 100644
--- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/ValidCodeAction.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/IMonoDevelopEditorOperations.cs
@@ -1,10 +1,10 @@
-//
-// ValidCodeAction.cs
+//
+// ITextEditor.cs
//
// Author:
// Mike Krüger <mkrueger@xamarin.com>
//
-// Copyright (c) 2015 Xamarin Inc. (http://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
@@ -23,25 +23,37 @@
// 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.CodeActions;
-using Microsoft.CodeAnalysis.Text;
-
-namespace MonoDevelop.CodeActions
+namespace MonoDevelop.Ide.Editor
{
- /// <summary>
- /// Represents a code action that's valid at a specific segment.
- /// </summary>
- class ValidCodeAction
+ interface IMonoDevelopEditorOperations : Microsoft.VisualStudio.Text.Operations.IEditorOperations
{
- public CodeAction CodeAction { get; private set; }
+ void SwitchCaretMode ();
+
+ void DeletePreviousSubword ();
+
+ void DeleteNextSubword ();
+
+ void StartCaretPulseAnimation ();
+
+ void JoinLines ();
+
+ void MoveToNextSubWord ();
+
+ void MoveToPrevSubWord ();
+
+ void Undo ();
+
+ void Redo ();
+
+ void MoveBlockUp ();
+
+ void MoveBlockDown ();
- public TextSpan ValidSegment { get; private set; }
+ void ToggleBlockSelectionMode ();
- public ValidCodeAction (CodeAction codeAction, TextSpan validSegment)
- {
- this.CodeAction = codeAction;
- this.ValidSegment = validSegment;
- }
+ void ShowQuickInfo ();
}
} \ 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
index e0d1a9d7b3..36eae1181f 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/ITextEditorImpl.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/ITextEditorImpl.cs
@@ -46,6 +46,8 @@ namespace MonoDevelop.Ide.Editor
interface ITextEditorImpl : IDisposable
{
+ Microsoft.VisualStudio.Text.Editor.ITextView TextView { get; set; }
+
ViewContent ViewContent { get; }
string ContentName { get; set; }
@@ -98,7 +100,7 @@ namespace MonoDevelop.Ide.Editor
void FixVirtualIndentation ();
- IEditorActionHost Actions { get; }
+ IMonoDevelopEditorOperations Actions { get; }
ITextMarkerFactory TextMarkerFactory { get; }
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditor.cs
index 3e67d61419..01df4804d9 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditor.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditor.cs
@@ -50,7 +50,7 @@ namespace MonoDevelop.Ide.Editor
public sealed class TextEditor : Control, ITextDocument, IDisposable
{
readonly ITextEditorImpl textEditorImpl;
- public Microsoft.VisualStudio.Text.Editor.ITextView TextView { get; }
+ public Microsoft.VisualStudio.Text.Editor.ITextView TextView { get => textEditorImpl.TextView; set => textEditorImpl.TextView = value; }
IReadonlyTextDocument ReadOnlyTextDocument { get { return textEditorImpl.Document; } }
@@ -970,6 +970,8 @@ namespace MonoDevelop.Ide.Editor
if (isDisposed)
return;
Runtime.AssertMainThread ();
+ this.TextView.Close ();
+
// Break fileTypeCondition circular event handling reference.
fileTypeCondition = null;
isDisposed = true;
@@ -980,8 +982,6 @@ namespace MonoDevelop.Ide.Editor
provider.Dispose ();
textEditorImpl.Dispose ();
- this.TextView.Close();
-
base.Dispose (disposing);
}
@@ -1002,7 +1002,7 @@ namespace MonoDevelop.Ide.Editor
}
}
- internal IEditorActionHost EditorActionHost {
+ internal Microsoft.VisualStudio.Text.Operations.IEditorOperations EditorOperations {
get {
return textEditorImpl.Actions;
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorDisplayBinding.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorDisplayBinding.cs
index ba49053020..f3f90d19b6 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorDisplayBinding.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorDisplayBinding.cs
@@ -97,18 +97,12 @@ namespace MonoDevelop.Ide.Editor
{
TextEditor editor;
- // HACK: this is a very poor test for whether to load the document. Maybe add an IsPlaceholder property to FilePath.
- // Another alternative would be to add a parameter to CreateContent.
- if (File.Exists(fileName))
- {
- editor = TextEditorFactory.CreateNewEditor(fileName, mimeType);
- }
- else
- {
- editor = TextEditorFactory.CreateNewEditor();
- editor.FileName = fileName;
- editor.MimeType = mimeType;
- }
+ // HACK: CreateNewEditor really needs to know whether the document exists (& should be loaded)
+ // or we're creating an empty document with the given file name & mime type.
+ //
+ // That information could be added to FilePath but fileName is converted to a string below
+ // which means the information is lost.
+ editor = TextEditorFactory.CreateNewEditor(fileName, mimeType);
editor.GetViewContent ().Project = ownerProject;
return editor.GetViewContent ();
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 3b2d881171..58f710ad14 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/FindInFilesDialog.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/FindInFilesDialog.cs
@@ -500,20 +500,7 @@ namespace MonoDevelop.Ide.FindInFiles
Visible = true,
Ready = true,
};
-
- var checkMenuItem = searchentryFileMask.AddFilterOption (0, GettextCatalog.GetString ("Include binary files"));
- checkMenuItem.DrawAsRadio = false;
- checkMenuItem.Active = properties.Get ("IncludeBinaryFiles", false);
- checkMenuItem.Toggled += delegate {
- properties.Set ("IncludeBinaryFiles", checkMenuItem.Active);
- };
-
- var checkMenuItem1 = searchentryFileMask.AddFilterOption (1, GettextCatalog.GetString ("Include hidden files and directories"));
- checkMenuItem1.DrawAsRadio = false;
- checkMenuItem1.Active = properties.Get ("IncludeHiddenFiles", false);
- checkMenuItem1.Toggled += delegate {
- properties.Set ("IncludeHiddenFiles", checkMenuItem1.Active);
- };
+
searchentryFileMask.Query = properties.Get ("MonoDevelop.FindReplaceDialogs.FileMask", "");
@@ -793,15 +780,12 @@ namespace MonoDevelop.Ide.FindInFiles
return null;
}
- scope = new DirectoryScope (comboboxentryPath.Entry.Text, checkbuttonRecursively.Active) {
- IncludeHiddenFiles = properties.Get ("IncludeHiddenFiles", false)
- };
+ scope = new DirectoryScope (comboboxentryPath.Entry.Text, checkbuttonRecursively.Active);
break;
default:
throw new ApplicationException ("Unknown scope:" + comboboxScope.Active);
}
- scope.IncludeBinaryFiles = properties.Get ("IncludeBinaryFiles", false);
return scope;
}
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 9da97c4c0b..19a08d442f 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/Scope.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/Scope.cs
@@ -39,6 +39,7 @@ namespace MonoDevelop.Ide.FindInFiles
{
public abstract class Scope
{
+ [Obsolete ("Unused - will be removed")]
public bool IncludeBinaryFiles {
get;
set;
@@ -145,7 +146,7 @@ namespace MonoDevelop.Ide.FindInFiles
() => new List<FileProvider> (),
(folder, loop, providers) => {
foreach (var file in folder.Files.Where (f => filterOptions.NameMatches (f.FileName) && File.Exists (f.FullPath))) {
- if (!IncludeBinaryFiles && !DesktopService.GetFileIsText (file.FullPath))
+ if (!DesktopService.GetFileIsText (file.FullPath))
continue;
lock (alreadyVisited) {
if (alreadyVisited.Contains (file.FullPath))
@@ -171,7 +172,7 @@ namespace MonoDevelop.Ide.FindInFiles
foreach (ProjectFile file in project.GetSourceFilesAsync (conf).Result.Where (f => filterOptions.NameMatches (f.Name) && File.Exists (f.Name))) {
if ((file.Flags & ProjectItemFlags.Hidden) == ProjectItemFlags.Hidden)
continue;
- if (!IncludeBinaryFiles && !DesktopService.GetFileIsText (file.FilePath))
+ if (!DesktopService.GetFileIsText (file.FilePath))
continue;
lock (alreadyVisited) {
@@ -227,7 +228,7 @@ namespace MonoDevelop.Ide.FindInFiles
foreach (ProjectFile file in project.GetSourceFilesAsync (conf).Result.Where (f => filterOptions.NameMatches (f.Name) && File.Exists (f.Name))) {
if ((file.Flags & ProjectItemFlags.Hidden) == ProjectItemFlags.Hidden)
continue;
- if (!IncludeBinaryFiles && !DesktopService.GetFileIsText (file.Name))
+ if (!DesktopService.GetFileIsText (file.Name))
continue;
if (alreadyVisited.Contains (file.FilePath.FullPath))
continue;
@@ -280,6 +281,7 @@ namespace MonoDevelop.Ide.FindInFiles
get { return PathMode.Absolute; }
}
+ [Obsolete ("Unused - will be removed")]
public bool IncludeHiddenFiles {
get;
set;
@@ -315,33 +317,29 @@ namespace MonoDevelop.Ide.FindInFiles
}
foreach (string fileName in Directory.EnumerateFiles (curPath, "*")) {
- if (!IncludeHiddenFiles) {
- if (Platform.IsWindows) {
- var attr = File.GetAttributes (fileName);
- if (attr.HasFlag (FileAttributes.Hidden))
- continue;
- }
- if (Path.GetFileName (fileName).StartsWith (".", StringComparison.Ordinal))
+ if (Platform.IsWindows) {
+ var attr = File.GetAttributes (fileName);
+ if (attr.HasFlag (FileAttributes.Hidden))
continue;
}
+ if (Path.GetFileName (fileName).StartsWith (".", StringComparison.Ordinal))
+ continue;
if (!filterOptions.NameMatches (fileName))
continue;
- if (!IncludeBinaryFiles && !DesktopService.GetFileIsText (fileName))
+ if (!DesktopService.GetFileIsText (fileName))
continue;
yield return fileName;
}
if (recurse) {
foreach (string directoryName in Directory.EnumerateDirectories (curPath)) {
- if (!IncludeHiddenFiles) {
- if (Platform.IsWindows) {
- var attr = File.GetAttributes (directoryName);
- if (attr.HasFlag (FileAttributes.Hidden))
- continue;
- }
- if (Path.GetFileName (directoryName).StartsWith (".", StringComparison.Ordinal))
+ if (Platform.IsWindows) {
+ var attr = File.GetAttributes (directoryName);
+ if (attr.HasFlag (FileAttributes.Hidden))
continue;
}
+ if (Path.GetFileName (directoryName).StartsWith (".", StringComparison.Ordinal))
+ continue;
directoryStack.Push (directoryName);
}
}
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 6abe1511a5..c7f81d654c 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Document.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Document.cs
@@ -862,7 +862,7 @@ namespace MonoDevelop.Ide.Gui
return Task.CompletedTask;
SubscribeRoslynWorkspace ();
analysisDocument = FileName != null ? TypeSystemService.GetDocumentId (this.Project, this.FileName) : null;
- if (analysisDocument != null) {
+ if (analysisDocument != null && !RoslynWorkspace.IsDocumentOpen(analysisDocument)) {
TypeSystemService.InformDocumentOpen (analysisDocument, Editor);
OnAnalysisDocumentChanged (EventArgs.Empty);
return Task.CompletedTask;
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 fd2177cd6f..864ca29f0e 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/ViewCommandHandlers.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/ViewCommandHandlers.cs
@@ -359,61 +359,61 @@ namespace MonoDevelop.Ide.Gui
[CommandHandler (TextEditorCommands.LineEnd)]
protected void OnLineEnd ()
{
- doc.Editor.EditorActionHost.MoveCaretToLineEnd ();
+ doc.Editor.EditorOperations.MoveToEndOfLine (false);
}
[CommandHandler (TextEditorCommands.LineStart)]
protected void OnLineStart ()
{
- doc.Editor.EditorActionHost.MoveCaretToLineStart ();
+ doc.Editor.EditorOperations.MoveToStartOfLine (false);
}
[CommandHandler (TextEditorCommands.DeleteLeftChar)]
protected void OnDeleteLeftChar ()
{
- doc.Editor.EditorActionHost.Backspace ();
+ doc.Editor.EditorOperations.Backspace ();
}
[CommandHandler (TextEditorCommands.DeleteRightChar)]
protected void OnDeleteRightChar ()
{
- doc.Editor.EditorActionHost.Delete ();
+ doc.Editor.EditorOperations.Delete ();
}
[CommandHandler (TextEditorCommands.CharLeft)]
protected void OnCharLeft ()
{
- doc.Editor.EditorActionHost.MoveCaretLeft ();
+ doc.Editor.EditorOperations.MoveToPreviousCharacter (false);
}
[CommandHandler (TextEditorCommands.CharRight)]
protected void OnCharRight ()
{
- doc.Editor.EditorActionHost.MoveCaretRight ();
+ doc.Editor.EditorOperations.MoveToNextCharacter (false);
}
[CommandHandler (TextEditorCommands.LineUp)]
protected void OnLineUp ()
{
- doc.Editor.EditorActionHost.MoveCaretUp ();
+ doc.Editor.EditorOperations.MoveLineUp (false);
}
[CommandHandler (TextEditorCommands.LineDown)]
protected void OnLineDown ()
{
- doc.Editor.EditorActionHost.MoveCaretDown ();
+ doc.Editor.EditorOperations.MoveLineDown (false);
}
[CommandHandler (TextEditorCommands.DocumentStart)]
protected void OnDocumentStart ()
{
- doc.Editor.EditorActionHost.MoveCaretToDocumentStart ();
+ doc.Editor.EditorOperations.MoveToStartOfDocument (false);
}
[CommandHandler (TextEditorCommands.DocumentEnd)]
protected void OnDocumentEnd ()
{
- doc.Editor.EditorActionHost.MoveCaretToDocumentEnd ();
+ doc.Editor.EditorOperations.MoveToEndOfDocument (false);
}
[CommandHandler (TextEditorCommands.DeleteLine)]
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/IMonoDevelopHostDocument.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/IMonoDevelopHostDocument.cs
new file mode 100644
index 0000000000..4c4871116b
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/IMonoDevelopHostDocument.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.VisualStudio.Text;
+
+namespace MonoDevelop.Ide.TypeSystem
+{
+ interface IMonoDevelopHostDocument
+ {
+ /// <summary>
+ /// Updates the text of the document.
+ /// </summary>
+ void UpdateText (SourceText newText);
+ }
+
+ static class MonoDevelopHostDocumentRegistration
+ {
+ internal static void Register (Document document, IMonoDevelopHostDocument hostDocument)
+ {
+ if (document.TryGetText (out SourceText sourceText)) {
+ ITextBuffer textBuffer = sourceText.Container.TryGetTextBuffer ();
+
+ textBuffer?.Properties.AddProperty (typeof (IMonoDevelopHostDocument), hostDocument);
+ }
+ }
+
+ internal static void UnRegister(Document document)
+ {
+ if (document.TryGetText (out SourceText sourceText)) {
+ ITextBuffer textBuffer = sourceText.Container.TryGetTextBuffer ();
+
+ textBuffer?.Properties.RemoveProperty (typeof (IMonoDevelopHostDocument));
+ }
+ }
+
+ internal static IMonoDevelopHostDocument FromDocument(Document document)
+ {
+ IMonoDevelopHostDocument containedDocument = null;
+ if (document.TryGetText (out SourceText sourceText)) {
+ ITextBuffer textBuffer = sourceText.Container.TryGetTextBuffer ();
+
+ containedDocument = textBuffer?.Properties.GetProperty<IMonoDevelopHostDocument> (typeof (IMonoDevelopHostDocument));
+ }
+
+ return containedDocument;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs
index 15c3947bbb..1a604658fc 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs
@@ -103,8 +103,8 @@ namespace MonoDevelop.Ide.TypeSystem
IdeApp.Workspace.ActiveConfigurationChanged += HandleActiveConfigurationChanged;
}
ISolutionCrawlerRegistrationService solutionCrawler = Services.GetService<ISolutionCrawlerRegistrationService> ();
- //Options = Options.WithChangedOption (Microsoft.CodeAnalysis.Diagnostics.InternalRuntimeDiagnosticOptions.Syntax, true)
- // .WithChangedOption (Microsoft.CodeAnalysis.Diagnostics.InternalRuntimeDiagnosticOptions.Semantic, true);
+ Options = Options.WithChangedOption (Microsoft.CodeAnalysis.Diagnostics.InternalRuntimeDiagnosticOptions.Syntax, true)
+ .WithChangedOption (Microsoft.CodeAnalysis.Diagnostics.InternalRuntimeDiagnosticOptions.Semantic, true);
if (IdeApp.Preferences.EnableSourceAnalysis) {
solutionCrawler.Register (this);
@@ -755,6 +755,16 @@ namespace MonoDevelop.Ide.TypeSystem
IdeApp.Workbench.OpenDocument (doc.FilePath, mdProject, activate);
}
}
+ }
+
+ internal void InternalOnDocumentOpened (DocumentId documentId, SourceTextContainer textContainer, bool isCurrentContext = true)
+ {
+ OnDocumentOpened (documentId, textContainer, isCurrentContext);
+ }
+
+ internal void InternalOnDocumentClosed (DocumentId documentId, TextLoader reloader, bool updateActiveContext = false)
+ {
+ OnDocumentClosed (documentId, reloader, updateActiveContext);
}
List<MonoDevelopSourceTextContainer> openDocuments = new List<MonoDevelopSourceTextContainer>();
@@ -822,6 +832,10 @@ namespace MonoDevelop.Ide.TypeSystem
if (openDoc != null) {
openDoc.Dispose ();
openDocuments.Remove (openDoc);
+ } else {
+ //Apparently something else opened this file via InternalOnDocumentOpened(e.g. .cshtml)
+ //it's job of whatever opened to also call InternalOnDocumentClosed
+ return;
}
}
if (!CurrentSolution.ContainsDocument (analysisDocument))
@@ -865,6 +879,13 @@ namespace MonoDevelop.Ide.TypeSystem
var document = GetDocument (id);
if (document == null)
return;
+
+ var hostDocument = MonoDevelopHostDocumentRegistration.FromDocument (document);
+ if (hostDocument != null) {
+ hostDocument.UpdateText (text);
+ return;
+ }
+
bool isOpen;
var filePath = document.FilePath;
Projection projection = null;
@@ -1204,6 +1225,11 @@ namespace MonoDevelop.Ide.TypeSystem
}
var path = DetermineFilePath (info.Id, info.Name, info.FilePath, info.Folders, mdProject?.FileName.ParentDirectory, true);
+ // If file is already part of project don't re-add it, example of this is .cshtml
+ if (mdProject?.IsFileInProject (path) == true) {
+ this.OnDocumentAdded (info);
+ return;
+ }
info = info.WithFilePath (path).WithTextLoader (new MonoDevelopTextLoader (path));
string formattedText;
@@ -1483,7 +1509,6 @@ namespace MonoDevelop.Ide.TypeSystem
originalOffset = offset;
return false;
}
-
}
// static class MonoDevelopWorkspaceFeatures
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
index ec571ac1af..7d8da6d18a 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService_WorkspaceHandling.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService_WorkspaceHandling.cs
@@ -41,7 +41,8 @@ namespace MonoDevelop.Ide.TypeSystem
{
public static partial class TypeSystemService
{
- static readonly MonoDevelopWorkspace emptyWorkspace;
+ //Internal for unit test
+ internal static readonly MonoDevelopWorkspace emptyWorkspace;
static object workspaceLock = new object();
static ImmutableList<MonoDevelopWorkspace> workspaces = ImmutableList<MonoDevelopWorkspace>.Empty;
@@ -116,42 +117,47 @@ namespace MonoDevelop.Ide.TypeSystem
internal static async Task<List<MonoDevelopWorkspace>> Load (WorkspaceItem item, ProgressMonitor progressMonitor, CancellationToken cancellationToken = default (CancellationToken), bool showStatusIcon = true)
{
using (Counters.ParserService.WorkspaceItemLoaded.BeginTiming ()) {
- var wsList = new List<MonoDevelopWorkspace> ();
+ var wsList = CreateWorkspaces (item).ToList();
//If we want BeginTiming to work correctly we need to `await`
- return await InternalLoad (wsList, item, progressMonitor, cancellationToken, showStatusIcon).ContinueWith (t => { t.Wait (); return wsList; });
+ await InternalLoad (wsList, progressMonitor, cancellationToken, showStatusIcon).ConfigureAwait (false);
+ return wsList.ToList ();
}
}
- static Task InternalLoad (List<MonoDevelopWorkspace> list, MonoDevelop.Projects.WorkspaceItem item, ProgressMonitor progressMonitor, CancellationToken cancellationToken = default(CancellationToken), bool showStatusIcon = true)
+ static IEnumerable<MonoDevelopWorkspace> CreateWorkspaces (WorkspaceItem item)
{
- return Task.Run (async () => {
- var ws = item as MonoDevelop.Projects.Workspace;
- if (ws != null) {
- foreach (var it in ws.Items) {
- await InternalLoad (list, it, progressMonitor, cancellationToken).ConfigureAwait (false);
- }
- ws.ItemAdded += OnWorkspaceItemAdded;
- ws.ItemRemoved += OnWorkspaceItemRemoved;
- } else {
- var solution = item as MonoDevelop.Projects.Solution;
- if (solution != null) {
- var workspace = new MonoDevelopWorkspace (solution);
- lock (workspaceLock)
- workspaces = workspaces.Add (workspace);
- list.Add (workspace);
- if (showStatusIcon)
- workspace.ShowStatusIcon ();
- await workspace.TryLoadSolution (cancellationToken).ConfigureAwait (false);
- solution.SolutionItemAdded += OnSolutionItemAdded;
- solution.SolutionItemRemoved += OnSolutionItemRemoved;
- TaskCompletionSource<MonoDevelopWorkspace> request;
- if (workspaceRequests.TryGetValue (solution, out request))
- request.TrySetResult (workspace);
- if (showStatusIcon)
- workspace.HideStatusIcon ();
+ if (item is MonoDevelop.Projects.Workspace ws) {
+ foreach (var wsItem in ws.Items) {
+ foreach (var mdWorkspace in CreateWorkspaces (wsItem)) {
+ yield return mdWorkspace;
}
}
- });
+ ws.ItemAdded += OnWorkspaceItemAdded;
+ ws.ItemRemoved += OnWorkspaceItemRemoved;
+ } else if (item is MonoDevelop.Projects.Solution solution) {
+ var workspace = new MonoDevelopWorkspace (solution);
+ lock (workspaceLock)
+ workspaces = workspaces.Add (workspace);
+ solution.SolutionItemAdded += OnSolutionItemAdded;
+ solution.SolutionItemRemoved += OnSolutionItemRemoved;
+ yield return workspace;
+ }
+ }
+
+ static async Task InternalLoad (List<MonoDevelopWorkspace> mdWorkspaces, ProgressMonitor progressMonitor, CancellationToken cancellationToken = default (CancellationToken), bool showStatusIcon = true)
+ {
+ foreach (var workspace in mdWorkspaces) {
+ if (showStatusIcon)
+ workspace.ShowStatusIcon ();
+
+ await workspace.TryLoadSolution (cancellationToken).ConfigureAwait (false);
+ TaskCompletionSource<MonoDevelopWorkspace> request;
+ if (workspaceRequests.TryGetValue (workspace.MonoDevelopSolution, out request))
+ request.TrySetResult (workspace);
+ if (showStatusIcon)
+ workspace.HideStatusIcon ();
+
+ }
}
internal static void Unload (MonoDevelop.Projects.WorkspaceItem item)
@@ -256,7 +262,29 @@ namespace MonoDevelop.Ide.TypeSystem
var parentSolution = project.ParentSolution;
var workspace = await GetWorkspaceAsync (parentSolution, cancellationToken);
var projectId = workspace.GetProjectId (project);
- return projectId == null ? null : workspace.CurrentSolution.GetProject (projectId);
+ if (projectId == null)
+ throw new Exception ("Project not part of workspace");
+ var proj = workspace.CurrentSolution.GetProject (projectId);
+ if (proj != null)
+ return proj;
+ //We assume that since we have projectId and project is not found in solution
+ //project is being loaded(waiting MSBuild to return list of source files)
+ var taskSource = new TaskCompletionSource<Microsoft.CodeAnalysis.Project> ();
+ EventHandler<WorkspaceChangeEventArgs> del = (s, e) => {
+ if (e.Kind == WorkspaceChangeKind.SolutionAdded || e.Kind == WorkspaceChangeKind.SolutionReloaded) {
+ proj = workspace.CurrentSolution.GetProject (projectId);
+ if (proj != null)
+ taskSource.SetResult (proj);
+ }
+ };
+ cancellationToken.Register (taskSource.SetCanceled);
+ workspace.WorkspaceChanged += del;
+ try {
+ proj = await taskSource.Task;
+ } finally {
+ workspace.WorkspaceChanged -= del;
+ }
+ return proj;
}
public static Task<Compilation> GetCompilationAsync (MonoDevelop.Projects.Project project, CancellationToken cancellationToken = default(CancellationToken))
@@ -277,7 +305,7 @@ namespace MonoDevelop.Ide.TypeSystem
static void OnWorkspaceItemAdded (object s, MonoDevelop.Projects.WorkspaceItemEventArgs args)
{
- Task.Run (() => TypeSystemService.Load (args.Item, null));
+ TypeSystemService.Load (args.Item, null).Ignore ();
}
static void OnWorkspaceItemRemoved (object s, MonoDevelop.Projects.WorkspaceItemEventArgs args)
@@ -285,12 +313,12 @@ namespace MonoDevelop.Ide.TypeSystem
Unload (args.Item);
}
- static async void OnSolutionItemAdded (object sender, MonoDevelop.Projects.SolutionItemChangeEventArgs args)
+ static void OnSolutionItemAdded (object sender, MonoDevelop.Projects.SolutionItemChangeEventArgs args)
{
var project = args.SolutionItem as MonoDevelop.Projects.Project;
if (project != null) {
Unload (project.ParentSolution);
- await Load (project.ParentSolution, new ProgressMonitor());
+ Load (project.ParentSolution, null).Ignore ();
}
}
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/ValidCodeDiagnosticAction.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/WorkspaceExtensions.cs
index 5c946ac714..580af7969a 100644
--- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/ValidCodeDiagnosticAction.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/WorkspaceExtensions.cs
@@ -1,10 +1,10 @@
//
-// ValidCodeDiagnosticAction.cs
+// WorkspaceExtensions.cs
//
// Author:
-// Mike Krüger <mkrueger@xamarin.com>
+// Kirill Osenkov <https://github.com/KirillOsenkov>
//
-// Copyright (c) 2015 Xamarin Inc. (http://xamarin.com)
+// Copyright (c) 2018 Microsoft
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -24,39 +24,23 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-using System;
-using Microsoft.CodeAnalysis.CodeActions;
-using MonoDevelop.Core.Text;
-using MonoDevelop.CodeActions;
using Microsoft.CodeAnalysis;
-using MonoDevelop.CodeIssues;
-using Microsoft.CodeAnalysis.Text;
-using System.Collections.Immutable;
+using Microsoft.CodeAnalysis.SolutionCrawler;
-namespace MonoDevelop.CodeActions
+namespace MonoDevelop.Ide.TypeSystem
{
- /// <summary>
- /// Represents a code action that's valid at a specific segment that was created as a action for a specific code diagnostic.
- /// </summary>
- class ValidCodeDiagnosticAction : ValidCodeAction
+ static class WorkspaceExtensions
{
- ImmutableArray<Diagnostic> validDiagnostics;
-
- public CodeDiagnosticFixDescriptor Diagnostic {
- get;
- private set;
- }
-
- public ImmutableArray<Diagnostic> ValidDiagnostics {
- get {
- return validDiagnostics;
- }
+ internal static void RegisterSolutionCrawler (Workspace workspace)
+ {
+ var solutionCrawlerRegistrationService = workspace.Services.GetService<ISolutionCrawlerRegistrationService> ();
+ solutionCrawlerRegistrationService.Register (workspace);
}
- public ValidCodeDiagnosticAction (CodeDiagnosticFixDescriptor diagnostic, CodeAction codeAction, ImmutableArray<Diagnostic> validDiagnostics, TextSpan validSegment) : base (codeAction, validSegment)
+ internal static void UnregisterSolutionCrawler (Workspace workspace)
{
- this.Diagnostic = diagnostic;
- this.validDiagnostics = validDiagnostics;
+ var solutionCrawlerRegistrationService = workspace.Services.GetService<ISolutionCrawlerRegistrationService> ();
+ solutionCrawlerRegistrationService.Unregister (workspace);
}
}
} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj
index f73ac34f27..704f980a31 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj
@@ -8562,9 +8562,13 @@
<Compile Include="MonoDevelop.Ide.Commands\ViewCommands.cs" />
<Compile Include="MonoDevelop.Ide.Commands\WindowCommands.cs" />
<Compile Include="MonoDevelop.Ide.Composition\CompositionManager.cs" />
+ <Compile Include="MonoDevelop.Ide.Composition\InlineRenameService.cs" />
<Compile Include="MonoDevelop.Ide.Composition\JoinableTaskContextHost.cs" />
<Compile Include="MonoDevelop.Ide.Composition\PlatformCatalog.cs" />
<Compile Include="MonoDevelop.Ide.Composition\PlatformExtensions.cs" />
+ <Compile Include="MonoDevelop.Ide.Composition\PreviewFactoryService.cs" />
+ <Compile Include="MonoDevelop.Ide.Composition\RoslynWaitIndicator.cs" />
+ <Compile Include="MonoDevelop.Ide.Composition\SuggestedActionCategoryRegistryService.cs" />
<Compile Include="MonoDevelop.Ide.Editor\ITextEditorFactoryService.cs" />
<Compile Include="MonoDevelop.Ide.Gui\DisplayBindingService.cs" />
<Compile Include="MonoDevelop.Ide.Gui\BackgroundProgressMonitor.cs" />
@@ -8643,6 +8647,7 @@
<Compile Include="MonoDevelop.Ide.Gui\Workbench.cs" />
<Compile Include="MonoDevelop.Ide.Gui\StartupInfo.cs" />
<Compile Include="MonoDevelop.Ide.Gui\ProgressMonitors.cs" />
+ <Compile Include="MonoDevelop.Ide.TypeSystem\IMonoDevelopHostDocument.cs" />
<Compile Include="MonoDevelop.Ide.TypeSystem\MonoDevelopPersistentStorageLocationService.cs" />
<Compile Include="MonoDevelop.Ide\RoslynLogger.cs" />
<Compile Include="MonoDevelop.Ide\Services.cs" />
@@ -9274,6 +9279,7 @@
<Compile Include="MonoDevelop.Components\ImageLoader.cs" />
<Compile Include="MonoDevelop.Ide.CodeCompletion\ParameterHintingData.cs" />
<Compile Include="MonoDevelop.Ide.TypeSystem\MonoDevelopWorkspace.cs" />
+ <Compile Include="MonoDevelop.Ide.TypeSystem\WorkspaceExtensions.cs" />
<Compile Include="MonoDevelop.Ide.Editor\IDocumentLine.cs" />
<Compile Include="MonoDevelop.Ide.CodeTemplates\IListDataProvider.cs" />
<Compile Include="MonoDevelop.Ide.Editor\ITextEditorOptions.cs" />
@@ -9343,7 +9349,7 @@
<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\InternalExtensionAPI\IMonoDevelopEditorOperations.cs" />
<Compile Include="MonoDevelop.Ide.Editor\EditActions.cs" />
<Compile Include="MonoDevelop.Ide.Editor\DocumentContext.cs" />
<Compile Include="MonoDevelop.Ide.Editor\InternalExtensionAPI\ITextEditorFactory.cs" />
@@ -9728,6 +9734,7 @@
<InternalsVisibleTo Include="MonoDevelop.Refactoring.Tests" />
<InternalsVisibleTo Include="Xamarin.Forms.Addin" />
<InternalsVisibleTo Include="MonoDevelop.TextEditor.Tests" />
+ <InternalsVisibleTo Include="WebToolingAddin" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Target Name="AfterBuild">