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

github.com/mono/monodevelop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules3
-rw-r--r--main/Main.sln50
m---------main/external/NRefactory60
m---------main/external/RefactoringEssentials0
-rw-r--r--main/src/addins/CSharpBinding/CSharpBinding.csproj219
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/AddImport/AbstractAddImportCodeFixProvider.cs2
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/Async/CSharpAddAsyncCodeFixProvider.cs2
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/Async/CSharpConvertToAsyncMethodCodeFixProvider.cs2
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/FullyQualify/CSharpFullyQualifyCodeFixProvider.cs2
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/ImplementAbstractClass/ImplementAbstractClassCodeFixProvider.cs2
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/MoveTypeToFile/MoveTypeToFile.cs2
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/RemoveUnnecessaryCast/RemoveUnnecessaryCastCodeFixProvider.cs2
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/RemoveUnnecessaryUsings/RemoveUnnecessaryUsingsCodeFixProvider.cs2
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/SimplifyTypeNames/SimplifyTypeNamesCodeFixProvider.cs2
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeRefactorings/ExtractMethod/ExtractMethodCodeRefactoringProvider.cs2
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs2
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CSharpCompletionTextEditorExtension.cs10
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/ProtocolMemberContextHandler.cs4
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/RoslynCodeCompletionFactory.cs18
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/RoslynCompletionData.cs14
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Diagnostics/InconsistentNaming/NameConventionRule.cs1
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/CompletionContext.cs103
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/CompletionEngine.cs204
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/CompletionResult.cs121
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/CompletionTriggerInfo.cs95
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/CompletionTriggerReason.cs37
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/AttributeNamedParameterContextHandler.cs232
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/CastCompletionContextHandler.cs108
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/CompletionContextHandler.cs187
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/DelegateCreationContextHandler.cs267
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/EnumMemberContextHandler.cs117
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/ExplicitInterfaceContextHandler.cs153
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/ExternAliasContextHandler.cs72
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/FormatItemContextHandler.cs256
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/KeywordContextHandler.cs244
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/NamedParameterContextHandler.cs218
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/ObjectCreationContextHandler.cs187
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/ObjectInitializerContextHandler.cs250
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/OverrideContextHandler.cs372
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/PartialContextHandler.cs178
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/PreProcessorExpressionContextHandler.cs59
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/RoslynRecommendationsCompletionContextHandler.cs178
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/SenderCompletionContextHandler.cs122
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/SnippetContextHandler.cs104
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/SpeculativeNameContextHandler.cs125
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/SpeculativeTContextHandler.cs103
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/XmlDocCommentContextHandler.cs399
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/DisplayFlags.cs42
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/EditorBrowsableBehavior.cs35
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ICompletionCategory.cs37
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ICompletionDataFactory.cs77
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ICompletionKeyHandler.cs56
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AbstractKeywordRecommender.cs55
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AbstractSyntacticSingleKeywordRecommender.cs79
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AddKeywordRecommender.cs22
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AliasKeywordRecommender.cs36
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AsKeywordRecommender.cs21
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AscendingKeywordRecommender.cs21
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AssemblyKeywordRecommender.cs32
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AsyncKeywordRecommender.cs39
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AwaitKeywordRecommender.cs54
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/BaseKeywordRecommender.cs69
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/BoolKeywordRecommender.cs47
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/BreakKeywordRecommender.cs54
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ByKeywordRecommender.cs53
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ByteKeywordRecommender.cs49
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/CaseKeywordRecommender.cs41
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/CatchKeywordRecommender.cs21
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/CharKeywordRecommender.cs47
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/CheckedKeywordRecommender.cs24
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ChecksumKeywordRecommender.cs28
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ClassKeywordRecommender.cs44
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ConstKeywordRecommender.cs65
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ContinueKeywordRecommender.cs47
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DecimalKeywordRecommender.cs47
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DefaultKeywordRecommender.cs42
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DefineKeywordRecommender.cs25
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DelegateKeywordRecommender.cs53
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DescendingKeywordRecommender.cs21
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DisableKeywordRecommender.cs30
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DoKeywordRecommender.cs23
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DoubleKeywordRecommender.cs47
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DynamicKeywordRecommender.cs64
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ElifKeywordRecommender.cs21
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ElseKeywordRecommender.cs63
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/EndIfKeywordRecommender.cs21
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/EndRegionKeywordRecommender.cs21
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/EnumKeywordRecommender.cs37
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/EqualsKeywordRecommender.cs45
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ErrorKeywordRecommender.cs21
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/EventKeywordRecommender.cs44
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ExplicitKeywordRecommender.cs41
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ExternKeywordRecommender.cs100
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/FalseKeywordRecommender.cs26
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/FieldKeywordRecommender.cs30
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/FinallyKeywordRecommender.cs22
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/FixedKeywordRecommender.cs50
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/FloatKeywordRecommender.cs47
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ForEachKeywordRecommender.cs23
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ForKeywordRecommender.cs23
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/FromKeywordRecommender.cs24
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/GetKeywordRecommender.cs24
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/GlobalKeywordRecommender.cs42
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/GotoKeywordRecommender.cs23
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/GroupKeywordRecommender.cs32
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/HiddenKeywordRecommender.cs32
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/IfKeywordRecommender.cs24
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ImplicitKeywordRecommender.cs41
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/InKeywordRecommender.cs112
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/IntKeywordRecommender.cs48
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/InterfaceKeywordRecommender.cs38
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/InternalKeywordRecommender.cs69
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/IntoKeywordRecommender.cs117
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/IsKeywordRecommender.cs23
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/JoinKeywordRecommender.cs21
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/LetKeywordRecommender.cs32
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/LineKeywordRecommender.cs21
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/LockKeywordRecommender.cs23
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/LongKeywordRecommender.cs48
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/MethodKeywordRecommender.cs43
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ModuleKeywordRecommender.cs32
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/NameOfKeywordRecommender.cs34
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/NamespaceKeywordRecommender.cs160
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/NewKeywordRecommender.cs123
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/NullKeywordRecommender.cs52
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ObjectKeywordRecommender.cs46
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/OnKeywordRecommender.cs49
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/OperatorKeywordRecommender.cs36
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/OrderByKeywordRecommender.cs32
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/OutKeywordRecommender.cs30
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/OverrideKeywordRecommender.cs38
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ParamKeywordRecommender.cs37
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ParamsKeywordRecommender.cs21
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/PartialKeywordRecommender.cs64
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/PragmaKeywordRecommender.cs21
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/PrivateKeywordRecommender.cs87
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/PropertyKeywordRecommender.cs22
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ProtectedKeywordRecommender.cs74
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/PublicKeywordRecommender.cs60
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ReadOnlyKeywordRecommender.cs40
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/RefKeywordRecommender.cs27
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ReferenceKeywordRecommender.cs26
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/RegionKeywordRecommender.cs21
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/RemoveKeywordRecommender.cs22
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/RestoreKeywordRecommender.cs30
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ReturnKeywordRecommender.cs33
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/SByteKeywordRecommender.cs48
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/SealedKeywordRecommender.cs55
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/SelectKeywordRecommender.cs38
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/SetKeywordRecommender.cs24
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ShortKeywordRecommender.cs48
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/SizeOfKeywordRecommender.cs24
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/StackAllocKeywordRecommender.cs42
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/StaticKeywordRecommender.cs83
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/StringKeywordRecommender.cs46
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/StructKeywordRecommender.cs40
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/SwitchKeywordRecommender.cs23
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ThisKeywordRecommender.cs97
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ThrowKeywordRecommender.cs23
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/TrueKeywordRecommender.cs26
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/TryKeywordRecommender.cs23
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/TypeKeywordRecommender.cs21
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/TypeOfKeywordRecommender.cs34
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/TypeVarKeywordRecommender.cs51
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/UIntKeywordRecommender.cs48
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ULongKeywordRecommender.cs48
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/UShortKeywordRecommender.cs48
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/UncheckedKeywordRecommender.cs24
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/UndefKeywordRecommender.cs25
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/UnsafeKeywordRecommender.cs74
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/UsingKeywordRecommender.cs139
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/VarKeywordRecommender.cs35
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/VirtualKeywordRecommender.cs37
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/VoidKeywordRecommender.cs112
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/VolatileKeywordRecommender.cs40
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/WarningKeywordRecommender.cs34
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/WhenKeywordRecommender.cs21
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/WhereKeywordRecommender.cs142
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/WhileKeywordRecommender.cs50
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/YieldKeywordRecommender.cs21
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/AbstractExtractMethodService.cs45
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/AbstractSyntaxTriviaService.Result.cs296
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/AbstractSyntaxTriviaService.cs131
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpExtractMethodService.cs27
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.Analyzer.cs136
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.CSharpCodeGenerator.CallSiteContainerRewriter.cs392
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.CSharpCodeGenerator.ExpressionCodeGenerator.cs242
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.CSharpCodeGenerator.MultipleStatementsCodeGenerator.cs139
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.CSharpCodeGenerator.SingleStatementCodeGenerator.cs87
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.CSharpCodeGenerator.cs583
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.FormattingProvider.cs66
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.PostProcessor.cs314
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.TriviaResult.cs161
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.cs128
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSelectionResult.ExpressionResult.cs140
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSelectionResult.StatementResult.cs88
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSelectionResult.cs207
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSelectionValidator.Validator.cs86
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSelectionValidator.cs485
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSyntaxTriviaService.cs16
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSyntaxTriviaServiceFactory.cs20
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/Extensions.cs285
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/Enums.cs55
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/Extensions.cs121
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ExtractMethodMatrix.cs229
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ExtractMethodOptions.cs17
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ExtractMethodResult.cs66
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ExtractMethodService.cs21
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/FailedExtractMethodResult.cs13
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/IExtractMethodService.cs16
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ISyntaxTriviaService.cs66
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/InsertionPoint.cs64
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.Analyzer.SymbolMapBuilder.cs79
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.Analyzer.cs957
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.AnalyzerResult.cs176
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.CodeGenerator.cs316
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.GeneratedCode.cs39
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.TriviaResult.cs181
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.TypeParameterCollector.cs57
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.VariableInfo.cs138
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.VariableSymbol.cs357
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.cs171
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/OperationStatus.cs68
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/OperationStatus_Statics.cs23
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/OperationStatus`1.cs29
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ParameterStyle.cs41
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ReturnStyle.cs23
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/SelectionResult.cs158
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/SelectionValidator.NullSelectionResult.cs52
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/SelectionValidator.cs185
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/SimpleExtractMethodResult.cs17
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/UniqueNameGenerator.cs28
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/VariableStyle.cs49
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Formatter/CSharpEditorFormattingService.cs292
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Formatter/CommonFormattingHelpers.cs372
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Formatter/FormattingHelpers.cs552
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Formatter/FormattingOptionsFactory.cs170
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Formatter/FormattingRangeHelper.cs434
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Formatter/Indent.cs249
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/FrameworkLookup/FrameworkLookup.cs490
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateFromMembers/AbstractCodeRefactoringResult.cs31
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateFromMembers/AbstractGenerateFromMembersService.cs160
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateFromMembers/GenerateConstructor/AbstractGenerateConstructorService.cs267
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateFromMembers/GenerateConstructor/CSharpGenerateConstructorService.cs53
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateFromMembers/GenerateConstructor/GenerateConstructorResult.cs17
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateFromMembers/GenerateFromMembersHelpers.cs36
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/AbstractCodeRefactoringResult.cs31
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/AbstractGenerateMemberService.cs138
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateConstructor/AbstractGenerateConstructorService.cs703
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateConstructor/CSharpGenerateConstructorService.cs278
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateConstructor/GenerateConstructorHelpers.cs35
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateDefaultConstructors/AbstractGenerateDefaultConstructorsService.cs242
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateDefaultConstructors/CSharpGenerateDefaultConstructorsService.cs49
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateDefaultConstructors/GenerateDefaultConstructorsResult.cs17
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateEnumMember/AbstractGenerateEnumMemberService.cs241
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateEnumMember/CSharpGenerateEnumMemberService.cs54
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/AbstractGenerateConversionService.cs135
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/AbstractGenerateMethodService.cs266
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.cs579
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/CSharpCommonGenerationServiceMethods.cs36
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/CSharpGenerateConversionService.cs233
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/CSharpGenerateMethodService.cs173
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/CSharpGenerateParameterizedMemberService.cs172
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/MethodGenerationKind.cs11
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateVariable/AbstractGenerateVariableService.cs696
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateVariable/CSharpGenerateVariableService.cs167
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateType/AbstractGenerateTypeService.cs1812
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateType/CSharpGenerateTypeService.cs940
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateType/GenerateTypeDialogOptions.cs27
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateType/GenerateTypeOptionsResult.cs55
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateType/TypeKindOptions.cs89
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GotoDefinition/GotoDefinitionHelpers.cs130
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GotoDefinition/GotoDefinitionService.cs114
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementAbstractClass/AbstractImplementAbstractClassService.Editor.cs166
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementAbstractClass/AbstractImplementAbstractClassService.State.cs70
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementAbstractClass/AbstractImplementAbstractClassService.cs35
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementAbstractClass/CSharpImplementAbstractClassService.cs46
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs559
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.CodeAction_Conflicts.cs89
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.CodeAction_Method.cs80
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.CodeAction_Property.cs150
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.DisposePatternCodeAction.cs150
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.State.cs84
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.cs109
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/CSharpImplementInterfaceService.cs170
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/CSharpIndentEngine.cs537
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/CacheIndentEngine.cs623
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/IDocumentIndentEngine.cs99
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/IStateMachineIndentEngine.cs60
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/ITextPasteHandler.cs52
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/IndentState.cs2018
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/NullIStateMachineIndentEngine.cs207
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/TextPasteIndentEngine.cs677
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.AbstractIntroduceVariableCodeAction.cs134
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.CodeAction.cs28
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.IntroduceVariableAllOccurrenceCodeAction.cs39
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State.cs271
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State_Attribute.cs25
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State_Block.cs38
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State_ConstructorInitializer.cs43
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State_Field.cs50
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State_Parameter.cs36
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State_Query.cs37
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.cs329
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/CSharpIntroduceVariableService.Rewriter.cs61
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/CSharpIntroduceVariableService.cs131
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/CSharpIntroduceVariableService_IntroduceField.cs198
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/CSharpIntroduceVariableService_IntroduceLocal.cs378
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/CSharpIntroduceVariableService_IntroduceQueryLocal.cs107
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/IntroduceVariableResult.cs33
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/OrganizeImports/CSharpOrganizeImportsService.Rewriter.cs87
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/OrganizeImports/CSharpOrganizeImportsService.cs56
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ParameterHinting/IParameterHintingData.cs229
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ParameterHinting/IParameterHintingDataFactory.cs47
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ParameterHinting/ParameterHintingEngine.cs255
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ParameterHinting/ParameterHintingResult.cs87
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ParameterHinting/ParameterUtil.cs215
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/RemoveUnnecessaryImports/CSharpRemoveUnnecessaryImportsService.Rewriter.cs168
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/RemoveUnnecessaryImports/CSharpRemoveUnnecessaryImportsService.cs115
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/SemanticHighlighting/SemanticHighlightingVisitor.cs539
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValueTreeView.cs39
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionEditorExtension.cs2
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeRefactoringService.cs1
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/AnalyzersFromAssembly.cs10
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/DiagnosticResult.cs2
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.csproj4
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionData.cs7
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ListWidget.cs1
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ListWindow.cs1
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ParameterHintingData.cs1
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/ExpansionObject.cs1
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj8
332 files changed, 38817 insertions, 121 deletions
diff --git a/.gitmodules b/.gitmodules
index cb047cfe6f..395dab8a8b 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -56,3 +56,6 @@
path = main/external/libgit2
url = git://github.com/mono/libgit2.git
branch = xs-5.10-v1
+[submodule "main/external/RefactoringEssentials"]
+ path = main/external/RefactoringEssentials
+ url = git://github.com/icsharpcode/RefactoringEssentials.git
diff --git a/main/Main.sln b/main/Main.sln
index 6798841da1..41323b2b80 100644
--- a/main/Main.sln
+++ b/main/Main.sln
@@ -265,8 +265,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.NRefactory6.CSh
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ICSharpCode.NRefactory6.Shared", "external\NRefactory6\ICSharpCode.NRefactory6.Shared\ICSharpCode.NRefactory6.Shared.shproj", "{5AD3093F-F79D-4014-B197-27D09492C3E3}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NR6Pack", "external\NRefactory6\NR6Pack\NR6Pack.csproj", "{C465A5DC-AD28-49A2-89C0-F81838814A7E}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GuiUnit_NET_4_5", "external\guiunit\src\framework\GuiUnit_NET_4_5.csproj", "{D12F0F7B-8DE3-43EC-BA49-41052D065A9B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibGit2Sharp", "external\libgit2sharp\LibGit2Sharp\LibGit2Sharp.csproj", "{EE6ED99F-CB12-4683-B055-D28FC7357A34}"
@@ -302,6 +300,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ide.Tests", "tests\Ide.Test
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsPlatform.Tests", "tests\WindowsPlatform.Tests\WindowsPlatform.Tests.csproj", "{865100E2-A29C-4FCD-B803-1A0B9A0A6EF7}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RefactoringEssentials", "external\RefactoringEssentials\RefactoringEssentials\RefactoringEssentials.csproj", "{C465A5DC-AD28-49A2-89C0-F81838814A7E}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -1506,26 +1506,6 @@ Global
{C3887A93-B2BD-4097-8E2F-3A063EFF32FD}.ReleaseMac|Any CPU.Build.0 = Release|Any CPU
{C3887A93-B2BD-4097-8E2F-3A063EFF32FD}.ReleaseWin32|Any CPU.ActiveCfg = Release|Any CPU
{C3887A93-B2BD-4097-8E2F-3A063EFF32FD}.ReleaseWin32|Any CPU.Build.0 = Release|Any CPU
- {C465A5DC-AD28-49A2-89C0-F81838814A7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C465A5DC-AD28-49A2-89C0-F81838814A7E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C465A5DC-AD28-49A2-89C0-F81838814A7E}.Debug|x86.ActiveCfg = Debug|Any CPU
- {C465A5DC-AD28-49A2-89C0-F81838814A7E}.Debug|x86.Build.0 = Debug|Any CPU
- {C465A5DC-AD28-49A2-89C0-F81838814A7E}.DebugGnome|Any CPU.ActiveCfg = Debug|Any CPU
- {C465A5DC-AD28-49A2-89C0-F81838814A7E}.DebugGnome|Any CPU.Build.0 = Debug|Any CPU
- {C465A5DC-AD28-49A2-89C0-F81838814A7E}.DebugMac|Any CPU.ActiveCfg = Debug|Any CPU
- {C465A5DC-AD28-49A2-89C0-F81838814A7E}.DebugMac|Any CPU.Build.0 = Debug|Any CPU
- {C465A5DC-AD28-49A2-89C0-F81838814A7E}.DebugWin32|Any CPU.ActiveCfg = Debug|Any CPU
- {C465A5DC-AD28-49A2-89C0-F81838814A7E}.DebugWin32|Any CPU.Build.0 = Debug|Any CPU
- {C465A5DC-AD28-49A2-89C0-F81838814A7E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C465A5DC-AD28-49A2-89C0-F81838814A7E}.Release|Any CPU.Build.0 = Release|Any CPU
- {C465A5DC-AD28-49A2-89C0-F81838814A7E}.Release|x86.ActiveCfg = Release|Any CPU
- {C465A5DC-AD28-49A2-89C0-F81838814A7E}.Release|x86.Build.0 = Release|Any CPU
- {C465A5DC-AD28-49A2-89C0-F81838814A7E}.ReleaseGnome|Any CPU.ActiveCfg = Release|Any CPU
- {C465A5DC-AD28-49A2-89C0-F81838814A7E}.ReleaseGnome|Any CPU.Build.0 = Release|Any CPU
- {C465A5DC-AD28-49A2-89C0-F81838814A7E}.ReleaseMac|Any CPU.ActiveCfg = Release|Any CPU
- {C465A5DC-AD28-49A2-89C0-F81838814A7E}.ReleaseMac|Any CPU.Build.0 = Release|Any CPU
- {C465A5DC-AD28-49A2-89C0-F81838814A7E}.ReleaseWin32|Any CPU.ActiveCfg = Release|Any CPU
- {C465A5DC-AD28-49A2-89C0-F81838814A7E}.ReleaseWin32|Any CPU.Build.0 = Release|Any CPU
{C4B0275C-37D3-43F2-927D-ABF556600804}.Debug|Any CPU.ActiveCfg = Debug|x86
{C4B0275C-37D3-43F2-927D-ABF556600804}.DebugGnome|Any CPU.ActiveCfg = Debug|x86
{C4B0275C-37D3-43F2-927D-ABF556600804}.DebugMac|Any CPU.ActiveCfg = Debug|x86
@@ -1919,6 +1899,26 @@ Global
{FEC19BDA-4904-4005-8C09-68E82E8BEF6A}.ReleaseMac|Any CPU.Build.0 = Release|Any CPU
{FEC19BDA-4904-4005-8C09-68E82E8BEF6A}.ReleaseWin32|Any CPU.ActiveCfg = Release|Any CPU
{FEC19BDA-4904-4005-8C09-68E82E8BEF6A}.ReleaseWin32|Any CPU.Build.0 = Release|Any CPU
+ {C465A5DC-AD28-49A2-89C0-F81838814A7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C465A5DC-AD28-49A2-89C0-F81838814A7E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C465A5DC-AD28-49A2-89C0-F81838814A7E}.DebugMac|Any CPU.ActiveCfg = Debug|Any CPU
+ {C465A5DC-AD28-49A2-89C0-F81838814A7E}.DebugMac|Any CPU.Build.0 = Debug|Any CPU
+ {C465A5DC-AD28-49A2-89C0-F81838814A7E}.DebugWin32|Any CPU.ActiveCfg = Debug|Any CPU
+ {C465A5DC-AD28-49A2-89C0-F81838814A7E}.DebugWin32|Any CPU.Build.0 = Debug|Any CPU
+ {C465A5DC-AD28-49A2-89C0-F81838814A7E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C465A5DC-AD28-49A2-89C0-F81838814A7E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C465A5DC-AD28-49A2-89C0-F81838814A7E}.DebugGnome|Any CPU.ActiveCfg = Debug|Any CPU
+ {C465A5DC-AD28-49A2-89C0-F81838814A7E}.DebugGnome|Any CPU.Build.0 = Debug|Any CPU
+ {C465A5DC-AD28-49A2-89C0-F81838814A7E}.ReleaseMac|Any CPU.ActiveCfg = Release|Any CPU
+ {C465A5DC-AD28-49A2-89C0-F81838814A7E}.ReleaseMac|Any CPU.Build.0 = Release|Any CPU
+ {C465A5DC-AD28-49A2-89C0-F81838814A7E}.ReleaseWin32|Any CPU.ActiveCfg = Release|Any CPU
+ {C465A5DC-AD28-49A2-89C0-F81838814A7E}.ReleaseWin32|Any CPU.Build.0 = Release|Any CPU
+ {C465A5DC-AD28-49A2-89C0-F81838814A7E}.ReleaseGnome|Any CPU.ActiveCfg = Release|Any CPU
+ {C465A5DC-AD28-49A2-89C0-F81838814A7E}.ReleaseGnome|Any CPU.Build.0 = Release|Any CPU
+ {C465A5DC-AD28-49A2-89C0-F81838814A7E}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C465A5DC-AD28-49A2-89C0-F81838814A7E}.Debug|x86.Build.0 = Debug|Any CPU
+ {C465A5DC-AD28-49A2-89C0-F81838814A7E}.Release|x86.ActiveCfg = Release|Any CPU
+ {C465A5DC-AD28-49A2-89C0-F81838814A7E}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{7525BB88-6142-4A26-93B9-A30C6983390A} = {9D360D43-0C05-49D6-84DB-4E7AB2F38F82}
@@ -2042,7 +2042,6 @@ Global
{2A705FC6-1A9E-4941-9E47-254D79F2D9D5} = {2D711139-8765-4929-BC7A-AA2DEE6F615D}
{7E891659-45F3-42B5-B940-A728780CCAE9} = {840E8B8D-929C-4CB2-9D23-2702FF01269A}
{5AD3093F-F79D-4014-B197-27D09492C3E3} = {840E8B8D-929C-4CB2-9D23-2702FF01269A}
- {C465A5DC-AD28-49A2-89C0-F81838814A7E} = {840E8B8D-929C-4CB2-9D23-2702FF01269A}
{BFE8691A-D323-4622-9021-7B8B27F81599} = {5D3F7E65-E55B-45CA-A83B-D1E10040281E}
{8A04FF99-5DFE-4E3D-A24F-72A621C8DDC6} = {5D3F7E65-E55B-45CA-A83B-D1E10040281E}
{D0B5AF2B-4BC1-4EB4-81D3-E5B85DDCE925} = {5D3F7E65-E55B-45CA-A83B-D1E10040281E}
@@ -2053,6 +2052,7 @@ Global
{9E4BA410-8338-42EC-AF9C-422C35ECED81} = {78C10DAE-D3D7-44FC-93DF-831D8D54ECF9}
{73D4CC8B-BAB9-4A29-841B-F25C6311F067} = {78C10DAE-D3D7-44FC-93DF-831D8D54ECF9}
{865100E2-A29C-4FCD-B803-1A0B9A0A6EF7} = {78C10DAE-D3D7-44FC-93DF-831D8D54ECF9}
+ {C465A5DC-AD28-49A2-89C0-F81838814A7E} = {F12939F1-D55A-4CE9-9F33-8D959BFC7D6C}
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
Policies = $0
@@ -2064,7 +2064,7 @@ Global
$2.TabsToSpaces = False
$2.inheritsSet = null
$2.inheritsScope = text/plain
- $2.scope = application/glade+xml
+ $2.scope = application/json
$0.DotNetNamingPolicy = $3
$3.DirectoryNamespaceAssociation = Flat
$3.ResourceNamePolicy = FileName
@@ -2101,7 +2101,7 @@ Global
$12.scope = image/svg+xml
$0.XmlFormattingPolicy = $13
$13.inheritsSet = null
- $13.scope = application/glade+xml
+ $13.scope = application/config+xml
$13.inheritsScope = application/xml
$0.TextStylePolicy = $14
$14.TabsToSpaces = False
diff --git a/main/external/NRefactory6 b/main/external/NRefactory6
-Subproject 1434281c7ab7bf20234d55ed9d369cf5615476e
+Subproject eca10d754c673c743858edfb857549b756efee9
diff --git a/main/external/RefactoringEssentials b/main/external/RefactoringEssentials
new file mode 160000
+Subproject b33f5e68749daa53cd70779d2b0c26dd6a504ac
diff --git a/main/src/addins/CSharpBinding/CSharpBinding.csproj b/main/src/addins/CSharpBinding/CSharpBinding.csproj
index 111300b4aa..80bef28958 100644
--- a/main/src/addins/CSharpBinding/CSharpBinding.csproj
+++ b/main/src/addins/CSharpBinding/CSharpBinding.csproj
@@ -126,10 +126,9 @@
<Name>ICSharpCode.NRefactory6.CSharp</Name>
<Private>False</Private>
</ProjectReference>
- <ProjectReference Include="..\..\..\external\NRefactory6\NR6Pack\NR6Pack.csproj">
+ <ProjectReference Include="..\..\..\external\RefactoringEssentials\RefactoringEssentials\RefactoringEssentials.csproj">
<Project>{C465A5DC-AD28-49A2-89C0-F81838814A7E}</Project>
- <Name>NR6Pack</Name>
- <Private>False</Private>
+ <Name>RefactoringEssentials</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
@@ -396,6 +395,218 @@
<Compile Include="MonoDevelop.CSharp.Refactoring\CodeGenerationService.cs" />
<Compile Include="MonoDevelop.CSharp\CSharpBraceMatcher.cs" />
<Compile Include="MonoDevelop.CSharp.Refactoring\FindProjectReferenceUsagesHandler.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\CSharp\CSharpExtractMethodService.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\CSharp\CSharpMethodExtractor.Analyzer.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\CSharp\CSharpMethodExtractor.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\CSharp\CSharpMethodExtractor.CSharpCodeGenerator.CallSiteContainerRewriter.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\CSharp\CSharpMethodExtractor.CSharpCodeGenerator.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\CSharp\CSharpMethodExtractor.CSharpCodeGenerator.ExpressionCodeGenerator.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\CSharp\CSharpMethodExtractor.CSharpCodeGenerator.MultipleStatementsCodeGenerator.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\CSharp\CSharpMethodExtractor.CSharpCodeGenerator.SingleStatementCodeGenerator.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\CSharp\CSharpMethodExtractor.FormattingProvider.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\CSharp\CSharpMethodExtractor.PostProcessor.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\CSharp\CSharpMethodExtractor.TriviaResult.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\CSharp\CSharpSelectionResult.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\CSharp\CSharpSelectionResult.ExpressionResult.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\CSharp\CSharpSelectionResult.StatementResult.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\CSharp\CSharpSelectionValidator.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\CSharp\CSharpSelectionValidator.Validator.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\CSharp\CSharpSyntaxTriviaService.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\CSharp\CSharpSyntaxTriviaServiceFactory.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\CSharp\Extensions.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\AbstractExtractMethodService.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\AbstractSyntaxTriviaService.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\AbstractSyntaxTriviaService.Result.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\Enums.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\Extensions.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\ExtractMethodMatrix.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\ExtractMethodOptions.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\ExtractMethodResult.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\ExtractMethodService.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\FailedExtractMethodResult.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\IExtractMethodService.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\InsertionPoint.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\ISyntaxTriviaService.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\MethodExtractor.Analyzer.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\MethodExtractor.Analyzer.SymbolMapBuilder.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\MethodExtractor.AnalyzerResult.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\MethodExtractor.CodeGenerator.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\MethodExtractor.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\MethodExtractor.GeneratedCode.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\MethodExtractor.TriviaResult.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\MethodExtractor.TypeParameterCollector.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\MethodExtractor.VariableInfo.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\MethodExtractor.VariableSymbol.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\OperationStatus.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\OperationStatus_Statics.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\OperationStatus`1.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\ParameterStyle.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\ReturnStyle.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\SelectionResult.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\SelectionValidator.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\SelectionValidator.NullSelectionResult.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\SimpleExtractMethodResult.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\UniqueNameGenerator.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\ExtractMethod\VariableStyle.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\ContextHandler\AttributeNamedParameterContextHandler.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\ContextHandler\CastCompletionContextHandler.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\ContextHandler\CompletionContextHandler.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\ContextHandler\DelegateCreationContextHandler.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\ContextHandler\EnumMemberContextHandler.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\ContextHandler\ExplicitInterfaceContextHandler.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\ContextHandler\ExternAliasContextHandler.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\ContextHandler\FormatItemContextHandler.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\ContextHandler\KeywordContextHandler.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\ContextHandler\NamedParameterContextHandler.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\ContextHandler\ObjectCreationContextHandler.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\ContextHandler\ObjectInitializerContextHandler.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\ContextHandler\OverrideContextHandler.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\ContextHandler\PartialContextHandler.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\ContextHandler\PreProcessorExpressionContextHandler.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\ContextHandler\RoslynRecommendationsCompletionContextHandler.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\ContextHandler\SenderCompletionContextHandler.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\ContextHandler\SnippetContextHandler.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\ContextHandler\SpeculativeNameContextHandler.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\ContextHandler\SpeculativeTContextHandler.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\ContextHandler\XmlDocCommentContextHandler.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\AbstractKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\AbstractSyntacticSingleKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\AddKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\AliasKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\AscendingKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\AsKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\AssemblyKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\AsyncKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\AwaitKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\BaseKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\BoolKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\BreakKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\ByKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\ByteKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\CaseKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\CatchKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\CharKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\CheckedKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\ChecksumKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\ClassKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\ConstKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\ContinueKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\DecimalKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\DefaultKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\DefineKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\DelegateKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\DescendingKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\DisableKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\DoKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\DoubleKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\DynamicKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\ElifKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\ElseKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\EndIfKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\EndRegionKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\EnumKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\EqualsKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\ErrorKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\EventKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\ExplicitKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\ExternKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\FalseKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\FieldKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\FinallyKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\FixedKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\FloatKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\ForEachKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\ForKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\FromKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\GetKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\GlobalKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\GotoKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\GroupKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\HiddenKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\IfKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\ImplicitKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\InKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\InterfaceKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\InternalKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\IntKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\IntoKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\IsKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\JoinKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\LetKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\LineKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\LockKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\LongKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\MethodKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\ModuleKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\NameOfKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\NamespaceKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\NewKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\NullKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\ObjectKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\OnKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\OperatorKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\OrderByKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\OutKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\OverrideKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\ParamKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\ParamsKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\PartialKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\PragmaKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\PrivateKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\PropertyKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\ProtectedKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\PublicKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\ReadOnlyKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\ReferenceKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\RefKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\RegionKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\RemoveKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\RestoreKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\ReturnKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\SByteKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\SealedKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\SelectKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\SetKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\ShortKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\SizeOfKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\StackAllocKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\StaticKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\StringKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\StructKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\SwitchKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\ThisKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\ThrowKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\TrueKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\TryKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\TypeKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\TypeOfKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\TypeVarKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\UIntKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\ULongKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\UncheckedKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\UndefKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\UnsafeKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\UShortKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\UsingKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\VarKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\VirtualKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\VoidKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\VolatileKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\WarningKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\WhenKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\WhereKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\WhileKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\KeywordRecommender\YieldKeywordRecommender.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\CompletionContext.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\CompletionEngine.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\CompletionResult.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\CompletionTriggerInfo.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\CompletionTriggerReason.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\DisplayFlags.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\EditorBrowsableBehavior.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\ICompletionCategory.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\ICompletionDataFactory.cs" />
+ <Compile Include="MonoDevelop.CSharp.Features\Completion\ICompletionKeyHandler.cs" />
</ItemGroup>
<ItemGroup>
<None Include="Makefile.am" />
@@ -449,5 +660,7 @@
<Folder Include="MonoDevelop.CSharp.CodeFixes\RemoveUnnecessaryCast\" />
<Folder Include="MonoDevelop.CSharp.Diagnostics\MonoTODO\" />
<Folder Include="MonoDevelop.CSharp.CodeRefactorings\ConvertToEnum\" />
+ <Folder Include="Neuer Ordner\" />
+ <Folder Include="MonoDevelop.CSharp.Features\EncapsulateField\" />
</ItemGroup>
</Project>
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/AddImport/AbstractAddImportCodeFixProvider.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/AddImport/AbstractAddImportCodeFixProvider.cs
index ca6de1af18..3ad7b6e1b7 100644
--- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/AddImport/AbstractAddImportCodeFixProvider.cs
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/AddImport/AbstractAddImportCodeFixProvider.cs
@@ -13,7 +13,7 @@ using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
using ICSharpCode.NRefactory6.CSharp;
-using ICSharpCode.NRefactory6.CSharp.Refactoring;
+using RefactoringEssentials;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/Async/CSharpAddAsyncCodeFixProvider.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/Async/CSharpAddAsyncCodeFixProvider.cs
index 11e5cf2d72..a5fcc5b4b2 100644
--- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/Async/CSharpAddAsyncCodeFixProvider.cs
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/Async/CSharpAddAsyncCodeFixProvider.cs
@@ -11,7 +11,7 @@ using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.CodeActions;
-using ICSharpCode.NRefactory6.CSharp.Refactoring;
+using RefactoringEssentials;
using MonoDevelop.CSharp.CodeFixes;
using ICSharpCode.NRefactory6.CSharp;
using Microsoft.CodeAnalysis.CSharp;
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/Async/CSharpConvertToAsyncMethodCodeFixProvider.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/Async/CSharpConvertToAsyncMethodCodeFixProvider.cs
index 5bd232f594..b1bd4224f6 100644
--- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/Async/CSharpConvertToAsyncMethodCodeFixProvider.cs
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/Async/CSharpConvertToAsyncMethodCodeFixProvider.cs
@@ -9,7 +9,7 @@ using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CodeActions;
-using ICSharpCode.NRefactory6.CSharp.Refactoring;
+using RefactoringEssentials;
using MonoDevelop.CSharp.CodeFixes;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/FullyQualify/CSharpFullyQualifyCodeFixProvider.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/FullyQualify/CSharpFullyQualifyCodeFixProvider.cs
index 01fb9b6052..7a40df4f42 100644
--- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/FullyQualify/CSharpFullyQualifyCodeFixProvider.cs
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/FullyQualify/CSharpFullyQualifyCodeFixProvider.cs
@@ -39,7 +39,7 @@ using Microsoft.CodeAnalysis.FindSymbols;
using System.Runtime.CompilerServices;
using Microsoft.CodeAnalysis.CodeActions;
using System;
-using ICSharpCode.NRefactory6.CSharp.Refactoring;
+using RefactoringEssentials;
using MonoDevelop.Ide.TypeSystem;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/ImplementAbstractClass/ImplementAbstractClassCodeFixProvider.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/ImplementAbstractClass/ImplementAbstractClassCodeFixProvider.cs
index f821d0d4a6..50f93e61f4 100644
--- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/ImplementAbstractClass/ImplementAbstractClassCodeFixProvider.cs
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/ImplementAbstractClass/ImplementAbstractClassCodeFixProvider.cs
@@ -10,7 +10,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis;
using ICSharpCode.NRefactory6.CSharp.Features.ImplementAbstractClass;
using MonoDevelop.Core;
-using ICSharpCode.NRefactory6.CSharp.Refactoring;
+using RefactoringEssentials;
using ICSharpCode.NRefactory6.CSharp;
namespace MonoDevelop.CSharp.CodeFixes.ImplementAbstractClass
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/MoveTypeToFile/MoveTypeToFile.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/MoveTypeToFile/MoveTypeToFile.cs
index a8c74428e8..7fc62d4b6c 100644
--- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/MoveTypeToFile/MoveTypeToFile.cs
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/MoveTypeToFile/MoveTypeToFile.cs
@@ -29,7 +29,7 @@ using System.IO;
using System.Linq;
using System.Threading.Tasks;
using ICSharpCode.NRefactory.CSharp;
-using ICSharpCode.NRefactory6.CSharp.Refactoring;
+using RefactoringEssentials;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/RemoveUnnecessaryCast/RemoveUnnecessaryCastCodeFixProvider.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/RemoveUnnecessaryCast/RemoveUnnecessaryCastCodeFixProvider.cs
index 63770abbdf..26283ce45d 100644
--- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/RemoveUnnecessaryCast/RemoveUnnecessaryCastCodeFixProvider.cs
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/RemoveUnnecessaryCast/RemoveUnnecessaryCastCodeFixProvider.cs
@@ -19,7 +19,7 @@ using Microsoft.CodeAnalysis;
using MonoDevelop.CSharp.Diagnostics;
using ICSharpCode.NRefactory6.CSharp;
using MonoDevelop.Core;
-using ICSharpCode.NRefactory6.CSharp.Refactoring;
+using RefactoringEssentials;
using Microsoft.CodeAnalysis.CSharp;
namespace MonoDevelop.CSharp.CodeFixes.RemoveUnnecessaryCast
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/RemoveUnnecessaryUsings/RemoveUnnecessaryUsingsCodeFixProvider.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/RemoveUnnecessaryUsings/RemoveUnnecessaryUsingsCodeFixProvider.cs
index d7cb42e0d2..e3ed5cea15 100644
--- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/RemoveUnnecessaryUsings/RemoveUnnecessaryUsingsCodeFixProvider.cs
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/RemoveUnnecessaryUsings/RemoveUnnecessaryUsingsCodeFixProvider.cs
@@ -11,7 +11,7 @@ using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis;
using ICSharpCode.NRefactory6.CSharp.Features.RemoveUnnecessaryImports;
using MonoDevelop.Core;
-using ICSharpCode.NRefactory6.CSharp.Refactoring;
+using RefactoringEssentials;
using Microsoft.CodeAnalysis.Text;
using MonoDevelop.CSharp.Diagnostics;
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/SimplifyTypeNames/SimplifyTypeNamesCodeFixProvider.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/SimplifyTypeNames/SimplifyTypeNamesCodeFixProvider.cs
index be3ccc4cd3..0b2b11df09 100644
--- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/SimplifyTypeNames/SimplifyTypeNamesCodeFixProvider.cs
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/SimplifyTypeNames/SimplifyTypeNamesCodeFixProvider.cs
@@ -19,7 +19,7 @@ using ICSharpCode.NRefactory6.CSharp;
using MonoDevelop.Core;
using System;
using Microsoft.CodeAnalysis.CSharp;
-using ICSharpCode.NRefactory6.CSharp.Refactoring;
+using RefactoringEssentials;
using MonoDevelop.CSharp.Diagnostics.SimplifyTypeNames;
namespace MonoDevelop.CSharp.CodeFixes.SimplifyTypeNames
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeRefactorings/ExtractMethod/ExtractMethodCodeRefactoringProvider.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeRefactorings/ExtractMethod/ExtractMethodCodeRefactoringProvider.cs
index 992b28b012..b1f395cf6d 100644
--- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeRefactorings/ExtractMethod/ExtractMethodCodeRefactoringProvider.cs
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeRefactorings/ExtractMethod/ExtractMethodCodeRefactoringProvider.cs
@@ -9,10 +9,10 @@ using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Text;
using System.Threading;
using ICSharpCode.NRefactory6.CSharp.ExtractMethod;
-using ICSharpCode.NRefactory6.CSharp.Refactoring;
using MonoDevelop.Core;
using MonoDevelop.Ide;
using ICSharpCode.NRefactory6.CSharp;
+using RefactoringEssentials;
namespace MonoDevelop.CSharp.CodeRefactorings.ExtractMethod
{
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs
index b5951ab7bf..5e4447f063 100644
--- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs
@@ -21,7 +21,7 @@ using Roslyn.Utilities;
using Microsoft.CodeAnalysis;
using ICSharpCode.NRefactory6.CSharp;
using Microsoft.CodeAnalysis.CSharp;
-using ICSharpCode.NRefactory6.CSharp.Refactoring;
+using RefactoringEssentials;
using MonoDevelop.Core;
namespace MonoDevelop.CSharp.CodeRefactorings.InlineTemporary
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CSharpCompletionTextEditorExtension.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CSharpCompletionTextEditorExtension.cs
index 476adc3e88..ea030ccc9a 100644
--- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CSharpCompletionTextEditorExtension.cs
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CSharpCompletionTextEditorExtension.cs
@@ -123,7 +123,7 @@ namespace MonoDevelop.CSharp.Completion
}
static Func<Microsoft.CodeAnalysis.Document, CancellationToken, Task<Microsoft.CodeAnalysis.Document>> WithFrozenPartialSemanticsAsync;
- static List<ICompletionData> snippets;
+ static List<ICSharpCode.NRefactory6.CSharp.Completion.CompletionData> snippets;
static CSharpCompletionTextEditorExtension ()
{
@@ -142,8 +142,8 @@ namespace MonoDevelop.CSharp.Completion
CompletionEngine.SnippetCallback = delegate(CancellationToken arg) {
if (snippets != null)
- return Task.FromResult ((IEnumerable<ICompletionData>)snippets);
- var newSnippets = new List<ICompletionData> ();
+ return Task.FromResult((IEnumerable<ICSharpCode.NRefactory6.CSharp.Completion.CompletionData>)snippets);
+ var newSnippets = new List<ICSharpCode.NRefactory6.CSharp.Completion.CompletionData>();
foreach (var ct in MonoDevelop.Ide.CodeTemplates.CodeTemplateService.GetCodeTemplates ("text/x-csharp")) {
if (string.IsNullOrEmpty (ct.Shortcut) || ct.CodeTemplateContext != MonoDevelop.Ide.CodeTemplates.CodeTemplateContext.Standard)
continue;
@@ -155,7 +155,7 @@ namespace MonoDevelop.CSharp.Completion
});
}
snippets = newSnippets;
- return Task.FromResult ((IEnumerable<ICompletionData>)newSnippets);
+ return Task.FromResult((IEnumerable<ICSharpCode.NRefactory6.CSharp.Completion.CompletionData>)newSnippets);
};
}
@@ -408,7 +408,7 @@ namespace MonoDevelop.CSharp.Completion
return null;
foreach (var symbol in completionResult) {
- list.Add ((CompletionData)symbol);
+ list.Add ((Ide.CodeCompletion.CompletionData)symbol);
}
if (IdeApp.Preferences.AddImportedItemsToCompletionList.Value && list.OfType<RoslynSymbolCompletionData> ().Any (cd => cd.Symbol is ITypeSymbol)) {
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/ProtocolMemberContextHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/ProtocolMemberContextHandler.cs
index a67e29b303..3ab31e92a0 100644
--- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/ProtocolMemberContextHandler.cs
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/ProtocolMemberContextHandler.cs
@@ -48,9 +48,9 @@ namespace MonoDevelop.CSharp.Completion
this.factory = factory;
}
- protected override IEnumerable<ICompletionData> CreateCompletionData (CompletionEngine engine, SemanticModel semanticModel, int position, ITypeSymbol returnType, Accessibility seenAccessibility, SyntaxToken startToken, SyntaxToken tokenBeforeReturnType, bool afterKeyword, CancellationToken cancellationToken)
+ protected override IEnumerable<CompletionData> CreateCompletionData (CompletionEngine engine, SemanticModel semanticModel, int position, ITypeSymbol returnType, Accessibility seenAccessibility, SyntaxToken startToken, SyntaxToken tokenBeforeReturnType, bool afterKeyword, CancellationToken cancellationToken)
{
- var result = new List<ICompletionData> ();
+ var result = new List<CompletionData> ();
ISet<ISymbol> overridableMembers;
if (!TryDetermineOverridableMembers (semanticModel, tokenBeforeReturnType, seenAccessibility, out overridableMembers, cancellationToken)) {
return result;
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/RoslynCodeCompletionFactory.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/RoslynCodeCompletionFactory.cs
index bc8779ff37..0dbc5d906f 100644
--- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/RoslynCodeCompletionFactory.cs
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/RoslynCodeCompletionFactory.cs
@@ -63,7 +63,7 @@ namespace MonoDevelop.CSharp.Completion
#region ICompletionDataFactory implementation
- ICSharpCode.NRefactory6.CSharp.Completion.ICompletionData ICSharpCode.NRefactory6.CSharp.Completion.ICompletionDataFactory.CreateGenericData (ICSharpCode.NRefactory6.CSharp.Completion.ICompletionKeyHandler keyHandler, string data, ICSharpCode.NRefactory6.CSharp.Completion.GenericDataType genericDataType)
+ ICSharpCode.NRefactory6.CSharp.Completion.CompletionData ICSharpCode.NRefactory6.CSharp.Completion.ICompletionDataFactory.CreateGenericData (ICSharpCode.NRefactory6.CSharp.Completion.ICompletionKeyHandler keyHandler, string data, ICSharpCode.NRefactory6.CSharp.Completion.GenericDataType genericDataType)
{
return new RoslynCompletionData (keyHandler) {
CompletionText = data,
@@ -128,7 +128,7 @@ namespace MonoDevelop.CSharp.Completion
}
}
- ICSharpCode.NRefactory6.CSharp.Completion.ICompletionData ICSharpCode.NRefactory6.CSharp.Completion.ICompletionDataFactory.CreateFormatItemCompletionData (ICSharpCode.NRefactory6.CSharp.Completion.ICompletionKeyHandler keyHandler, string format, string description, object example)
+ ICSharpCode.NRefactory6.CSharp.Completion.CompletionData ICSharpCode.NRefactory6.CSharp.Completion.ICompletionDataFactory.CreateFormatItemCompletionData (ICSharpCode.NRefactory6.CSharp.Completion.ICompletionKeyHandler keyHandler, string format, string description, object example)
{
return new FormatItemCompletionData (keyHandler, format, description, example);
}
@@ -174,7 +174,7 @@ namespace MonoDevelop.CSharp.Completion
}
}
- ICSharpCode.NRefactory6.CSharp.Completion.ICompletionData ICSharpCode.NRefactory6.CSharp.Completion.ICompletionDataFactory.CreateXmlDocCompletionData (ICSharpCode.NRefactory6.CSharp.Completion.ICompletionKeyHandler keyHandler, string title, string description, string insertText)
+ ICSharpCode.NRefactory6.CSharp.Completion.CompletionData ICSharpCode.NRefactory6.CSharp.Completion.ICompletionDataFactory.CreateXmlDocCompletionData (ICSharpCode.NRefactory6.CSharp.Completion.ICompletionKeyHandler keyHandler, string title, string description, string insertText)
{
return new XmlDocCompletionData (keyHandler, this, title, description, insertText);
}
@@ -189,17 +189,17 @@ namespace MonoDevelop.CSharp.Completion
return new RoslynSymbolCompletionData (keyHandler, this, symbol, text);
}
- ICSharpCode.NRefactory6.CSharp.Completion.ICompletionData ICSharpCode.NRefactory6.CSharp.Completion.ICompletionDataFactory.CreateNewOverrideCompletionData(ICSharpCode.NRefactory6.CSharp.Completion.ICompletionKeyHandler keyHandler, int declarationBegin, ITypeSymbol currentType, ISymbol m, bool afterKeyword)
+ ICSharpCode.NRefactory6.CSharp.Completion.CompletionData ICSharpCode.NRefactory6.CSharp.Completion.ICompletionDataFactory.CreateNewOverrideCompletionData(ICSharpCode.NRefactory6.CSharp.Completion.ICompletionKeyHandler keyHandler, int declarationBegin, ITypeSymbol currentType, ISymbol m, bool afterKeyword)
{
return new CreateOverrideCompletionData (keyHandler, this, declarationBegin, currentType, m, afterKeyword);
}
- ICSharpCode.NRefactory6.CSharp.Completion.ICompletionData ICSharpCode.NRefactory6.CSharp.Completion.ICompletionDataFactory.CreatePartialCompletionData(ICSharpCode.NRefactory6.CSharp.Completion.ICompletionKeyHandler keyHandler, int declarationBegin, ITypeSymbol currentType, IMethodSymbol method, bool afterKeyword)
+ ICSharpCode.NRefactory6.CSharp.Completion.CompletionData ICSharpCode.NRefactory6.CSharp.Completion.ICompletionDataFactory.CreatePartialCompletionData(ICSharpCode.NRefactory6.CSharp.Completion.ICompletionKeyHandler keyHandler, int declarationBegin, ITypeSymbol currentType, IMethodSymbol method, bool afterKeyword)
{
return new CreatePartialCompletionData (keyHandler, this, declarationBegin, currentType, method, afterKeyword);
}
- ICSharpCode.NRefactory6.CSharp.Completion.ICompletionData ICSharpCode.NRefactory6.CSharp.Completion.ICompletionDataFactory.CreateAnonymousMethod(ICSharpCode.NRefactory6.CSharp.Completion.ICompletionKeyHandler keyHandler, string displayText, string description, string textBeforeCaret, string textAfterCaret)
+ ICSharpCode.NRefactory6.CSharp.Completion.CompletionData ICSharpCode.NRefactory6.CSharp.Completion.ICompletionDataFactory.CreateAnonymousMethod(ICSharpCode.NRefactory6.CSharp.Completion.ICompletionKeyHandler keyHandler, string displayText, string description, string textBeforeCaret, string textAfterCaret)
{
return new AnonymousMethodCompletionData (keyHandler) {
CompletionText = textBeforeCaret + "|" + textAfterCaret,
@@ -208,17 +208,17 @@ namespace MonoDevelop.CSharp.Completion
};
}
- ICSharpCode.NRefactory6.CSharp.Completion.ICompletionData ICSharpCode.NRefactory6.CSharp.Completion.ICompletionDataFactory.CreateNewMethodDelegate(ICSharpCode.NRefactory6.CSharp.Completion.ICompletionKeyHandler keyHandler, ITypeSymbol delegateType, string varName, INamedTypeSymbol curType)
+ ICSharpCode.NRefactory6.CSharp.Completion.CompletionData ICSharpCode.NRefactory6.CSharp.Completion.ICompletionDataFactory.CreateNewMethodDelegate(ICSharpCode.NRefactory6.CSharp.Completion.ICompletionKeyHandler keyHandler, ITypeSymbol delegateType, string varName, INamedTypeSymbol curType)
{
return new EventCreationCompletionData (keyHandler, this, delegateType, varName, curType);
}
- ICSharpCode.NRefactory6.CSharp.Completion.ICompletionData ICSharpCode.NRefactory6.CSharp.Completion.ICompletionDataFactory.CreateObjectCreation (ICSharpCode.NRefactory6.CSharp.Completion.ICompletionKeyHandler keyHandler, ITypeSymbol type, ISymbol symbol, int declarationBegin, bool afterKeyword)
+ ICSharpCode.NRefactory6.CSharp.Completion.CompletionData ICSharpCode.NRefactory6.CSharp.Completion.ICompletionDataFactory.CreateObjectCreation (ICSharpCode.NRefactory6.CSharp.Completion.ICompletionKeyHandler keyHandler, ITypeSymbol type, ISymbol symbol, int declarationBegin, bool afterKeyword)
{
return new ObjectCreationCompletionData (keyHandler, this, semanticModel, type, symbol, declarationBegin, afterKeyword);
}
- ICSharpCode.NRefactory6.CSharp.Completion.ICompletionData ICSharpCode.NRefactory6.CSharp.Completion.ICompletionDataFactory.CreateCastCompletionData (ICSharpCode.NRefactory6.CSharp.Completion.ICompletionKeyHandler keyHandler, ISymbol member, SyntaxNode nodeToCast, ITypeSymbol targetType)
+ ICSharpCode.NRefactory6.CSharp.Completion.CompletionData ICSharpCode.NRefactory6.CSharp.Completion.ICompletionDataFactory.CreateCastCompletionData (ICSharpCode.NRefactory6.CSharp.Completion.ICompletionKeyHandler keyHandler, ISymbol member, SyntaxNode nodeToCast, ITypeSymbol targetType)
{
return new CastCompletionData (keyHandler, this, semanticModel, member, nodeToCast, targetType);
}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/RoslynCompletionData.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/RoslynCompletionData.cs
index 372e2e0ad7..c1596a382e 100644
--- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/RoslynCompletionData.cs
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/RoslynCompletionData.cs
@@ -38,7 +38,7 @@ using MonoDevelop.Ide;
namespace MonoDevelop.CSharp.Completion
{
- class RoslynCompletionData : CompletionData, ICSharpCode.NRefactory6.CSharp.Completion.ICompletionData
+ class RoslynCompletionData : CompletionData, ICSharpCode.NRefactory6.CSharp.Completion.CompletionData
{
List<CompletionData> overloads;
@@ -48,7 +48,7 @@ namespace MonoDevelop.CSharp.Completion
}
}
- void ICSharpCode.NRefactory6.CSharp.Completion.ICompletionData.AddOverload (ICSharpCode.NRefactory6.CSharp.Completion.ICompletionData data)
+ void ICSharpCode.NRefactory6.CSharp.Completion.CompletionData.AddOverload (ICSharpCode.NRefactory6.CSharp.Completion.CompletionData data)
{
if (overloads == null)
overloads = new List<CompletionData> ();
@@ -56,7 +56,7 @@ namespace MonoDevelop.CSharp.Completion
sorted = null;
}
- ICSharpCode.NRefactory6.CSharp.Completion.ICompletionCategory ICSharpCode.NRefactory6.CSharp.Completion.ICompletionData.CompletionCategory {
+ ICSharpCode.NRefactory6.CSharp.Completion.ICompletionCategory ICSharpCode.NRefactory6.CSharp.Completion.CompletionData.CompletionCategory {
get {
return (ICSharpCode.NRefactory6.CSharp.Completion.ICompletionCategory)base.CompletionCategory;
}
@@ -65,7 +65,7 @@ namespace MonoDevelop.CSharp.Completion
}
}
- ICSharpCode.NRefactory6.CSharp.Completion.DisplayFlags ICSharpCode.NRefactory6.CSharp.Completion.ICompletionData.DisplayFlags {
+ ICSharpCode.NRefactory6.CSharp.Completion.DisplayFlags ICSharpCode.NRefactory6.CSharp.Completion.CompletionData.DisplayFlags {
get {
return (ICSharpCode.NRefactory6.CSharp.Completion.DisplayFlags)base.DisplayFlags;
}
@@ -76,9 +76,9 @@ namespace MonoDevelop.CSharp.Completion
List<CompletionData> sorted;
- IEnumerable<ICSharpCode.NRefactory6.CSharp.Completion.ICompletionData> ICSharpCode.NRefactory6.CSharp.Completion.ICompletionData.OverloadedData {
+ IEnumerable<ICSharpCode.NRefactory6.CSharp.Completion.CompletionData> ICSharpCode.NRefactory6.CSharp.Completion.CompletionData.OverloadedData {
get {
- return (IEnumerable<ICSharpCode.NRefactory6.CSharp.Completion.ICompletionData>)OverloadedData;
+ return (IEnumerable<ICSharpCode.NRefactory6.CSharp.Completion.CompletionData>)OverloadedData;
}
}
@@ -98,7 +98,7 @@ namespace MonoDevelop.CSharp.Completion
protected readonly ICSharpCode.NRefactory6.CSharp.Completion.ICompletionKeyHandler keyHandler;
- ICSharpCode.NRefactory6.CSharp.Completion.ICompletionKeyHandler ICSharpCode.NRefactory6.CSharp.Completion.ICompletionData.KeyHandler {
+ ICSharpCode.NRefactory6.CSharp.Completion.ICompletionKeyHandler ICSharpCode.NRefactory6.CSharp.Completion.CompletionData.KeyHandler {
get {
return keyHandler;
}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Diagnostics/InconsistentNaming/NameConventionRule.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Diagnostics/InconsistentNaming/NameConventionRule.cs
index 2453efff88..7816fa310f 100644
--- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Diagnostics/InconsistentNaming/NameConventionRule.cs
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Diagnostics/InconsistentNaming/NameConventionRule.cs
@@ -27,7 +27,6 @@ using System;
using System.Text;
using MonoDevelop.Projects.Policies;
using MonoDevelop.Core.Serialization;
-using ICSharpCode.NRefactory6.CSharp.Refactoring;
namespace MonoDevelop.CSharp.Diagnostics.InconsistentNaming
{
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/CompletionContext.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/CompletionContext.cs
new file mode 100644
index 0000000000..88acf43833
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/CompletionContext.cs
@@ -0,0 +1,103 @@
+//
+// CompletionContext.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 Microsoft.CodeAnalysis;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+using ICSharpCode.NRefactory6.CSharp.Completion;
+using System.Linq;
+
+namespace ICSharpCode.NRefactory6.CSharp
+{
+ public class CompletionContext
+ {
+ readonly Document document;
+
+ public Document Document {
+ get {
+ return document;
+ }
+ }
+
+ SemanticModel semanticModel;
+
+ internal async Task<SemanticModel> GetSemanticModelAsync (CancellationToken cancellationToken = default (CancellationToken))
+ {
+ if (semanticModel == null)
+ semanticModel = await document.GetSemanticModelAsync (cancellationToken);
+ return semanticModel;
+ }
+
+ readonly int position;
+ public int Position {
+ get {
+ return position;
+ }
+ }
+
+ Task<SyntaxContext> syntaxContext;
+ object syntaxCreationLock = new object ();
+
+ internal async Task<SyntaxContext> GetSyntaxContextAsync (Workspace workspace, CancellationToken cancellationToken = default (CancellationToken))
+ {
+ if (syntaxContext == null) {
+ lock (syntaxCreationLock) {
+ syntaxContext = syntaxContext ?? Task.Run (() => {
+ return SyntaxContext.Create (workspace, document, semanticModel, position, cancellationToken);
+ });
+ }
+ }
+ return await syntaxContext;
+ }
+
+ IEnumerable<CompletionContextHandler> additionalContextHandlers;
+
+ /// <summary>
+ /// Adds completion context handlers to the given context.
+ /// </summary>
+ public IEnumerable<CompletionContextHandler> AdditionalContextHandlers {
+ get {
+ return additionalContextHandlers ?? Enumerable.Empty<CompletionContextHandler> ();
+ }
+ set {
+ additionalContextHandlers = value;
+ }
+ }
+
+ /// <summary>
+ /// If false no default handlers will be used and only the AdditionalContextHandlers will run.
+ /// </summary>
+ public bool UseDefaultContextHandlers { get; set; } = true;
+
+ public CompletionContext (Document document, int position, SemanticModel semanticModel = null)
+ {
+ this.document = document;
+ this.semanticModel = semanticModel;
+ this.position = position;
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/CompletionEngine.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/CompletionEngine.cs
new file mode 100644
index 0000000000..157e8c0155
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/CompletionEngine.cs
@@ -0,0 +1,204 @@
+//
+// CSharpCompletionEngine.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2011 Xamarin Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp;
+
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using System.Text;
+using Microsoft.CodeAnalysis.Recommendations;
+using System.Threading.Tasks;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ public partial class CompletionEngine
+ {
+ static CompletionContextHandler[] handlers = {
+ new RoslynRecommendationsCompletionContextHandler (),
+ new KeywordContextHandler(),
+ new OverrideContextHandler(),
+ new PartialContextHandler(),
+ new EnumMemberContextHandler(),
+ new XmlDocCommentContextHandler(),
+ new ExplicitInterfaceContextHandler(),
+ new AttributeNamedParameterContextHandler(),
+ new NamedParameterContextHandler(),
+ new SpeculativeTContextHandler(),
+ new SnippetContextHandler(),
+ new ObjectInitializerContextHandler(),
+ new FormatItemContextHandler(),
+ new SpeculativeNameContextHandler(),
+ new DelegateCreationContextHandler(),
+ new ObjectCreationContextHandler(),
+ new SenderCompletionContextHandler(),
+ new CastCompletionContextHandler(),
+ new PreProcessorExpressionContextHandler()
+ };
+
+ static readonly ICompletionKeyHandler DefaultKeyHandler = new RoslynRecommendationsCompletionContextHandler ();
+
+ readonly ICompletionDataFactory factory;
+ readonly Workspace workspace;
+
+ public ICompletionDataFactory Factory {
+ get {
+ return factory;
+ }
+ }
+
+ public Workspace Workspace {
+ get {
+ return workspace;
+ }
+ }
+
+ public CompletionEngine(Workspace workspace, ICompletionDataFactory factory)
+ {
+ if (workspace == null)
+ throw new ArgumentNullException("workspace");
+ if (factory == null)
+ throw new ArgumentNullException("factory");
+ this.workspace = workspace;
+ this.factory = factory;
+ }
+
+ public void AddImportCompletionData (CompletionResult result, Document document, SemanticModel semanticModel, int position, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ var ns = new Stack<INamespaceOrTypeSymbol>();
+ ns.Push(semanticModel.Compilation.GlobalNamespace);
+
+ semanticModel.LookupNamespacesAndTypes(position);
+ }
+
+ public async Task<CompletionResult> GetCompletionDataAsync(CompletionContext completionContext, CompletionTriggerInfo info, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ if (completionContext == null)
+ throw new ArgumentNullException ("completionContext");
+
+ var document = completionContext.Document;
+ var semanticModel = await completionContext.GetSemanticModelAsync (cancellationToken).ConfigureAwait(false);
+ var position = completionContext.Position;
+
+ var text = await document.GetTextAsync (cancellationToken).ConfigureAwait (false);
+ var ctx = await completionContext.GetSyntaxContextAsync (workspace, cancellationToken).ConfigureAwait (false);
+
+ // case lambda parameter (n1, $
+ if (ctx.TargetToken.IsKind (SyntaxKind.CommaToken) &&
+ ctx.TargetToken.Parent != null && ctx.TargetToken.Parent.Parent != null &&
+ ctx.TargetToken.Parent.Parent.IsKind(SyntaxKind.ParenthesizedLambdaExpression))
+ return CompletionResult.Empty;
+
+ var result = new CompletionResult();
+
+ if (position > 0) {
+ var nonExclusiveHandlers = new List<CompletionContextHandler> ();
+ var exclusiveHandlers = new List<CompletionContextHandler> ();
+ IEnumerable<CompletionContextHandler> handlerList;
+ if (completionContext.UseDefaultContextHandlers) {
+ handlerList = handlers.Concat (completionContext.AdditionalContextHandlers);
+ } else {
+ handlerList = completionContext.AdditionalContextHandlers;
+ }
+ foreach (var handler in handlerList) {
+ if (info.CompletionTriggerReason == CompletionTriggerReason.CompletionCommand || handler.IsTriggerCharacter (text, position - 1)) {
+ if (await handler.IsExclusiveAsync (document, position, info, cancellationToken)) {
+ exclusiveHandlers.Add (handler);
+ } else {
+ nonExclusiveHandlers.Add (handler);
+ }
+ }
+ }
+
+ foreach (var handler in exclusiveHandlers) {
+ var handlerResult = handler.GetCompletionDataAsync (result, this, completionContext, info, cancellationToken).Result;
+ //Console.WriteLine ("-----" + handler);
+ //foreach (var item in handlerResult) {
+ // Console.WriteLine (item.DisplayText);
+ //}
+ if (handlerResult != null)
+ result.AddRange (handlerResult);
+ }
+
+ if (result.Count == 0) {
+ foreach (var handler in nonExclusiveHandlers) {
+ var handlerResult = handler.GetCompletionDataAsync (result, this, completionContext, info, cancellationToken).Result;
+ //foreach (var item in handlerResult) {
+ // Console.WriteLine (item.DisplayText);
+ //}
+ if (handlerResult != null)
+ result.AddRange (handlerResult);
+ }
+ }
+ }
+
+ // prevent auto selection for "<number>." case
+ if (ctx.TargetToken.IsKind(SyntaxKind.DotToken)) {
+ var accessExpr = ctx.TargetToken.Parent as MemberAccessExpressionSyntax;
+ if (accessExpr != null &&
+ accessExpr.Expression != null &&
+ accessExpr.Expression.IsKind(SyntaxKind.NumericLiteralExpression)) {
+ result.AutoSelect = false;
+ }
+ }
+
+ if (ctx.LeftToken.Parent != null &&
+ ctx.LeftToken.Parent.Parent != null &&
+ ctx.TargetToken.Parent != null && !ctx.TargetToken.Parent.IsKind(SyntaxKind.NameEquals) &&
+ ctx.LeftToken.Parent.Parent.IsKind(SyntaxKind.AnonymousObjectMemberDeclarator))
+ result.AutoSelect = false;
+
+ if (ctx.TargetToken.IsKind (SyntaxKind.OpenParenToken) && ctx.TargetToken.GetPreviousToken ().IsKind (SyntaxKind.OpenParenToken)) {
+ var validTypes = TypeGuessing.GetValidTypes (semanticModel, ctx.TargetToken.Parent, cancellationToken);
+ result.AutoSelect = !validTypes.Any (t => t.IsDelegateType ());
+ }
+
+ foreach (var type in ctx.InferredTypes) {
+ if (type.TypeKind == TypeKind.Delegate) {
+ result.AutoSelect = false;
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ IEnumerable<ISymbol> GetAllMembers (ITypeSymbol type)
+ {
+ if (type == null)
+ yield break;
+ foreach (var member in type.GetMembers()) {
+ yield return member;
+ }
+ foreach (var baseMember in GetAllMembers(type.BaseType))
+ yield return baseMember;
+ }
+
+ public static Func<CancellationToken, Task<IEnumerable<CompletionData>>> SnippetCallback;
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/CompletionResult.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/CompletionResult.cs
new file mode 100644
index 0000000000..1d6164d6b7
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/CompletionResult.cs
@@ -0,0 +1,121 @@
+//
+// CompletionResult.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2014 Xamarin Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Collections.Generic;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ public class CompletionResult : IReadOnlyList<CompletionData>
+ {
+ public static readonly CompletionResult Empty = new CompletionResult ();
+
+ readonly List<CompletionData> data = new List<CompletionData> ();
+
+ public string DefaultCompletionString {
+ get;
+ internal set;
+ }
+
+ bool autoCompleteEmptyMatch = true;
+
+ public bool AutoCompleteEmptyMatch {
+ get { return autoCompleteEmptyMatch; }
+ set { autoCompleteEmptyMatch = value; }
+ }
+
+ public bool AutoCompleteEmptyMatchOnCurlyBracket {
+ get;
+ set;
+ }
+
+ public bool AutoSelect {
+ get;
+ set;
+ }
+
+ public bool CloseOnSquareBrackets {
+ get;
+ set;
+ }
+
+ public readonly List<IMethodSymbol> PossibleDelegates = new List<IMethodSymbol>();
+
+ #region IReadOnlyList<ICompletionData> implemenation
+ public IEnumerator<CompletionData> GetEnumerator()
+ {
+ return data.GetEnumerator();
+ }
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+ {
+ return ((System.Collections.IEnumerable)data).GetEnumerator();
+ }
+
+ public CompletionData this[int index] {
+ get {
+ return data [index];
+ }
+ }
+
+ public int Count {
+ get {
+ return data.Count;
+ }
+ }
+ #endregion
+
+ internal CompletionResult()
+ {
+ AutoSelect = true;
+ AutoCompleteEmptyMatchOnCurlyBracket = true;
+ }
+
+ internal void AddData (CompletionData completionData)
+ {
+ if (completionData.DisplayText == "foobar")
+ Console.WriteLine (Environment.StackTrace);
+ data.Add(completionData);
+ }
+
+ internal void AddRange (IEnumerable<CompletionData> completionData)
+ {
+ foreach (var data in completionData)
+ if (data.DisplayText == "foobar")
+ Console.WriteLine (Environment.StackTrace);
+
+ data.AddRange(completionData);
+ }
+
+ public static CompletionResult Create(IEnumerable<CompletionData> data)
+ {
+ var result = new CompletionResult();
+ result.data.AddRange(data);
+ return result;
+ }
+ }
+}
+
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/CompletionTriggerInfo.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/CompletionTriggerInfo.cs
new file mode 100644
index 0000000000..9ada1cd180
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/CompletionTriggerInfo.cs
@@ -0,0 +1,95 @@
+//
+// CompletionTriggerInfo.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2015 Xamarin Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ /// <summary>
+ /// Provides information about what triggered completion.
+ /// </summary>
+ public struct CompletionTriggerInfo
+ {
+ /// <summary>
+ /// Provides the reason that completion was triggered.
+ /// </summary>
+ public CompletionTriggerReason CompletionTriggerReason { get; private set; }
+
+ /// <summary>
+ /// If the <see cref="CompletionTriggerReason"/> was <see
+ /// cref="CompletionTriggerReason.CharTyped"/> then this was the character that was
+ /// typed or deleted by backspace. Otherwise it is null.
+ /// </summary>
+ public char? TriggerCharacter { get; private set; }
+
+ /// <summary>
+ /// Returns true if the reason completion was triggered was to augment an existing list of
+ /// completion items.
+ /// </summary>
+ public bool IsAugment { get; private set; }
+
+ /// <summary>
+ /// Returns true if completion was triggered by the debugger.
+ /// </summary>
+ public bool IsDebugger { get; private set; }
+
+ /// <summary>
+ /// Return true if completion is running in the Immediate Window.
+ /// </summary>
+ public bool IsImmediateWindow { get; private set; }
+
+ public CompletionTriggerInfo (CompletionTriggerReason completionTriggerReason, char? triggerCharacter = null, bool isAugment = false, bool isDebugger = false, bool isImmediateWindow = false) : this()
+ {
+ this.CompletionTriggerReason = completionTriggerReason;
+ this.TriggerCharacter = triggerCharacter;
+ this.IsAugment = isAugment;
+ this.IsDebugger = isDebugger;
+ this.IsImmediateWindow = isImmediateWindow;
+ }
+
+ public CompletionTriggerInfo WithIsAugment(bool isAugment)
+ {
+ return this.IsAugment == isAugment
+ ? this
+ : new CompletionTriggerInfo(this.CompletionTriggerReason, this.TriggerCharacter, isAugment, this.IsDebugger, this.IsImmediateWindow);
+ }
+
+ public CompletionTriggerInfo WithIsDebugger(bool isDebugger)
+ {
+ return this.IsDebugger == isDebugger
+ ? this
+ : new CompletionTriggerInfo(this.CompletionTriggerReason, this.TriggerCharacter, this.IsAugment, isDebugger, this.IsImmediateWindow);
+ }
+
+ public CompletionTriggerInfo WithIsImmediateWindow(bool isImmediateWIndow)
+ {
+ return this.IsImmediateWindow == isImmediateWIndow
+ ? this
+ : new CompletionTriggerInfo(this.CompletionTriggerReason, this.TriggerCharacter, this.IsAugment, this.IsDebugger, isImmediateWIndow);
+ }
+ }
+}
+
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/CompletionTriggerReason.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/CompletionTriggerReason.cs
new file mode 100644
index 0000000000..4c27c4727b
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/CompletionTriggerReason.cs
@@ -0,0 +1,37 @@
+//
+// CompletionTriggerReason.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2015 Xamarin Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ public enum CompletionTriggerReason
+ {
+ CharTyped,
+ CompletionCommand
+ }
+}
+
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/AttributeNamedParameterContextHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/AttributeNamedParameterContextHandler.cs
new file mode 100644
index 0000000000..ef74da4307
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/AttributeNamedParameterContextHandler.cs
@@ -0,0 +1,232 @@
+//
+// AttributeNamedParameterContextHandler.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.Threading.Tasks;
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using System.Linq;
+using System.Collections.Immutable;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ class AttributeNamedParameterContextHandler : CompletionContextHandler
+ {
+ protected async override Task<IEnumerable<CompletionData>> GetItemsWorkerAsync (CompletionResult completionResult, CompletionEngine engine, CompletionContext completionContext, CompletionTriggerInfo info, CancellationToken cancellationToken)
+ {
+ var document = completionContext.Document;
+ var position = completionContext.Position;
+
+ var syntaxTree = await document.GetCSharpSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
+ if (syntaxTree.IsInNonUserCode(position, cancellationToken))
+ {
+ return null;
+ }
+
+ var token = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken);
+ token = token.GetPreviousTokenIfTouchingWord(position);
+
+ if (token.Kind() != SyntaxKind.OpenParenToken && token.Kind() != SyntaxKind.CommaToken)
+ {
+ return null;
+ }
+
+ var attributeArgumentList = token.Parent as AttributeArgumentListSyntax;
+ var attributeSyntax = token.Parent.Parent as AttributeSyntax;
+ if (attributeSyntax == null || attributeArgumentList == null)
+ {
+ return null;
+ }
+
+ // We actually want to collect two sets of named parameters to present the user. The
+ // normal named parameters that come from the attribute constructors. These will be
+ // presented like "foo:". And also the named parameters that come from the writable
+ // fields/properties in the attribute. These will be presented like "bar =".
+
+ var existingNamedParameters = GetExistingNamedParameters(attributeArgumentList, position);
+
+ var workspace = document.Project.Solution.Workspace;
+ var semanticModel = await document.GetCSharpSemanticModelForNodeAsync(attributeSyntax, cancellationToken).ConfigureAwait(false);
+ var nameColonItems = await GetNameColonItemsAsync(engine, workspace, semanticModel, position, token, attributeSyntax, existingNamedParameters, cancellationToken).ConfigureAwait(false);
+ var nameEqualsItems = await GetNameEqualsItemsAsync(engine, workspace, semanticModel, position, token, attributeSyntax, existingNamedParameters, cancellationToken).ConfigureAwait(false);
+
+ // If we're after a name= parameter, then we only want to show name= parameters.
+ if (IsAfterNameEqualsArgument(token))
+ {
+ return nameEqualsItems;
+ }
+
+ return nameColonItems.Concat(nameEqualsItems);
+ }
+
+ protected async Task<bool> IsExclusiveAsync(Document document, int caretPosition, CompletionTriggerInfo triggerInfo, CancellationToken cancellationToken)
+ {
+ var syntaxTree = await document.GetCSharpSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
+ var token = syntaxTree.FindTokenOnLeftOfPosition(caretPosition, cancellationToken)
+ .GetPreviousTokenIfTouchingWord(caretPosition);
+
+ return IsAfterNameColonArgument(token) || IsAfterNameEqualsArgument(token);
+ }
+
+ private bool IsAfterNameColonArgument(SyntaxToken token)
+ {
+ var argumentList = token.Parent as AttributeArgumentListSyntax;
+ if (token.Kind() == SyntaxKind.CommaToken && argumentList != null)
+ {
+ foreach (var item in argumentList.Arguments.GetWithSeparators())
+ {
+ if (item.IsToken && item.AsToken() == token)
+ {
+ return false;
+ }
+
+ if (item.IsNode)
+ {
+ var node = item.AsNode() as AttributeArgumentSyntax;
+ if (node.NameColon != null)
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private bool IsAfterNameEqualsArgument(SyntaxToken token)
+ {
+ var argumentList = token.Parent as AttributeArgumentListSyntax;
+ if (token.Kind() == SyntaxKind.CommaToken && argumentList != null)
+ {
+ foreach (var item in argumentList.Arguments.GetWithSeparators())
+ {
+ if (item.IsToken && item.AsToken() == token)
+ {
+ return false;
+ }
+
+ if (item.IsNode)
+ {
+ var node = item.AsNode() as AttributeArgumentSyntax;
+ if (node.NameEquals != null)
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private Task<IEnumerable<CompletionData>> GetNameEqualsItemsAsync(CompletionEngine engine, Workspace workspace, SemanticModel semanticModel,
+ int position, SyntaxToken token, AttributeSyntax attributeSyntax, ISet<string> existingNamedParameters,
+ CancellationToken cancellationToken)
+ {
+ var attributeNamedParameters = GetAttributeNamedParameters(semanticModel, position, attributeSyntax, cancellationToken);
+ // var unspecifiedNamedParameters = attributeNamedParameters.Where(p => !existingNamedParameters.Contains(p.Name));
+
+ // var text = await semanticModel.SyntaxTree.GetTextAsync(cancellationToken).ConfigureAwait(false);
+ return Task.FromResult (
+ attributeNamedParameters
+ .Where (p => !existingNamedParameters.Contains (p.Name))
+ .Select (p => {
+ var result = engine.Factory.CreateSymbolCompletionData (this, p);
+ result.DisplayFlags |= DisplayFlags.NamedArgument;
+ return (CompletionData)result;
+ }));
+
+
+ }
+
+ private Task<IEnumerable<CompletionData>> GetNameColonItemsAsync(
+ CompletionEngine engine, Workspace workspace, SemanticModel semanticModel, int position, SyntaxToken token, AttributeSyntax attributeSyntax, ISet<string> existingNamedParameters,
+ CancellationToken cancellationToken)
+ {
+ var parameterLists = GetParameterLists(semanticModel, position, attributeSyntax, cancellationToken);
+ parameterLists = parameterLists.Where(pl => IsValid(pl, existingNamedParameters));
+
+ // var text = await semanticModel.SyntaxTree.GetTextAsync(cancellationToken).ConfigureAwait(false);
+ return Task.FromResult (
+ from pl in parameterLists
+ from p in pl
+ where !existingNamedParameters.Contains (p.Name)
+ select engine.Factory.CreateGenericData(this, p.Name + ":", GenericDataType.NamedParameter));
+ }
+
+ private bool IsValid(ImmutableArray<IParameterSymbol> parameterList, ISet<string> existingNamedParameters)
+ {
+ return existingNamedParameters.Except(parameterList.Select(p => p.Name)).IsEmpty();
+ }
+
+ private ISet<string> GetExistingNamedParameters(AttributeArgumentListSyntax argumentList, int position)
+ {
+ var existingArguments1 =
+ argumentList.Arguments.Where(a => a.Span.End <= position)
+ .Where(a => a.NameColon != null)
+ .Select(a => a.NameColon.Name.Identifier.ValueText);
+ var existingArguments2 =
+ argumentList.Arguments.Where(a => a.Span.End <= position)
+ .Where(a => a.NameEquals != null)
+ .Select(a => a.NameEquals.Name.Identifier.ValueText);
+
+ return existingArguments1.Concat(existingArguments2).ToSet();
+ }
+
+ private IEnumerable<ImmutableArray<IParameterSymbol>> GetParameterLists(
+ SemanticModel semanticModel,
+ int position,
+ AttributeSyntax attribute,
+ CancellationToken cancellationToken)
+ {
+ var within = semanticModel.GetEnclosingNamedTypeOrAssembly(position, cancellationToken);
+ var attributeType = semanticModel.GetTypeInfo(attribute, cancellationToken).Type as INamedTypeSymbol;
+ if (within != null && attributeType != null)
+ {
+ return attributeType.InstanceConstructors.Where(c => c.IsAccessibleWithin(within))
+ .Select(c => c.Parameters);
+ }
+
+ return SpecializedCollections.EmptyEnumerable<ImmutableArray<IParameterSymbol>>();
+ }
+
+ private IEnumerable<ISymbol> GetAttributeNamedParameters(
+ SemanticModel semanticModel,
+ int position,
+ AttributeSyntax attribute,
+ CancellationToken cancellationToken)
+ {
+ var within = semanticModel.GetEnclosingNamedTypeOrAssembly(position, cancellationToken);
+ var attributeType = semanticModel.GetTypeInfo(attribute, cancellationToken).Type as INamedTypeSymbol;
+ return attributeType.GetAttributeNamedParameters(semanticModel.Compilation, within);
+ }
+
+ }
+}
+
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/CastCompletionContextHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/CastCompletionContextHandler.cs
new file mode 100644
index 0000000000..233b58e555
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/CastCompletionContextHandler.cs
@@ -0,0 +1,108 @@
+//
+// CastCompletionContextHandler.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.Threading.Tasks;
+using System.Collections.Generic;
+using System.Threading;
+using System.Linq;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ public class CastCompletionContextHandler : CompletionContextHandler
+ {
+ protected async override Task<IEnumerable<CompletionData>> GetItemsWorkerAsync (CompletionResult completionResult, CompletionEngine engine, CompletionContext completionContext, CompletionTriggerInfo info, CancellationToken cancellationToken)
+ {
+ var position = completionContext.Position;
+ var document = completionContext.Document;
+ var ctx = await completionContext.GetSyntaxContextAsync (engine.Workspace, cancellationToken).ConfigureAwait (false);
+ var syntaxTree = ctx.SyntaxTree;
+ if (syntaxTree.IsInNonUserCode(position, cancellationToken) ||
+ syntaxTree.IsPreProcessorDirectiveContext(position, cancellationToken))
+ return Enumerable.Empty<CompletionData> ();
+ if (!syntaxTree.IsRightOfDotOrArrowOrColonColon(position, cancellationToken))
+ return Enumerable.Empty<CompletionData> ();
+ var ma = ctx.LeftToken.Parent as MemberAccessExpressionSyntax;
+ if (ma == null)
+ return Enumerable.Empty<CompletionData> ();
+
+ var model = ctx.CSharpSyntaxContext.SemanticModel;
+
+ var symbolInfo = model.GetSymbolInfo (ma.Expression);
+ if (symbolInfo.Symbol == null)
+ return Enumerable.Empty<CompletionData> ();
+
+ var list = new List<CompletionData> ();
+ var within = model.GetEnclosingNamedTypeOrAssembly(position, cancellationToken);
+ var addedSymbols = new HashSet<string> ();
+ foreach (var ifStmSyntax in ma.Expression.AncestorsAndSelf ().OfType<IfStatementSyntax> ()) {
+ var condition = ifStmSyntax.Condition.SkipParens ();
+ if (condition == null || !condition.IsKind (SyntaxKind.IsExpression))
+ continue;
+ var isExpr = ((BinaryExpressionSyntax)condition);
+ var leftSymbol = model.GetSymbolInfo (isExpr.Left);
+
+ if (leftSymbol.Symbol == symbolInfo.Symbol) {
+ var type = model.GetTypeInfo (isExpr.Right).Type;
+ if (type != null) {
+ Analyze (engine, ma.Expression, type, within, list, addedSymbols, cancellationToken);
+ }
+ }
+ }
+
+ return list;
+ }
+
+ void Analyze (CompletionEngine engine, SyntaxNode node, ITypeSymbol type, ISymbol within, List<CompletionData> list, HashSet<string> addedSymbols, CancellationToken cancellationToken)
+ {
+ var startType = type;
+
+ while (type.SpecialType != SpecialType.System_Object) {
+ foreach (var member in type.GetMembers ()) {
+ cancellationToken.ThrowIfCancellationRequested ();
+ if (member.IsImplicitlyDeclared || member.IsStatic)
+ continue;
+ if (member.IsOrdinaryMethod () || member.Kind == SymbolKind.Field || member.Kind == SymbolKind.Property) {
+ if (member.IsAccessibleWithin (within)) {
+ var completionData = engine.Factory.CreateCastCompletionData (this, member, node, startType);
+ if (addedSymbols.Contains (completionData.DisplayText))
+ continue;
+ addedSymbols.Add (completionData.DisplayText);
+ list.Add (completionData);
+ }
+ }
+ }
+
+ type = type.BaseType;
+ }
+ }
+ }
+}
+
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/CompletionContextHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/CompletionContextHandler.cs
new file mode 100644
index 0000000000..0b8a673d73
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/CompletionContextHandler.cs
@@ -0,0 +1,187 @@
+//
+// CompletionContextHandler.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2014 Xamarin Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp;
+
+using Microsoft.CodeAnalysis.Text;
+using System.Threading.Tasks;
+using System.Security.Policy;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ public abstract class CompletionContextHandler : ICompletionKeyHandler
+ {
+ public async Task<IEnumerable<CompletionData>> GetCompletionDataAsync (CompletionResult result, CompletionEngine engine, CompletionContext completionContext, CompletionTriggerInfo info, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ // If we were triggered by typign a character, then do a semantic check to make sure
+ // we're still applicable. If not, then return immediately.
+ if (info.CompletionTriggerReason == CompletionTriggerReason.CharTyped)
+ {
+ var isSemanticTriggerCharacter = await IsSemanticTriggerCharacterAsync(completionContext.Document, completionContext.Position - 1, cancellationToken).ConfigureAwait(false);
+ if (!isSemanticTriggerCharacter)
+ return null;
+ }
+
+ return await GetItemsWorkerAsync(result, engine, completionContext, info, cancellationToken).ConfigureAwait(false);
+
+ }
+
+ protected virtual Task<bool> IsSemanticTriggerCharacterAsync(Document document, int characterPosition, CancellationToken cancellationToken)
+ {
+ return Task.FromResult (true);
+ }
+
+ protected abstract Task<IEnumerable<CompletionData>> GetItemsWorkerAsync (CompletionResult result, CompletionEngine engine, CompletionContext completionContext, CompletionTriggerInfo info, CancellationToken cancellationToken);
+
+ static readonly char[] csharpCommitChars = {
+ ' ', '{', '}', '[', ']', '(', ')', '.', ',', ':',
+ ';', '+', '-', '*', '/', '%', '&', '|', '^', '!',
+ '~', '=', '<', '>', '?', '@', '#', '\'', '\"', '\\'
+ };
+
+ public virtual bool IsCommitCharacter (CompletionData completionItem, char ch, string textTypedSoFar)
+ {
+ return csharpCommitChars.Contains (ch);
+ }
+
+ public virtual bool SendEnterThroughToEditor(CompletionData completionItem, string textTypedSoFar)
+ {
+ return string.Compare (completionItem.DisplayText, textTypedSoFar, StringComparison.OrdinalIgnoreCase) == 0;
+ }
+
+ public virtual bool IsFilterCharacter(CompletionData completionItem, char ch, string textTypedSoFar)
+ {
+ return false;
+ }
+
+ public virtual Task<bool> IsExclusiveAsync(Document document, int position, CompletionTriggerInfo triggerInfo, CancellationToken cancellationToken)
+ {
+ return Task.FromResult (false);
+ }
+
+ public virtual bool IsTriggerCharacter (SourceText text, int position)
+ {
+ var ch = text [position];
+ return ch == '.' || // simple member access
+ ch == '#' || // pre processor directives
+ ch == '>' && position >= 1 && text [position - 1] == '-' || // pointer member access
+ ch == ':' && position >= 1 && text [position - 1] == ':' || // alias name
+ IsStartingNewWord (text, position);
+ }
+
+ internal protected static bool IsTriggerAfterSpaceOrStartOfWordCharacter(SourceText text, int characterPosition)
+ {
+ var ch = text[characterPosition];
+ if (ch == ' ') {
+ ch = text[characterPosition - 1];
+ return !char.IsWhiteSpace (ch);
+ }
+ return IsStartingNewWord(text, characterPosition);
+ }
+
+ internal protected static bool IsStartingNewWord (SourceText text, int position)
+ {
+ var ch = text [position];
+ if (!SyntaxFacts.IsIdentifierStartCharacter (ch))
+ return false;
+
+ if (position > 0 && IsWordCharacter (text [position - 1]))
+ return false;
+
+ if (position < text.Length - 1 && IsWordCharacter (text [position + 1]))
+ return false;
+
+ return true;
+ }
+
+ protected static bool IsWordCharacter (char ch)
+ {
+ return SyntaxFacts.IsIdentifierStartCharacter (ch) || SyntaxFacts.IsIdentifierPartCharacter (ch);
+ }
+
+ protected static bool IsOnStartLine(int position, SourceText text, int startLine)
+ {
+ return text.Lines.IndexOf(position) == startLine;
+ }
+
+ protected static TextSpan GetTextChangeSpan(SourceText text, int position)
+ {
+ return GetTextChangeSpan(text, position, IsTextChangeSpanStartCharacter, IsWordCharacter);
+ }
+
+ public static bool IsTextChangeSpanStartCharacter(char ch)
+ {
+ return ch == '@' || IsWordCharacter(ch);
+ }
+
+ public static TextSpan GetTextChangeSpan(SourceText text, int position,
+ Func<char, bool> isWordStartCharacter, Func<char, bool> isWordCharacter)
+ {
+ int start = position;
+ while (start > 0 && isWordStartCharacter(text[start - 1]))
+ {
+ start--;
+ }
+
+ // If we're brought up in the middle of a word, extend to the end of the word as well.
+ // This means that if a user brings up the completion list at the start of the word they
+ // will "insert" the text before what's already there (useful for qualifying existing
+ // text). However, if they bring up completion in the "middle" of a word, then they will
+ // "overwrite" the text. Useful for correcting misspellings or just replacing unwanted
+ // code with new code.
+ int end = position;
+ if (start != position)
+ {
+ while (end < text.Length && isWordCharacter(text[end]))
+ {
+ end++;
+ }
+ }
+
+ return TextSpan.FromBounds(start, end);
+ }
+
+ protected class UnionCompletionItemComparer : IEqualityComparer<CompletionData>
+ {
+ public static UnionCompletionItemComparer Instance = new UnionCompletionItemComparer();
+
+ public bool Equals(CompletionData x, CompletionData y)
+ {
+ return x.DisplayText == y.DisplayText;
+ }
+
+ public int GetHashCode(CompletionData obj)
+ {
+ return obj.DisplayText.GetHashCode();
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/DelegateCreationContextHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/DelegateCreationContextHandler.cs
new file mode 100644
index 0000000000..987ebca4b8
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/DelegateCreationContextHandler.cs
@@ -0,0 +1,267 @@
+//
+// DelegateCreationContextHandler.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.Threading.Tasks;
+using System.Collections.Generic;
+using System.Threading;
+using System.Linq;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis;
+using System.Text;
+using Microsoft.CodeAnalysis.Text;
+using ICSharpCode.NRefactory6.CSharp.ExtractMethod;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ class DelegateCreationContextHandler : CompletionContextHandler
+ {
+ internal static readonly SymbolDisplayFormat NameFormat =
+ new SymbolDisplayFormat (
+ globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
+ typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
+ propertyStyle: SymbolDisplayPropertyStyle.NameOnly,
+ genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters | SymbolDisplayGenericsOptions.IncludeVariance,
+ memberOptions: SymbolDisplayMemberOptions.IncludeParameters | SymbolDisplayMemberOptions.IncludeExplicitInterface,
+ parameterOptions:
+ SymbolDisplayParameterOptions.IncludeParamsRefOut |
+ SymbolDisplayParameterOptions.IncludeExtensionThis |
+ SymbolDisplayParameterOptions.IncludeType |
+ SymbolDisplayParameterOptions.IncludeName,
+ miscellaneousOptions:
+ SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers |
+ SymbolDisplayMiscellaneousOptions.UseSpecialTypes);
+
+ internal static readonly SymbolDisplayFormat overrideNameFormat = NameFormat.WithParameterOptions (
+ SymbolDisplayParameterOptions.IncludeDefaultValue |
+ SymbolDisplayParameterOptions.IncludeExtensionThis |
+ SymbolDisplayParameterOptions.IncludeType |
+ SymbolDisplayParameterOptions.IncludeName |
+ SymbolDisplayParameterOptions.IncludeParamsRefOut);
+
+ public override bool IsTriggerCharacter (SourceText text, int position)
+ {
+ var ch = text [position];
+ return ch == '(' || ch == '[' || ch == ',' || IsTriggerAfterSpaceOrStartOfWordCharacter (text, position);
+ }
+
+ protected async override Task<IEnumerable<CompletionData>> GetItemsWorkerAsync (CompletionResult result, CompletionEngine engine, CompletionContext completionContext, CompletionTriggerInfo info, CancellationToken cancellationToken)
+ {
+ var document = completionContext.Document;
+ var position = completionContext.Position;
+
+ var tree = await document.GetSyntaxTreeAsync (cancellationToken).ConfigureAwait (false);
+ var model = await document.GetSemanticModelAsync (cancellationToken).ConfigureAwait (false);
+ if (tree.IsInNonUserCode (position, cancellationToken))
+ return Enumerable.Empty<CompletionData> ();
+
+ var ctx = await completionContext.GetSyntaxContextAsync (engine.Workspace, cancellationToken).ConfigureAwait (false);
+
+ if (!ctx.CSharpSyntaxContext.IsAnyExpressionContext)
+ return Enumerable.Empty<CompletionData> ();
+ var list = new List<CompletionData> ();
+ foreach (var type in ctx.InferredTypes) {
+ if (type.TypeKind != TypeKind.Delegate)
+ continue;
+ string delegateName = null;
+
+ if (ctx.TargetToken.IsKind (SyntaxKind.PlusEqualsToken)) {
+ delegateName = GuessEventHandlerBaseName (ctx.LeftToken.Parent, ctx.ContainingTypeDeclaration);
+ }
+
+ AddDelegateHandlers (list, ctx.TargetToken.Parent, model, engine, result, type, position, delegateName, cancellationToken);
+ }
+ if (list.Count > 0) {
+ result.AutoSelect = false;
+ }
+ return list;
+ }
+
+
+ static string GuessEventHandlerBaseName (SyntaxNode node, TypeDeclarationSyntax containingTypeDeclaration)
+ {
+ var addAssign = node as AssignmentExpressionSyntax;
+ if (addAssign == null)
+ return null;
+
+ var ident = addAssign.Left as IdentifierNameSyntax;
+ if (ident != null)
+ return ToPascalCase (containingTypeDeclaration.Identifier + "_" + ident);
+
+ var memberAccess = addAssign.Left as MemberAccessExpressionSyntax;
+ if (memberAccess != null)
+ return ToPascalCase (GetMemberAccessBaseName(memberAccess) + "_" + memberAccess.Name);
+
+ return null;
+ }
+
+ static string GetMemberAccessBaseName (MemberAccessExpressionSyntax memberAccess)
+ {
+ var ident = memberAccess.Expression as IdentifierNameSyntax;
+ if (ident != null)
+ return ident.ToString ();
+
+ var ma = memberAccess.Expression as MemberAccessExpressionSyntax;
+ if (ma != null)
+ return ma.Name.ToString ();
+
+ return "Handle";
+ }
+
+ static string ToPascalCase (string str)
+ {
+ var result = new StringBuilder ();
+ result.Append (char.ToUpper (str[0]));
+ bool nextUpper = false;
+ for (int i = 1; i < str.Length; i++) {
+ var ch = str [i];
+ if (nextUpper && char.IsLetter (ch)) {
+ ch = char.ToUpper (ch);
+ nextUpper = false;
+ }
+ result.Append (ch);
+ if (ch == '_')
+ nextUpper = true;
+ }
+
+ return result.ToString ();
+ }
+
+ void AddDelegateHandlers (List<CompletionData> completionList, SyntaxNode parent, SemanticModel semanticModel, CompletionEngine engine, CompletionResult result, ITypeSymbol delegateType, int position, string optDelegateName, CancellationToken cancellationToken)
+ {
+ var delegateMethod = delegateType.GetDelegateInvokeMethod ();
+ result.PossibleDelegates.Add (delegateMethod);
+
+ var thisLineIndent = "";
+ string EolMarker = "\n";
+ bool addSemicolon = true;
+ bool addDefault = true;
+
+ string delegateEndString = EolMarker + thisLineIndent + "}" + (addSemicolon ? ";" : "");
+ //bool containsDelegateData = completionList.Result.Any(d => d.DisplayText.StartsWith("delegate("));
+ CompletionData item;
+ if (addDefault) {
+ item = engine.Factory.CreateAnonymousMethod (
+ this,
+ "delegate",
+ "Creates anonymous delegate.",
+ "delegate {" + EolMarker + thisLineIndent,
+ delegateEndString
+ );
+ if (!completionList.Any (i => i.DisplayText == item.DisplayText))
+ completionList.Add (item);
+
+ //if (LanguageVersion.Major >= 5)
+
+ item = engine.Factory.CreateAnonymousMethod (
+ this,
+ "async delegate",
+ "Creates anonymous async delegate.",
+ "async delegate {" + EolMarker + thisLineIndent,
+ delegateEndString
+ );
+ if (!completionList.Any (i => i.DisplayText == item.DisplayText))
+ completionList.Add (item);
+ }
+
+ var sb = new StringBuilder ("(");
+ var sbWithoutTypes = new StringBuilder ("(");
+ for (int k = 0; k < delegateMethod.Parameters.Length; k++) {
+ if (k > 0) {
+ sb.Append (", ");
+ sbWithoutTypes.Append (", ");
+ }
+ sb.Append (delegateMethod.Parameters [k].ToMinimalDisplayString (semanticModel, position, overrideNameFormat));
+ sbWithoutTypes.Append (delegateMethod.Parameters [k].Name);
+ }
+
+ sb.Append (")");
+ sbWithoutTypes.Append (")");
+ var signature = sb.ToString ()
+ .Replace (", params ", ", ")
+ .Replace ("(params ", "(");
+
+ if (completionList.All (data => data.DisplayText != signature)) {
+ item = engine.Factory.CreateAnonymousMethod (
+ this,
+ signature + " =>",
+ "Creates typed lambda expression.",
+ signature + " => ",
+ (addSemicolon ? ";" : "")
+ );
+ if (!completionList.Any (i => i.DisplayText == item.DisplayText))
+ completionList.Add (item);
+
+ // if (LanguageVersion.Major >= 5) {
+
+ item = engine.Factory.CreateAnonymousMethod (
+ this,
+ "async " + signature + " =>",
+ "Creates typed async lambda expression.",
+ "async " + signature + " => ",
+ (addSemicolon ? ";" : "")
+ );
+ if (!completionList.Any (i => i.DisplayText == item.DisplayText))
+ completionList.Add (item);
+
+ var signatureWithoutTypes = sbWithoutTypes.ToString ();
+ if (!delegateMethod.Parameters.Any (p => p.RefKind != RefKind.None) && completionList.All (data => data.DisplayText != signatureWithoutTypes)) {
+ item = engine.Factory.CreateAnonymousMethod (
+ this,
+ signatureWithoutTypes + " =>",
+ "Creates typed lambda expression.",
+ signatureWithoutTypes + " => ",
+ (addSemicolon ? ";" : "")
+ );
+ if (!completionList.Any (i => i.DisplayText == item.DisplayText))
+ completionList.Add (item);
+
+ //if (LanguageVersion.Major >= 5) {
+ item = engine.Factory.CreateAnonymousMethod (
+ this,
+ "async " + signatureWithoutTypes + " =>",
+ "Creates typed async lambda expression.",
+ "async " + signatureWithoutTypes + " => ",
+ (addSemicolon ? ";" : "")
+ );
+ if (!completionList.Any (i => i.DisplayText == item.DisplayText))
+ completionList.Add (item);
+
+ //}
+ }
+ }
+ string varName = optDelegateName ?? "Handle" + delegateType.Name;
+
+
+ var curType = semanticModel.GetEnclosingSymbol<INamedTypeSymbol> (position, cancellationToken);
+ var uniqueName = new UniqueNameGenerator (semanticModel).CreateUniqueMethodName (parent, varName);
+ item = engine.Factory.CreateNewMethodDelegate (this, delegateType, uniqueName, curType);
+ if (!completionList.Any (i => i.DisplayText == item.DisplayText))
+ completionList.Add (item);
+ }
+ }
+}
+
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/EnumMemberContextHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/EnumMemberContextHandler.cs
new file mode 100644
index 0000000000..00d2693070
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/EnumMemberContextHandler.cs
@@ -0,0 +1,117 @@
+//
+// EnumMemberContextHandler.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2014 Xamarin Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.CodeAnalysis.Recommendations;
+using Microsoft.CodeAnalysis;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp;
+
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.CodeAnalysis.Options;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+
+// public class CompletionEngineCache
+// {
+// public List<INamespace> namespaces;
+// public ICompletionData[] importCompletion;
+// }
+
+ class EnumMemberContextHandler : CompletionContextHandler
+ {
+ public override bool IsCommitCharacter (CompletionData completionItem, char ch, string textTypedSoFar)
+ {
+ // Only commit on dot.
+ return ch == '.';
+ }
+
+ public override bool IsTriggerCharacter(SourceText text, int position)
+ {
+ // Bring up on space or at the start of a word, or after a ( or [.
+ //
+ // Note: we don't want to bring this up after traditional enum operators like & or |.
+ // That's because we don't like the experience where the enum appears directly after the
+ // operator. Instead, the user normally types <space> and we will bring up the list
+ // then.
+ var ch = text[position];
+ return
+ ch == ' ' ||
+ ch == '[' ||
+ ch == '(' ||
+ (/*options.GetOption(CompletionOptions.TriggerOnTypingLetters, LanguageNames.CSharp) && CompletionUtilities.*/IsStartingNewWord(text, position));
+ }
+
+ protected async override Task<IEnumerable<CompletionData>> GetItemsWorkerAsync (CompletionResult completionResult, CompletionEngine engine, CompletionContext completionContext, CompletionTriggerInfo info, CancellationToken cancellationToken)
+ {
+ var ctx = await completionContext.GetSyntaxContextAsync (engine.Workspace, cancellationToken).ConfigureAwait(false);
+ var model = await completionContext.GetSemanticModelAsync (cancellationToken).ConfigureAwait (false);
+ var tree = await completionContext.Document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
+ if (tree.IsInNonUserCode(completionContext.Position, cancellationToken))
+ return Enumerable.Empty<CompletionData> ();
+
+ var token = tree.FindTokenOnLeftOfPosition(completionContext.Position, cancellationToken);
+ if (token.IsMandatoryNamedParameterPosition())
+ return Enumerable.Empty<CompletionData> ();
+ var result = new List<CompletionData> ();
+
+ foreach (var _type in ctx.InferredTypes) {
+ var type = _type;
+ if (type.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T) {
+ type = type.GetTypeArguments().FirstOrDefault();
+ if (type == null)
+ continue;
+ }
+
+ if (type.TypeKind != TypeKind.Enum)
+ continue;
+ if (!type.IsEditorBrowsable ())
+ continue;
+
+ // Does type have any aliases?
+ ISymbol alias = await type.FindApplicableAlias(completionContext.Position, model, cancellationToken).ConfigureAwait(false);
+
+ if (string.IsNullOrEmpty(completionResult.DefaultCompletionString))
+ completionResult.DefaultCompletionString = type.Name;
+
+ result.Add (engine.Factory.CreateSymbolCompletionData(this, type, type.ToMinimalDisplayString(model, completionContext.Position, SymbolDisplayFormat.CSharpErrorMessageFormat)));
+ foreach (IFieldSymbol field in type.GetMembers().OfType<IFieldSymbol>()) {
+ if (field.DeclaredAccessibility == Accessibility.Public && (field.IsConst || field.IsStatic)) {
+ result.Add (engine.Factory.CreateEnumMemberCompletionData(this, alias, field));
+ }
+ }
+ }
+ return result;
+ }
+ }
+
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/ExplicitInterfaceContextHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/ExplicitInterfaceContextHandler.cs
new file mode 100644
index 0000000000..389f5f7ab7
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/ExplicitInterfaceContextHandler.cs
@@ -0,0 +1,153 @@
+//
+// ExplicitInterfaceContextHandler.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.Threading.Tasks;
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.Text;
+using System.Linq;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ class ExplicitInterfaceContextHandler : CompletionContextHandler
+ {
+ public override bool IsTriggerCharacter(SourceText text, int position)
+ {
+ return text[position] == '.';
+ }
+
+ protected async override Task<IEnumerable<CompletionData>> GetItemsWorkerAsync (CompletionResult completionResult, CompletionEngine engine, CompletionContext completionContext, CompletionTriggerInfo info, CancellationToken cancellationToken)
+ {
+ var position = completionContext.Position;
+ var document = completionContext.Document;
+ var span = new TextSpan(position, 0);
+ var semanticModel = await document.GetCSharpSemanticModelForSpanAsync(span, cancellationToken).ConfigureAwait(false);
+ var syntaxTree = semanticModel.SyntaxTree;
+ // var ctx = await completionContext.GetSyntaxContextAsync (engine.Workspace, cancellationToken).ConfigureAwait (false);
+
+ if (syntaxTree.IsInNonUserCode(position, cancellationToken) ||
+ syntaxTree.IsPreProcessorDirectiveContext(position, cancellationToken))
+ {
+ return Enumerable.Empty<CompletionData> ();
+ }
+
+ if (!syntaxTree.IsRightOfDotOrArrowOrColonColon(position, cancellationToken))
+ {
+ return Enumerable.Empty<CompletionData> ();
+ }
+
+ var node = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken)
+ .GetPreviousTokenIfTouchingWord(position)
+ .Parent;
+
+ if (node.Kind() == SyntaxKind.ExplicitInterfaceSpecifier)
+ {
+ return await GetCompletionsOffOfExplicitInterfaceAsync(
+ engine, document, semanticModel, position, ((ExplicitInterfaceSpecifierSyntax)node).Name, cancellationToken).ConfigureAwait(false);
+ }
+
+ return Enumerable.Empty<CompletionData> ();
+ }
+
+ private Task<IEnumerable<CompletionData>> GetCompletionsOffOfExplicitInterfaceAsync(
+ CompletionEngine engine, Document document, SemanticModel semanticModel, int position, NameSyntax name, CancellationToken cancellationToken)
+ {
+ // Bind the interface name which is to the left of the dot
+ var syntaxTree = semanticModel.SyntaxTree;
+ var nameBinding = semanticModel.GetSymbolInfo(name, cancellationToken);
+ // var context = CSharpSyntaxContext.CreateContext(document.Project.Solution.Workspace, semanticModel, position, cancellationToken);
+
+ var symbol = nameBinding.Symbol as ITypeSymbol;
+ if (symbol == null || symbol.TypeKind != TypeKind.Interface)
+ {
+ return Task.FromResult (Enumerable.Empty<CompletionData> ());
+ }
+
+ var members = semanticModel.LookupSymbols (
+ position: name.SpanStart,
+ container: symbol)
+ .Where (s => !s.IsStatic);
+ // .FilterToVisibleAndBrowsableSymbols(document.ShouldHideAdvancedMembers(), semanticModel.Compilation);
+
+ // We're going to create a entry for each one, including the signature
+ var completions = new List<CompletionData>();
+
+// var signatureDisplayFormat =
+// new SymbolDisplayFormat(
+// genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
+// memberOptions:
+// SymbolDisplayMemberOptions.IncludeParameters,
+// parameterOptions:
+// SymbolDisplayParameterOptions.IncludeName |
+// SymbolDisplayParameterOptions.IncludeType |
+// SymbolDisplayParameterOptions.IncludeParamsRefOut,
+// miscellaneousOptions:
+// SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers |
+// SymbolDisplayMiscellaneousOptions.UseSpecialTypes);
+
+ var namePosition = name.SpanStart;
+
+ // var text = await context.SyntaxTree.GetTextAsync(cancellationToken).ConfigureAwait(false);
+ // var textChangeSpan = GetTextChangeSpan(text, context.Position);
+
+ foreach (var member in members)
+ {
+ // var displayString = member.ToMinimalDisplayString(semanticModel, namePosition, signatureDisplayFormat);
+ // var memberCopied = member;
+ // var insertionText = displayString;
+
+ completions.Add(engine.Factory.CreateSymbolCompletionData (this, member)
+
+ /*new SymbolCompletionItem(
+ this,
+ displayString,
+ insertionText: insertionText,
+ filterSpan: textChangeSpan,
+ position: position,
+ symbols: new List<ISymbol> { member },
+ context: context) */);
+ }
+
+ return Task.FromResult ((IEnumerable<CompletionData>)completions);
+ }
+
+// public override TextChange GetTextChange(CompletionItem selectedItem, char? ch = default(char), string textTypedSoFar = null)
+// {
+// if (ch.HasValue && ch.Value == '(')
+// {
+// return new TextChange(selectedItem.FilterSpan, ((SymbolCompletionItem)selectedItem).Symbols[0].Name);
+// }
+//
+// return new TextChange(selectedItem.FilterSpan, selectedItem.DisplayText);
+// }
+
+ }
+}
+
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/ExternAliasContextHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/ExternAliasContextHandler.cs
new file mode 100644
index 0000000000..35620aad79
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/ExternAliasContextHandler.cs
@@ -0,0 +1,72 @@
+//
+// ExternAliasContextHandler.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.Threading.Tasks;
+using System.Collections.Generic;
+using System.Threading;
+using System.Linq;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ class ExternAliasContextHandler : CompletionContextHandler
+ {
+ protected async override Task<IEnumerable<CompletionData>> GetItemsWorkerAsync (CompletionResult completionResult, CompletionEngine engine, CompletionContext completionContext, CompletionTriggerInfo info, CancellationToken cancellationToken)
+ {
+ var document = completionContext.Document;
+ var position = completionContext.Position;
+
+ var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
+
+ if (tree.IsInNonUserCode(position, cancellationToken))
+ return Enumerable.Empty<CompletionData> ();
+
+ var targetToken = tree.FindTokenOnLeftOfPosition(position, cancellationToken).GetPreviousTokenIfTouchingWord(position);
+ if (targetToken.IsKind(SyntaxKind.AliasKeyword) && targetToken.Parent.IsKind(SyntaxKind.ExternAliasDirective))
+ {
+ var compilation = await document.GetCSharpCompilationAsync(cancellationToken).ConfigureAwait(false);
+ var aliases = compilation.ExternalReferences.Where(r => r.Properties.Aliases != null).SelectMany(r => r.Properties.Aliases).ToSet();
+
+ if (aliases.Any())
+ {
+ var root = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false);
+ var usedAliases = root.ChildNodes().OfType<ExternAliasDirectiveSyntax>().Where(e => !e.Identifier.IsMissing).Select(e => e.Identifier.ValueText);
+ foreach (var used in usedAliases) {
+ aliases.Remove (used);
+ }
+ aliases.Remove(MetadataReferenceProperties.GlobalAlias);
+ return aliases.Select (e => engine.Factory.CreateGenericData (this, e, GenericDataType.Undefined));
+ }
+ }
+
+ return Enumerable.Empty<CompletionData> ();
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/FormatItemContextHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/FormatItemContextHandler.cs
new file mode 100644
index 0000000000..bf740b53bd
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/FormatItemContextHandler.cs
@@ -0,0 +1,256 @@
+//
+// FormatItemContextHandler.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.Threading.Tasks;
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp;
+using System.Linq;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ class FormatItemContextHandler : CompletionContextHandler
+ {
+ public override bool IsTriggerCharacter (Microsoft.CodeAnalysis.Text.SourceText text, int position)
+ {
+ var ch = text [position];
+ return ch == ':' || ch == '"';
+ }
+
+ protected async override Task<IEnumerable<CompletionData>> GetItemsWorkerAsync (CompletionResult completionResult, CompletionEngine engine, CompletionContext completionContext, CompletionTriggerInfo info, CancellationToken cancellationToken)
+ {
+ var ctx = await completionContext.GetSyntaxContextAsync (engine.Workspace, cancellationToken).ConfigureAwait(false);
+ var document = completionContext.Document;
+ var position = completionContext.Position;
+ var semanticModel = await completionContext.GetSemanticModelAsync (cancellationToken).ConfigureAwait (false);
+
+ if (ctx.TargetToken.Parent != null && ctx.TargetToken.Parent.Parent != null &&
+ ctx.TargetToken.Parent.Parent.IsKind(SyntaxKind.Argument)) {
+
+ if (ctx.TargetToken.Parent == null || !ctx.TargetToken.Parent.IsKind(SyntaxKind.StringLiteralExpression) ||
+ ctx.TargetToken.Parent.Parent == null || !ctx.TargetToken.Parent.Parent.IsKind(SyntaxKind.Argument) ||
+ ctx.TargetToken.Parent.Parent.Parent == null || !ctx.TargetToken.Parent.Parent.Parent.IsKind(SyntaxKind.ArgumentList) ||
+ ctx.TargetToken.Parent.Parent.Parent.Parent == null || !ctx.TargetToken.Parent.Parent.Parent.Parent.IsKind(SyntaxKind.InvocationExpression)) {
+ return Enumerable.Empty<CompletionData> ();
+ }
+ var formatArgument = GetFormatItemNumber(document, position);
+ var invocationExpression = ctx.TargetToken.Parent.Parent.Parent.Parent as InvocationExpressionSyntax;
+ var symbolInfo = semanticModel.GetSymbolInfo(invocationExpression);
+ return GetFormatCompletionData(engine, semanticModel, invocationExpression, formatArgument, symbolInfo.Symbol);
+ }
+
+ return Enumerable.Empty<CompletionData> ();
+ }
+
+
+ static readonly DateTime curDate = DateTime.Now;
+
+ IEnumerable<CompletionData> GenerateNumberFormatitems(CompletionEngine engine, bool isFloatingPoint)
+ {
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "D", "decimal", 123);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "D5", "decimal", 123);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "C", "currency", 123);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "C0", "currency", 123);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "E", "exponential", 1.23E4);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "E2", "exponential", 1.234);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "e2", "exponential", 1.234);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "F", "fixed-point", 123.45);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "F1", "fixed-point", 123.45);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "G", "general", 1.23E+56);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "g2", "general", 1.23E+56);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "N", "number", 12345.68);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "N1", "number", 12345.68);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "P", "percent", 12.34);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "P1", "percent", 12.34);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "R", "round-trip", 0.1230000001);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "X", "hexadecimal", 1234);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "x8", "hexadecimal", 1234);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "0000", "custom", 123);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "####", "custom", 123);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "##.###", "custom", 1.23);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "##.000", "custom", 1.23);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "## 'items'", "custom", 12);
+ }
+
+ IEnumerable<CompletionData> GenerateDateTimeFormatitems(CompletionEngine engine)
+ {
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "D", "long date", curDate);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "d", "short date", curDate);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "F", "full date long", curDate);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "f", "full date short", curDate);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "G", "general long", curDate);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "g", "general short", curDate);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "M", "month", curDate);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "O", "ISO 8601", curDate);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "R", "RFC 1123", curDate);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "s", "sortable", curDate);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "T", "long time", curDate);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "t", "short time", curDate);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "U", "universal full", curDate);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "u", "universal sortable", curDate);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "Y", "year month", curDate);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "yy-MM-dd", "custom", curDate);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "yyyy MMMMM dd", "custom", curDate);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "yy-MMM-dd ddd", "custom", curDate);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "yyyy-M-d dddd", "custom", curDate);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "hh:mm:ss t z", "custom", curDate);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "hh:mm:ss tt zz", "custom", curDate);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "HH:mm:ss tt zz", "custom", curDate);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "HH:m:s tt zz", "custom", curDate);
+
+ }
+
+ [Flags]
+ enum TestEnum
+ {
+ EnumCaseName = 0,
+ Flag1 = 1,
+ Flag2 = 2,
+ Flags
+ }
+
+ IEnumerable<CompletionData> GenerateEnumFormatitems(CompletionEngine engine)
+ {
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "G", "string value", TestEnum.EnumCaseName);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "F", "flags value", TestEnum.Flags);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "D", "integer value", TestEnum.Flags);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "X", "hexadecimal", TestEnum.Flags);
+ }
+
+ IEnumerable<CompletionData> GenerateTimeSpanFormatitems(CompletionEngine engine)
+ {
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "c", "invariant", new TimeSpan(0, 1, 23, 456));
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "G", "general long", new TimeSpan(0, 1, 23, 456));
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "g", "general short", new TimeSpan(0, 1, 23, 456));
+ }
+
+ static Guid defaultGuid = Guid.NewGuid();
+
+ IEnumerable<CompletionData> GenerateGuidFormatitems(CompletionEngine engine)
+ {
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "N", "digits", defaultGuid);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "D", "hypens", defaultGuid);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "B", "braces", defaultGuid);
+ yield return engine.Factory.CreateFormatItemCompletionData(this, "P", "parentheses", defaultGuid);
+ }
+
+
+ static int GetFormatItemNumber(Document document, int offset)
+ {
+ int number = 0;
+ var o = offset - 2;
+ var text = document.GetTextAsync().Result;
+ while (o > 0) {
+ char ch = text[o];
+ if (ch == '{')
+ return number;
+ if (!char.IsDigit(ch))
+ break;
+ number = number * 10 + ch - '0';
+ o--;
+ }
+ return -1;
+ }
+
+ IEnumerable<CompletionData> GetFormatCompletionForType(CompletionEngine engine, ITypeSymbol type)
+ {
+ if (type == null) {
+ return GenerateNumberFormatitems (engine, false)
+ .Concat (GenerateDateTimeFormatitems (engine))
+ .Concat (GenerateTimeSpanFormatitems (engine))
+ .Concat (GenerateEnumFormatitems (engine))
+ .Concat (GenerateGuidFormatitems (engine));
+ }
+
+ switch (type.ToString()) {
+ case "long":
+ case "System.Int64":
+ case "ulong":
+ case "System.UInt64":
+ case "int":
+ case "System.Int32":
+ case "uint":
+ case "System.UInt32":
+ case "short":
+ case "System.Int16":
+ case "ushort":
+ case "System.UInt16":
+ case "byte":
+ case "System.Byte":
+ case "sbyte":
+ case "System.SByte":
+ return GenerateNumberFormatitems(engine, false);
+ case "float":
+ case "System.Single":
+ case "double":
+ case "System.Double":
+ case "decimal":
+ case "System.Decimal":
+ return GenerateNumberFormatitems(engine, true);
+ case "System.Enum":
+ return GenerateEnumFormatitems(engine);
+ case "System.DateTime":
+ return GenerateDateTimeFormatitems(engine);
+ case "System.TimeSpan":
+ return GenerateTimeSpanFormatitems(engine);
+ case "System.Guid":
+ return GenerateGuidFormatitems(engine);
+ }
+ return CompletionResult.Empty;
+ }
+
+ IEnumerable<CompletionData> GetFormatCompletionData(CompletionEngine engine, SemanticModel semanticModel, InvocationExpressionSyntax invocationExpression, int formatArgument, ISymbol symbol)
+ {
+ var ma = invocationExpression.Expression as MemberAccessExpressionSyntax;
+
+ if (ma != null && ma.Name.ToString () == "ToString") {
+ return GetFormatCompletionForType(engine, symbol != null ? symbol.ContainingType : null);
+ } else {
+ var method = symbol as IMethodSymbol;
+ if (method == null)
+ return Enumerable.Empty<CompletionData> ();
+
+ ExpressionSyntax fmtArgumets;
+ IList<ExpressionSyntax> args;
+ if (FormatStringHelper.TryGetFormattingParameters(semanticModel, invocationExpression, out fmtArgumets, out args, null)) {
+ ITypeSymbol type = null;
+ if (formatArgument + 1< args.Count) {
+ var invokeArgument = semanticModel.GetSymbolInfo(args[formatArgument + 1]);
+ if (invokeArgument.Symbol != null)
+ type = invokeArgument.Symbol.GetReturnType();
+ }
+
+ return GetFormatCompletionForType(engine, type);
+ }
+ }
+ return Enumerable.Empty<CompletionData> ();
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/KeywordContextHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/KeywordContextHandler.cs
new file mode 100644
index 0000000000..074171be1b
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/KeywordContextHandler.cs
@@ -0,0 +1,244 @@
+//
+// KeywordContextHandler.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2014 Xamarin Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.CodeAnalysis.Recommendations;
+using Microsoft.CodeAnalysis;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp;
+
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using System.Text;
+using System.Threading.Tasks;
+using ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders;
+using Microsoft.CodeAnalysis.Text;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ internal sealed class RecommendedKeyword
+ {
+ public string Keyword { get; private set; }
+ public bool IsIntrinsic { get; private set; }
+ public bool ShouldFormatOnCommit { get; private set; }
+
+ public RecommendedKeyword (string keyword, bool isIntrinsic = false, bool shouldFormatOnCommit = false)
+ {
+ this.Keyword = keyword;
+ this.IsIntrinsic = isIntrinsic;
+ this.ShouldFormatOnCommit = shouldFormatOnCommit;
+ }
+
+ }
+
+ internal interface IKeywordRecommender<TContext>
+ {
+ IEnumerable<RecommendedKeyword> RecommendKeywords(int position, TContext context, CancellationToken cancellationToken);
+ }
+
+ class KeywordContextHandler : CompletionContextHandler
+ {
+ static readonly IKeywordRecommender<CSharpSyntaxContext>[] recommender = {
+ new AbstractKeywordRecommender(),
+ new AddKeywordRecommender(),
+ new AliasKeywordRecommender(),
+ new AscendingKeywordRecommender(),
+ new AsKeywordRecommender(),
+ new AssemblyKeywordRecommender(),
+ new AsyncKeywordRecommender(),
+ new AwaitKeywordRecommender(),
+ new BaseKeywordRecommender(),
+ new BoolKeywordRecommender(),
+ new BreakKeywordRecommender(),
+ new ByKeywordRecommender(),
+ new ByteKeywordRecommender(),
+ new CaseKeywordRecommender(),
+ new CatchKeywordRecommender(),
+ new CharKeywordRecommender(),
+ new CheckedKeywordRecommender(),
+ new ChecksumKeywordRecommender(),
+ new ClassKeywordRecommender(),
+ new ConstKeywordRecommender(),
+ new ContinueKeywordRecommender(),
+ new DecimalKeywordRecommender(),
+ new DefaultKeywordRecommender(),
+ new DefineKeywordRecommender(),
+ new DelegateKeywordRecommender(),
+ new DescendingKeywordRecommender(),
+ new DisableKeywordRecommender(),
+ new DoKeywordRecommender(),
+ new DoubleKeywordRecommender(),
+ new DynamicKeywordRecommender(),
+ new ElifKeywordRecommender(),
+ new ElseKeywordRecommender(),
+ new EndIfKeywordRecommender(),
+ new EndRegionKeywordRecommender(),
+ new EnumKeywordRecommender(),
+ new EqualsKeywordRecommender(),
+ new ErrorKeywordRecommender(),
+ new EventKeywordRecommender(),
+ new ExplicitKeywordRecommender(),
+ new ExternKeywordRecommender(),
+ new FalseKeywordRecommender(),
+ new FieldKeywordRecommender(),
+ new FinallyKeywordRecommender(),
+ new FixedKeywordRecommender(),
+ new FloatKeywordRecommender(),
+ new ForEachKeywordRecommender(),
+ new ForKeywordRecommender(),
+ new FromKeywordRecommender(),
+ new GetKeywordRecommender(),
+ new GlobalKeywordRecommender(),
+ new GotoKeywordRecommender(),
+ new GroupKeywordRecommender(),
+ new HiddenKeywordRecommender(),
+ new IfKeywordRecommender(),
+ new ImplicitKeywordRecommender(),
+ new InKeywordRecommender(),
+ new InterfaceKeywordRecommender(),
+ new InternalKeywordRecommender(),
+ new IntKeywordRecommender(),
+ new IntoKeywordRecommender(),
+ new IsKeywordRecommender(),
+ new JoinKeywordRecommender(),
+ new LetKeywordRecommender(),
+ new LineKeywordRecommender(),
+ new LockKeywordRecommender(),
+ new LongKeywordRecommender(),
+ new MethodKeywordRecommender(),
+ new ModuleKeywordRecommender(),
+ new NameOfKeywordRecommender(),
+ new NamespaceKeywordRecommender(),
+ new NewKeywordRecommender(),
+ new NullKeywordRecommender(),
+ new ObjectKeywordRecommender(),
+ new OnKeywordRecommender(),
+ new OperatorKeywordRecommender(),
+ new OrderByKeywordRecommender(),
+ new OutKeywordRecommender(),
+ new OverrideKeywordRecommender(),
+ new ParamKeywordRecommender(),
+ new ParamsKeywordRecommender(),
+ new PartialKeywordRecommender(),
+ new PragmaKeywordRecommender(),
+ new PrivateKeywordRecommender(),
+ new PropertyKeywordRecommender(),
+ new ProtectedKeywordRecommender(),
+ new PublicKeywordRecommender(),
+ new ReadOnlyKeywordRecommender(),
+ new ReferenceKeywordRecommender(),
+ new RefKeywordRecommender(),
+ new RegionKeywordRecommender(),
+ new RemoveKeywordRecommender(),
+ new RestoreKeywordRecommender(),
+ new ReturnKeywordRecommender(),
+ new SByteKeywordRecommender(),
+ new SealedKeywordRecommender(),
+ new SelectKeywordRecommender(),
+ new SetKeywordRecommender(),
+ new ShortKeywordRecommender(),
+ new SizeOfKeywordRecommender(),
+ new StackAllocKeywordRecommender(),
+ new StaticKeywordRecommender(),
+ new StringKeywordRecommender(),
+ new StructKeywordRecommender(),
+ new SwitchKeywordRecommender(),
+ new ThisKeywordRecommender(),
+ new ThrowKeywordRecommender(),
+ new TrueKeywordRecommender(),
+ new TryKeywordRecommender(),
+ new TypeKeywordRecommender(),
+ new TypeOfKeywordRecommender(),
+ new TypeVarKeywordRecommender(),
+ new UIntKeywordRecommender(),
+ new ULongKeywordRecommender(),
+ new UncheckedKeywordRecommender(),
+ new UndefKeywordRecommender(),
+ new UnsafeKeywordRecommender(),
+ new UShortKeywordRecommender(),
+ new UsingKeywordRecommender(),
+ new VarKeywordRecommender(),
+ new VirtualKeywordRecommender(),
+ new VoidKeywordRecommender(),
+ new VolatileKeywordRecommender(),
+ new WarningKeywordRecommender(),
+// new WhenKeywordRecommender(),
+ new WhereKeywordRecommender(),
+ new WhileKeywordRecommender(),
+ new YieldKeywordRecommender()
+ };
+
+ public override bool IsTriggerCharacter (Microsoft.CodeAnalysis.Text.SourceText text, int position)
+ {
+ var ch = text [position];
+ return ch == '#' ||
+ ch == ' ' && position >= 1 && !char.IsWhiteSpace (text [position - 1]) ||
+ IsStartingNewWord (text, position);
+ }
+
+ protected async override Task<IEnumerable<CompletionData>> GetItemsWorkerAsync (CompletionResult completionResult, CompletionEngine engine, CompletionContext completionContext, CompletionTriggerInfo info, CancellationToken cancellationToken)
+ {
+ var ctx = await completionContext.GetSyntaxContextAsync (engine.Workspace, cancellationToken).ConfigureAwait (false);
+ var model = await completionContext.GetSemanticModelAsync (cancellationToken).ConfigureAwait (false);
+ if (ctx.CSharpSyntaxContext.IsInNonUserCode) {
+ return Enumerable.Empty<CompletionData> ();
+ }
+
+ if (ctx.TargetToken.IsKind (SyntaxKind.OverrideKeyword))
+ return Enumerable.Empty<CompletionData> ();
+
+ if (info.CompletionTriggerReason == CompletionTriggerReason.CharTyped && info.TriggerCharacter == ' ') {
+ if (!ctx.CSharpSyntaxContext.IsEnumBaseListContext && !ctx.LeftToken.IsKind (SyntaxKind.EqualsToken) && !ctx.LeftToken.IsKind (SyntaxKind.EqualsEqualsToken))
+ return Enumerable.Empty<CompletionData> ();
+// completionResult.AutoCompleteEmptyMatch = false;
+ }
+
+ var result = new List<CompletionData> ();
+
+ foreach (var r in recommender) {
+ var recommended = r.RecommendKeywords (completionContext.Position, ctx.CSharpSyntaxContext, cancellationToken);
+ if (recommended == null)
+ continue;
+ foreach (var kw in recommended) {
+ result.Add (engine.Factory.CreateGenericData (this, kw.Keyword, GenericDataType.Keyword));
+ }
+ }
+
+// if (ctx.IsPreProcessorKeywordContext) {
+// foreach (var kw in preprocessorKeywords)
+// result.Add(factory.CreateGenericData (this, kw, GenericDataType.PreprocessorKeyword));
+// }
+//
+
+// if (parent.IsKind(SyntaxKind.TypeParameterConstraintClause)) {
+// result.Add(factory.CreateGenericData (this, "new()", GenericDataType.PreprocessorKeyword));
+// }
+ return result;
+ }
+
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/NamedParameterContextHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/NamedParameterContextHandler.cs
new file mode 100644
index 0000000000..4eb8cff5ce
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/NamedParameterContextHandler.cs
@@ -0,0 +1,218 @@
+//
+// NamedParameterContextHandler.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.Threading.Tasks;
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using System.Collections.Immutable;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp;
+using System.Linq;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ class NamedParameterContextHandler : CompletionContextHandler, IEqualityComparer<IParameterSymbol>
+ {
+
+ protected async override Task<IEnumerable<CompletionData>> GetItemsWorkerAsync (CompletionResult completionResult, CompletionEngine engine, CompletionContext completionContext, CompletionTriggerInfo info, CancellationToken cancellationToken)
+ {
+ var document = completionContext.Document;
+ var position = completionContext.Position;
+ var syntaxTree = await document.GetCSharpSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
+ if (syntaxTree.IsInNonUserCode(position, cancellationToken))
+ {
+ return null;
+ }
+
+ var token = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken);
+ token = token.GetPreviousTokenIfTouchingWord(position);
+
+ if (token.Kind() != SyntaxKind.OpenParenToken &&
+ token.Kind() != SyntaxKind.OpenBracketToken &&
+ token.Kind() != SyntaxKind.CommaToken)
+ {
+ return null;
+ }
+
+ var argumentList = token.Parent as BaseArgumentListSyntax;
+ if (argumentList == null)
+ {
+ return null;
+ }
+
+ var semanticModel = await document.GetCSharpSemanticModelForNodeAsync(argumentList, cancellationToken).ConfigureAwait(false);
+ var parameterLists = GetParameterLists(semanticModel, position, argumentList.Parent, cancellationToken);
+ if (parameterLists == null)
+ {
+ return null;
+ }
+
+ var existingNamedParameters = GetExistingNamedParameters(argumentList, position);
+ parameterLists = parameterLists.Where(pl => IsValid(pl, existingNamedParameters));
+
+ var unspecifiedParameters = parameterLists.SelectMany(pl => pl)
+ .Where(p => !existingNamedParameters.Contains(p.Name))
+ .Distinct(this);
+
+ // var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
+
+ return unspecifiedParameters
+ .Select(p => engine.Factory.CreateGenericData(this, p.Name + ":", GenericDataType.NamedParameter));
+ }
+
+
+ private bool IsValid(ImmutableArray<IParameterSymbol> parameterList, ISet<string> existingNamedParameters)
+ {
+ // A parameter list is valid if it has parameters that match in name all the existing
+ // named parameters that have been provided.
+ return existingNamedParameters.Except(parameterList.Select(p => p.Name)).IsEmpty();
+ }
+
+ private ISet<string> GetExistingNamedParameters(BaseArgumentListSyntax argumentList, int position)
+ {
+ var existingArguments = argumentList.Arguments.Where(a => a.Span.End <= position && a.NameColon != null)
+ .Select(a => a.NameColon.Name.Identifier.ValueText);
+
+ return existingArguments.ToSet();
+ }
+
+ private IEnumerable<ImmutableArray<IParameterSymbol>> GetParameterLists(
+ SemanticModel semanticModel,
+ int position,
+ SyntaxNode invocableNode,
+ CancellationToken cancellationToken)
+ {
+ return invocableNode.TypeSwitch(
+ (InvocationExpressionSyntax invocationExpression) => GetInvocationExpressionParameterLists(semanticModel, position, invocationExpression, cancellationToken),
+ (ConstructorInitializerSyntax constructorInitializer) => GetConstructorInitializerParameterLists(semanticModel, position, constructorInitializer, cancellationToken),
+ (ElementAccessExpressionSyntax elementAccessExpression) => GetElementAccessExpressionParameterLists(semanticModel, position, elementAccessExpression, cancellationToken),
+ (ObjectCreationExpressionSyntax objectCreationExpression) => GetObjectCreationExpressionParameterLists(semanticModel, position, objectCreationExpression, cancellationToken));
+ }
+
+ private IEnumerable<ImmutableArray<IParameterSymbol>> GetObjectCreationExpressionParameterLists(
+ SemanticModel semanticModel,
+ int position,
+ ObjectCreationExpressionSyntax objectCreationExpression,
+ CancellationToken cancellationToken)
+ {
+ var type = semanticModel.GetTypeInfo(objectCreationExpression, cancellationToken).Type as INamedTypeSymbol;
+ var within = semanticModel.GetEnclosingNamedType(position, cancellationToken);
+ if (type != null && within != null && type.TypeKind != TypeKind.Delegate)
+ {
+ return type.InstanceConstructors.Where(c => c.IsAccessibleWithin(within))
+ .Select(c => c.Parameters);
+ }
+
+ return null;
+ }
+
+ private IEnumerable<ImmutableArray<IParameterSymbol>> GetElementAccessExpressionParameterLists(
+ SemanticModel semanticModel,
+ int position,
+ ElementAccessExpressionSyntax elementAccessExpression,
+ CancellationToken cancellationToken)
+ {
+ var expressionSymbol = semanticModel.GetSymbolInfo(elementAccessExpression.Expression, cancellationToken).GetAnySymbol();
+ var expressionType = semanticModel.GetTypeInfo(elementAccessExpression.Expression, cancellationToken).Type;
+
+ if (expressionSymbol != null && expressionType != null)
+ {
+ var indexers = semanticModel.LookupSymbols(position, expressionType, WellKnownMemberNames.Indexer).OfType<IPropertySymbol>();
+ var within = semanticModel.GetEnclosingNamedTypeOrAssembly(position, cancellationToken);
+ if (within != null)
+ {
+ return indexers.Where(i => i.IsAccessibleWithin(within, throughTypeOpt: expressionType))
+ .Select(i => i.Parameters);
+ }
+ }
+
+ return null;
+ }
+
+ private IEnumerable<ImmutableArray<IParameterSymbol>> GetConstructorInitializerParameterLists(
+ SemanticModel semanticModel,
+ int position,
+ ConstructorInitializerSyntax constructorInitializer,
+ CancellationToken cancellationToken)
+ {
+ var within = semanticModel.GetEnclosingNamedType(position, cancellationToken);
+ if (within != null &&
+ (within.TypeKind == TypeKind.Struct || within.TypeKind == TypeKind.Class))
+ {
+ var type = constructorInitializer.Kind() == SyntaxKind.BaseConstructorInitializer
+ ? within.BaseType
+ : within;
+
+ if (type != null)
+ {
+ return type.InstanceConstructors.Where(c => c.IsAccessibleWithin(within))
+ .Select(c => c.Parameters);
+ }
+ }
+
+ return null;
+ }
+
+ private IEnumerable<ImmutableArray<IParameterSymbol>> GetInvocationExpressionParameterLists(
+ SemanticModel semanticModel,
+ int position,
+ InvocationExpressionSyntax invocationExpression,
+ CancellationToken cancellationToken)
+ {
+ var within = semanticModel.GetEnclosingNamedTypeOrAssembly(position, cancellationToken);
+ if (within != null)
+ {
+ var methodGroup = semanticModel.GetMemberGroup(invocationExpression.Expression, cancellationToken).OfType<IMethodSymbol>();
+ var expressionType = semanticModel.GetTypeInfo(invocationExpression.Expression, cancellationToken).Type as INamedTypeSymbol;
+
+ if (methodGroup.Any())
+ {
+ return methodGroup.Where(m => m.IsAccessibleWithin(within))
+ .Select(m => m.Parameters);
+ }
+ else if (expressionType.IsDelegateType())
+ {
+ var delegateType = (INamedTypeSymbol)expressionType;
+ return SpecializedCollections.SingletonEnumerable(delegateType.DelegateInvokeMethod.Parameters);
+ }
+ }
+
+ return null;
+ }
+
+ bool IEqualityComparer<IParameterSymbol>.Equals(IParameterSymbol x, IParameterSymbol y)
+ {
+ return x.Name.Equals(y.Name);
+ }
+
+ int IEqualityComparer<IParameterSymbol>.GetHashCode(IParameterSymbol obj)
+ {
+ return obj.Name.GetHashCode();
+ }
+ }
+}
+
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/ObjectCreationContextHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/ObjectCreationContextHandler.cs
new file mode 100644
index 0000000000..2a15d15524
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/ObjectCreationContextHandler.cs
@@ -0,0 +1,187 @@
+//
+// ObjectCreationContextHandler.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.Threading.Tasks;
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using System.Linq;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ class ObjectCreationContextHandler : CompletionContextHandler
+ {
+ // static readonly ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders.NewKeywordRecommender nkr = new ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders.NewKeywordRecommender ();
+
+ public override bool IsTriggerCharacter (Microsoft.CodeAnalysis.Text.SourceText text, int position)
+ {
+ return IsTriggerAfterSpaceOrStartOfWordCharacter (text, position);
+ }
+
+ public override bool IsCommitCharacter (CompletionData completionItem, char ch, string textTypedSoFar)
+ {
+ return ch == ' ' || ch == '(' || ch == '{' || ch == '[';
+ }
+
+ protected async override Task<IEnumerable<CompletionData>> GetItemsWorkerAsync (CompletionResult result, CompletionEngine engine, CompletionContext completionContext, CompletionTriggerInfo info, CancellationToken cancellationToken)
+ {
+ var ctx = await completionContext.GetSyntaxContextAsync (engine.Workspace, cancellationToken).ConfigureAwait (false);
+ var list = new List<CompletionData> ();
+
+ var newExpression = GetObjectCreationNewExpression (ctx.SyntaxTree, completionContext.Position, cancellationToken);
+ if (newExpression == null) {
+ if (ctx.SyntaxTree.IsInNonUserCode(completionContext.Position, cancellationToken) ||
+ ctx.SyntaxTree.IsPreProcessorDirectiveContext(completionContext.Position, cancellationToken))
+ return Enumerable.Empty<CompletionData> ();
+
+// if (!nkr.IsValid (completionContext.Position, ctx.CSharpSyntaxContext, cancellationToken))
+// return Enumerable.Empty<ICompletionData> ();
+
+ var tokenOnLeftOfPosition = ctx.SyntaxTree.FindTokenOnLeftOfPosition (completionContext.Position, cancellationToken);
+ if (!tokenOnLeftOfPosition.IsKind (SyntaxKind.EqualsToken) && !tokenOnLeftOfPosition.Parent.IsKind (SyntaxKind.EqualsValueClause))
+ return Enumerable.Empty<CompletionData> ();
+
+ foreach (var inferredType in SyntaxContext.InferenceService.InferTypes (ctx.CSharpSyntaxContext.SemanticModel, completionContext.Position, cancellationToken)) {
+ if (inferredType.IsEnumType () || inferredType.IsInterfaceType () || inferredType.IsAbstract)
+ continue;
+ foreach (var symbol in await GetPreselectedSymbolsWorker(ctx.CSharpSyntaxContext, inferredType, completionContext.Position - 1, cancellationToken)) {
+ var symbolCompletionData = engine.Factory.CreateObjectCreation (this, inferredType, symbol, completionContext.Position, false);
+ list.Add (symbolCompletionData);
+ }
+ }
+ return list;
+ }
+
+ var type = SyntaxContext.InferenceService.InferType (ctx.CSharpSyntaxContext.SemanticModel, newExpression, objectAsDefault: false, cancellationToken: cancellationToken);
+
+ foreach (var symbol in await GetPreselectedSymbolsWorker(ctx.CSharpSyntaxContext, type, completionContext.Position, cancellationToken)) {
+ var symbolCompletionData = engine.Factory.CreateObjectCreation (this, type, symbol, newExpression.SpanStart, true);
+ list.Add (symbolCompletionData);
+ if (string.IsNullOrEmpty (result.DefaultCompletionString))
+ result.DefaultCompletionString = symbolCompletionData.DisplayText;
+ }
+ return list;
+ }
+
+
+ static Task<IEnumerable<ISymbol>> GetPreselectedSymbolsWorker2 (CSharpSyntaxContext context, ITypeSymbol type, CancellationToken cancellationToken)
+ {
+ // Unwrap an array type fully. We only want to offer the underlying element type in the
+ // list of completion items.
+ bool isArray = false;
+ while (type is IArrayTypeSymbol) {
+ isArray = true;
+ type = ((IArrayTypeSymbol)type).ElementType;
+ }
+
+ if (type == null) {
+ return Task.FromResult (Enumerable.Empty<ISymbol> ());
+ }
+
+ // Unwrap nullable
+ if (type.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T) {
+ type = type.GetTypeArguments ().FirstOrDefault ();
+ }
+
+ if (type.SpecialType == SpecialType.System_Void) {
+ return Task.FromResult (Enumerable.Empty<ISymbol> ());
+ }
+
+ if (type.ContainsAnonymousType ()) {
+ return Task.FromResult (Enumerable.Empty<ISymbol> ());
+ }
+
+ if (!type.CanBeReferencedByName) {
+ return Task.FromResult (Enumerable.Empty<ISymbol> ());
+ }
+
+ // Normally the user can't say things like "new IList". Except for "IList[] x = new |".
+ // In this case we do want to allow them to preselect certain types in the completion
+ // list even if they can't new them directly.
+ if (!isArray) {
+ if (type.TypeKind == TypeKind.Interface ||
+ type.TypeKind == TypeKind.Pointer ||
+ type.TypeKind == TypeKind.Dynamic ||
+ type.IsAbstract) {
+ return Task.FromResult (Enumerable.Empty<ISymbol> ());
+ }
+
+ if (type.TypeKind == TypeKind.TypeParameter &&
+ !((ITypeParameterSymbol)type).HasConstructorConstraint) {
+ return Task.FromResult (Enumerable.Empty<ISymbol> ());
+ }
+ }
+
+// if (!type.IsEditorBrowsable(options.GetOption(RecommendationOptions.HideAdvancedMembers, context.SemanticModel.Language), context.SemanticModel.Compilation))
+// {
+// return SpecializedTasks.EmptyEnumerable<ISymbol>();
+// }
+//
+ return Task.FromResult (SpecializedCollections.SingletonEnumerable ((ISymbol)type));
+ }
+
+ static async Task<IEnumerable<ISymbol>> GetPreselectedSymbolsWorker (CSharpSyntaxContext context, ITypeSymbol inferredType, int position, CancellationToken cancellationToken)
+ {
+ var result = await GetPreselectedSymbolsWorker2 (context, inferredType, cancellationToken).ConfigureAwait (false);
+ if (result.Any ()) {
+ var type = (ITypeSymbol)result.Single ();
+ var alias = await type.FindApplicableAlias (position, context.SemanticModel, cancellationToken).ConfigureAwait (false);
+ if (alias != null) {
+ return SpecializedCollections.SingletonEnumerable (alias);
+ }
+ }
+
+ return result;
+ }
+
+ internal static SyntaxNode GetObjectCreationNewExpression (SyntaxTree tree, int position, CancellationToken cancellationToken)
+ {
+ if (tree != null) {
+ if (!tree.IsInNonUserCode (position, cancellationToken)) {
+ var tokenOnLeftOfPosition = tree.FindTokenOnLeftOfPosition (position, cancellationToken);
+ var newToken = tokenOnLeftOfPosition.GetPreviousTokenIfTouchingWord (position);
+
+ // Only after 'new'.
+ if (newToken.Kind () == SyntaxKind.NewKeyword) {
+ // Only if the 'new' belongs to an object creation expression (and isn't a 'new'
+ // modifier on a member).
+ if (tree.IsObjectCreationTypeContext (position, tokenOnLeftOfPosition, cancellationToken)) {
+ return newToken.Parent as ExpressionSyntax;
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ }
+}
+
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/ObjectInitializerContextHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/ObjectInitializerContextHandler.cs
new file mode 100644
index 0000000000..0e99411d2e
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/ObjectInitializerContextHandler.cs
@@ -0,0 +1,250 @@
+//
+// ObjectInitializerContextHandler.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.Threading.Tasks;
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Text;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ class ObjectInitializerContextHandler : CompletionContextHandler
+ {
+ public override bool SendEnterThroughToEditor (CompletionData completionItem, string textTypedSoFar)
+ {
+ return false;
+ }
+
+ public override async Task<bool> IsExclusiveAsync (Document document, int position, CompletionTriggerInfo triggerInfo, CancellationToken cancellationToken)
+ {
+ // We're exclusive if this context could only be an object initializer and not also a
+ // collection initializer. If we're initializing something that could be initialized as
+ // an object or as a collection, say we're not exclusive. That way the rest of
+ // intellisense can be used in the collection intitializer.
+ //
+ // Consider this case:
+
+ // class c : IEnumerable<int>
+ // {
+ // public void Add(int addend) { }
+ // public int foo;
+ // }
+
+ // void foo()
+ // {
+ // var b = new c {|
+ // }
+
+ // There we could initialize b using either an object initializer or a collection
+ // initializer. Since we don't know which the user will use, we'll be non-exclusive, so
+ // the other providers can help the user write the collection initializer, if they want
+ // to.
+ var tree = await document.GetCSharpSyntaxTreeAsync (cancellationToken).ConfigureAwait (false);
+
+ if (tree.IsInNonUserCode (position, cancellationToken)) {
+ return false;
+ }
+
+ var token = tree.FindTokenOnLeftOfPosition (position, cancellationToken);
+ token = token.GetPreviousTokenIfTouchingWord (position);
+
+ if (token.Parent == null) {
+ return false;
+ }
+
+ var expression = token.Parent.Parent as ExpressionSyntax;
+ if (expression == null) {
+ return false;
+ }
+
+ var semanticModel = await document.GetCSharpSemanticModelForNodeAsync (expression, cancellationToken).ConfigureAwait (false);
+ var initializedType = semanticModel.GetTypeInfo (expression, cancellationToken).Type;
+ if (initializedType == null) {
+ return false;
+ }
+
+ // Non-exclusive if initializedType can be initialized as a collection.
+ if (initializedType.CanSupportCollectionInitializer ()) {
+ return false;
+ }
+
+ // By default, only our member names will show up.
+ return true;
+ }
+
+ public override bool IsTriggerCharacter (Microsoft.CodeAnalysis.Text.SourceText text, int characterPosition)
+ {
+ return base.IsTriggerCharacter (text, characterPosition) || text [characterPosition] == ' ';
+ }
+
+ protected async override Task<IEnumerable<CompletionData>> GetItemsWorkerAsync (CompletionResult completionResult, CompletionEngine engine, CompletionContext completionContext, CompletionTriggerInfo info, CancellationToken cancellationToken)
+ {
+ var document = completionContext.Document;
+ var position = completionContext.Position;
+ var workspace = document.Project.Solution.Workspace;
+ var semanticModel = await document.GetSemanticModelForSpanAsync (new TextSpan (position, 0), cancellationToken).ConfigureAwait (false);
+ var typeAndLocation = GetInitializedType (document, semanticModel, position, cancellationToken);
+ if (typeAndLocation == null)
+ return Enumerable.Empty<CompletionData> ();
+
+ var initializedType = typeAndLocation.Item1 as INamedTypeSymbol;
+ var initializerLocation = typeAndLocation.Item2;
+ if (initializedType == null)
+ return Enumerable.Empty<CompletionData> ();
+
+ // Find the members that can be initialized. If we have a NamedTypeSymbol, also get the overridden members.
+ IEnumerable<ISymbol> members = semanticModel.LookupSymbols (position, initializedType);
+ members = members.Where (m => IsInitializable (m, initializedType) &&
+ m.CanBeReferencedByName &&
+ IsLegalFieldOrProperty (m) &&
+ !m.IsImplicitlyDeclared);
+
+ // Filter out those members that have already been typed
+ var alreadyTypedMembers = GetInitializedMembers (semanticModel.SyntaxTree, position, cancellationToken);
+ var uninitializedMembers = members.Where (m => !alreadyTypedMembers.Contains (m.Name));
+
+ uninitializedMembers = uninitializedMembers.Where (m => m.IsEditorBrowsable ());
+
+ // var text = await semanticModel.SyntaxTree.GetTextAsync(cancellationToken).ConfigureAwait(false);
+ // var changes = GetTextChangeSpan(text, position);
+ var list = new List<CompletionData> ();
+
+ // Return the members
+ foreach (var member in uninitializedMembers) {
+ list.Add (engine.Factory.CreateSymbolCompletionData (this, member));
+ }
+ return list;
+ }
+
+ static bool IsLegalFieldOrProperty (ISymbol symbol)
+ {
+ var type = symbol.GetMemberType ();
+ if (type != null && type.CanSupportCollectionInitializer ()) {
+ return true;
+ }
+
+ return symbol.IsWriteableFieldOrProperty ();
+ }
+
+
+ static bool IsInitializable (ISymbol member, INamedTypeSymbol containingType)
+ {
+ var propertySymbol = member as IPropertySymbol;
+ if (propertySymbol != null) {
+ if (propertySymbol.Parameters.Any (p => !p.IsOptional))
+ return false;
+ }
+
+
+ return
+ !member.IsStatic &&
+ member.MatchesKind (SymbolKind.Field, SymbolKind.Property) &&
+ member.IsAccessibleWithin (containingType);
+ }
+
+
+ static Tuple<ITypeSymbol, Location> GetInitializedType (Document document, SemanticModel semanticModel, int position, CancellationToken cancellationToken)
+ {
+ var tree = semanticModel.SyntaxTree;
+ if (tree.IsInNonUserCode (position, cancellationToken)) {
+ return null;
+ }
+
+ var token = tree.FindTokenOnLeftOfPosition (position, cancellationToken);
+ token = token.GetPreviousTokenIfTouchingWord (position);
+
+ if (token.Kind () != SyntaxKind.CommaToken && token.Kind () != SyntaxKind.OpenBraceToken) {
+ return null;
+ }
+
+ if (token.Parent == null || token.Parent.Parent == null) {
+ return null;
+ }
+
+ // If we got a comma, we can syntactically find out if we're in an ObjectInitializerExpression
+ if (token.Kind () == SyntaxKind.CommaToken &&
+ token.Parent.Kind () != SyntaxKind.ObjectInitializerExpression) {
+ return null;
+ }
+
+ // new Foo { bar = $$
+ if (token.Parent.Parent.IsKind (SyntaxKind.ObjectCreationExpression)) {
+ var objectCreation = token.Parent.Parent as ObjectCreationExpressionSyntax;
+ if (objectCreation == null) {
+ return null;
+ }
+
+ var ctor = semanticModel.GetSymbolInfo (objectCreation, cancellationToken).Symbol;
+ var type = ctor != null ? ctor.ContainingType : null;
+ if (type == null) {
+ type = semanticModel.GetSpeculativeTypeInfo (objectCreation.SpanStart, objectCreation.Type, SpeculativeBindingOption.BindAsTypeOrNamespace).Type as INamedTypeSymbol;
+ }
+
+ return Tuple.Create<ITypeSymbol, Location> (type, token.GetLocation ());
+ }
+
+ // Nested: new Foo { bar = { $$
+ if (token.Parent.Parent.IsKind (SyntaxKind.SimpleAssignmentExpression)) {
+ // Use the type inferrer to get the type being initialzied.
+ var typeInferenceService = TypeGuessing.typeInferenceService;
+ var parentInitializer = token.GetAncestor<InitializerExpressionSyntax> ();
+
+ var expectedType = typeInferenceService.InferType (semanticModel, parentInitializer, objectAsDefault: false, cancellationToken: cancellationToken);
+ return Tuple.Create (expectedType, token.GetLocation ());
+ }
+
+ return null;
+ }
+
+ static HashSet<string> GetInitializedMembers (SyntaxTree tree, int position, CancellationToken cancellationToken)
+ {
+ var token = tree.FindTokenOnLeftOfPosition (position, cancellationToken)
+ .GetPreviousTokenIfTouchingWord (position);
+
+ // We should have gotten back a { or ,
+ if (token.Kind () == SyntaxKind.CommaToken || token.Kind () == SyntaxKind.OpenBraceToken) {
+ if (token.Parent != null) {
+ var initializer = token.Parent as InitializerExpressionSyntax;
+
+ if (initializer != null) {
+ return new HashSet<string> (initializer.Expressions.OfType<AssignmentExpressionSyntax> ()
+ .Where (b => b.OperatorToken.Kind () == SyntaxKind.EqualsToken)
+ .Select (b => b.Left)
+ .OfType<IdentifierNameSyntax> ()
+ .Select (i => i.Identifier.ValueText));
+ }
+ }
+ }
+
+ return new HashSet<string> ();
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/OverrideContextHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/OverrideContextHandler.cs
new file mode 100644
index 0000000000..e79c538df9
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/OverrideContextHandler.cs
@@ -0,0 +1,372 @@
+//
+// OverrideContextHandler.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.Linq;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CodeGeneration;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.CodeAnalysis.CSharp;
+using System;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ public class OverrideContextHandler : CompletionContextHandler
+ {
+ public override bool IsTriggerCharacter (SourceText text, int position)
+ {
+ return IsTriggerAfterSpaceOrStartOfWordCharacter (text, position);
+ }
+
+ protected async override Task<IEnumerable<CompletionData>> GetItemsWorkerAsync (CompletionResult completionResult, CompletionEngine engine, CompletionContext completionContext, CompletionTriggerInfo info, CancellationToken cancellationToken)
+ {
+ // var ctx = await completionContext.GetSyntaxContextAsync (engine.Workspace, cancellationToken).ConfigureAwait (false);
+ var document = completionContext.Document;
+ var semanticModel = await completionContext.GetSemanticModelAsync (cancellationToken).ConfigureAwait (false);
+ var tree = await document.GetSyntaxTreeAsync (cancellationToken).ConfigureAwait (false);
+ if (tree.IsInNonUserCode(completionContext.Position, cancellationToken))
+ return Enumerable.Empty<CompletionData> ();
+
+ var text = await document.GetTextAsync (cancellationToken).ConfigureAwait (false);
+
+ var startLineNumber = text.Lines.IndexOf (completionContext.Position);
+
+ // modifiers* override modifiers* type? |
+ Accessibility seenAccessibility;
+ //DeclarationModifiers modifiers;
+ var token = tree.FindTokenOnLeftOfPosition(completionContext.Position, cancellationToken);
+ if (token.Parent == null)
+ return Enumerable.Empty<CompletionData> ();
+
+ var parentMember = token.Parent.AncestorsAndSelf ().OfType<MemberDeclarationSyntax> ().FirstOrDefault (m => !m.IsKind (SyntaxKind.IncompleteMember));
+
+ if (!(parentMember is BaseTypeDeclarationSyntax) &&
+
+ /* May happen in case:
+ *
+ * override $
+ * public override string Foo () {}
+ */
+ !(token.IsKind (SyntaxKind.OverrideKeyword) && token.Span.Start <= parentMember.Span.Start))
+ return Enumerable.Empty<CompletionData> ();
+
+ var position = completionContext.Position;
+ var startToken = token.GetPreviousTokenIfTouchingWord(position);
+ ITypeSymbol returnType;
+ SyntaxToken tokenBeforeReturnType;
+ TryDetermineReturnType (startToken, semanticModel, cancellationToken, out returnType, out tokenBeforeReturnType);
+ if (returnType == null) {
+ var enclosingType = semanticModel.GetEnclosingSymbol (position, cancellationToken) as INamedTypeSymbol;
+ if (enclosingType != null && (startToken.IsKind (SyntaxKind.OpenBraceToken) || startToken.IsKind (SyntaxKind.CloseBraceToken) || startToken.IsKind (SyntaxKind.SemicolonToken))) {
+ return CreateCompletionData (engine, semanticModel, position, returnType, Accessibility.NotApplicable, startToken, tokenBeforeReturnType, false, cancellationToken);
+ }
+ }
+
+ if (!TryDetermineModifiers(ref tokenBeforeReturnType, text, startLineNumber, out seenAccessibility/*, out modifiers*/) ||
+ !TryCheckForTrailingTokens (tree, text, startLineNumber, position, cancellationToken)) {
+ return Enumerable.Empty<CompletionData> ();
+ }
+
+ return CreateCompletionData (engine, semanticModel, position, returnType, seenAccessibility, startToken, tokenBeforeReturnType, true, cancellationToken);
+ }
+
+ protected virtual IEnumerable<CompletionData> CreateCompletionData (CompletionEngine engine, SemanticModel semanticModel, int position, ITypeSymbol returnType, Accessibility seenAccessibility, SyntaxToken startToken, SyntaxToken tokenBeforeReturnType, bool afterKeyword, CancellationToken cancellationToken)
+ {
+ var result = new List<CompletionData> ();
+ ISet<ISymbol> overridableMembers;
+ if (!TryDetermineOverridableMembers (semanticModel, tokenBeforeReturnType, seenAccessibility, out overridableMembers, cancellationToken)) {
+ return result;
+ }
+ if (returnType != null) {
+ overridableMembers = FilterOverrides (overridableMembers, returnType);
+ }
+ var curType = semanticModel.GetEnclosingSymbol<INamedTypeSymbol> (position, cancellationToken);
+ var declarationBegin = afterKeyword ? startToken.Parent.SpanStart : position - 1;
+ foreach (var m in overridableMembers) {
+ var data = engine.Factory.CreateNewOverrideCompletionData (this, declarationBegin, curType, m, afterKeyword);
+ result.Add (data);
+ }
+ return result;
+ }
+
+ protected static ISet<ISymbol> FilterOverrides(ISet<ISymbol> members, ITypeSymbol returnType)
+ {
+ var filteredMembers = new HashSet<ISymbol>(
+ from m in members
+ where m.GetReturnType ().ToString () == returnType.ToString ()
+ select m);
+
+ // Don't filter by return type if we would then have nothing to show.
+ // This way, the user gets completion even if they speculatively typed the wrong return type
+ if (filteredMembers.Count > 0)
+ {
+ members = filteredMembers;
+ }
+
+ return members;
+ }
+
+ static bool TryDetermineReturnType(SyntaxToken startToken, SemanticModel semanticModel, CancellationToken cancellationToken, out ITypeSymbol returnType, out SyntaxToken nextToken)
+ {
+ nextToken = startToken;
+ returnType = null;
+ if (startToken.Parent is TypeSyntax)
+ {
+ var typeSyntax = (TypeSyntax)startToken.Parent;
+
+ // 'partial' is actually an identifier. If we see it just bail. This does mean
+ // we won't handle overrides that actually return a type called 'partial'. And
+ // not a single tear was shed.
+ if (typeSyntax is IdentifierNameSyntax &&
+ ((IdentifierNameSyntax)typeSyntax).Identifier.IsKindOrHasMatchingText(SyntaxKind.PartialKeyword))
+ {
+ return false;
+ }
+
+ returnType = semanticModel.GetTypeInfo(typeSyntax, cancellationToken).Type;
+ nextToken = typeSyntax.GetFirstToken().GetPreviousToken();
+ }
+
+ return true;
+ }
+
+
+ static bool HasOverridden (ISymbol original, ISymbol testSymbol)
+ {
+ if (original.Kind != testSymbol.Kind)
+ return false;
+ switch (testSymbol.Kind) {
+ case SymbolKind.Method:
+ return ((IMethodSymbol)testSymbol).OverriddenMethod == original;
+ case SymbolKind.Property:
+ return ((IPropertySymbol)testSymbol).OverriddenProperty == original;
+ case SymbolKind.Event:
+ return ((IEventSymbol)testSymbol).OverriddenEvent == original;
+ }
+ return false;
+ }
+
+ public static bool IsOverridable(ISymbol member, INamedTypeSymbol containingType)
+ {
+ if (member.IsAbstract || member.IsVirtual || member.IsOverride) {
+ if (member.IsSealed) {
+ return false;
+ }
+
+ if (!member.IsAccessibleWithin(containingType)) {
+ return false;
+ }
+
+ switch (member.Kind) {
+ case SymbolKind.Event:
+ return true;
+ case SymbolKind.Method:
+ return ((IMethodSymbol)member).MethodKind == MethodKind.Ordinary;
+ case SymbolKind.Property:
+ return !((IPropertySymbol)member).IsWithEvents;
+ }
+ }
+ return false;
+ }
+
+ static bool TryDetermineOverridableMembers(SemanticModel semanticModel, SyntaxToken startToken, Accessibility seenAccessibility, out ISet<ISymbol> overridableMembers, CancellationToken cancellationToken)
+ {
+ var result = new HashSet<ISymbol>();
+ var containingType = semanticModel.GetEnclosingSymbol<INamedTypeSymbol>(startToken.SpanStart, cancellationToken);
+ if (containingType != null && !containingType.IsScriptClass && !containingType.IsImplicitClass)
+ {
+ if (containingType.TypeKind == TypeKind.Class || containingType.TypeKind == TypeKind.Struct)
+ {
+ var baseTypes = containingType.GetBaseTypes().Reverse();
+ foreach (var type in baseTypes)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ // Prefer overrides in derived classes
+ RemoveOverriddenMembers(result, type, cancellationToken);
+
+ // Retain overridable methods
+ AddOverridableMembers(result, containingType, type, cancellationToken);
+ }
+ // Don't suggest already overridden members
+ RemoveOverriddenMembers(result, containingType, cancellationToken);
+ }
+ }
+
+ // Filter based on accessibility
+ if (seenAccessibility != Accessibility.NotApplicable)
+ {
+ result.RemoveWhere(m => m.DeclaredAccessibility != seenAccessibility);
+ }
+
+ overridableMembers = result;
+ return overridableMembers.Count > 0;
+ }
+
+ static void AddOverridableMembers(HashSet<ISymbol> result, INamedTypeSymbol containingType, INamedTypeSymbol type, CancellationToken cancellationToken)
+ {
+ foreach (var member in type.GetMembers())
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ if (IsOverridable(member, containingType))
+ {
+ result.Add(member);
+ }
+ }
+ }
+
+ protected static void RemoveOverriddenMembers(HashSet<ISymbol> result, INamedTypeSymbol containingType, CancellationToken cancellationToken)
+ {
+ foreach (var member in containingType.GetMembers())
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ var overriddenMember = member.OverriddenMember();
+ if (overriddenMember != null)
+ {
+ result.Remove(overriddenMember);
+ }
+ }
+ }
+
+
+ static bool TryCheckForTrailingTokens (SyntaxTree tree, SourceText text, int startLineNumber, int position, CancellationToken cancellationToken)
+ {
+ var root = tree.GetRoot (cancellationToken);
+ var token = root.FindToken (position);
+
+ // Don't want to offer Override completion if there's a token after the current
+ // position.
+ if (token.SpanStart > position) {
+ return false;
+ }
+
+ // If the next token is also on our line then we don't want to offer completion.
+ if (IsOnStartLine (text, startLineNumber, token.GetNextToken ().SpanStart)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ static bool IsOnStartLine (SourceText text, int startLineNumber, int position)
+ {
+ return text.Lines.IndexOf (position) == startLineNumber;
+ }
+
+ static bool TryDetermineModifiers(ref SyntaxToken startToken, SourceText text, int startLine, out Accessibility seenAccessibility/*, out DeclarationModifiers modifiers*/)
+ {
+ var token = startToken;
+ //modifiers = new DeclarationModifiers();
+ seenAccessibility = Accessibility.NotApplicable;
+ var overrideToken = default(SyntaxToken);
+ bool isUnsafe = false;
+ bool isSealed = false;
+ bool isAbstract = false;
+
+ while (IsOnStartLine(token.SpanStart, text, startLine) && !token.IsKind(SyntaxKind.None))
+ {
+ switch (token.Kind())
+ {
+ case SyntaxKind.UnsafeKeyword:
+ isUnsafe = true;
+ break;
+ case SyntaxKind.OverrideKeyword:
+ overrideToken = token;
+ break;
+ case SyntaxKind.SealedKeyword:
+ isSealed = true;
+ break;
+ case SyntaxKind.AbstractKeyword:
+ isAbstract = true;
+ break;
+ case SyntaxKind.ExternKeyword:
+ break;
+
+ // Filter on the most recently typed accessibility; keep the first one we see
+ case SyntaxKind.PublicKeyword:
+ if (seenAccessibility == Accessibility.NotApplicable)
+ {
+ seenAccessibility = Accessibility.Public;
+ }
+
+ break;
+ case SyntaxKind.InternalKeyword:
+ if (seenAccessibility == Accessibility.NotApplicable)
+ {
+ seenAccessibility = Accessibility.Internal;
+ }
+
+ // If we see internal AND protected, filter for protected internal
+ if (seenAccessibility == Accessibility.Protected)
+ {
+ seenAccessibility = Accessibility.ProtectedOrInternal;
+ }
+
+ break;
+ case SyntaxKind.ProtectedKeyword:
+ if (seenAccessibility == Accessibility.NotApplicable)
+ {
+ seenAccessibility = Accessibility.Protected;
+ }
+
+ // If we see protected AND internal, filter for protected internal
+ if (seenAccessibility == Accessibility.Internal)
+ {
+ seenAccessibility = Accessibility.ProtectedOrInternal;
+ }
+
+ break;
+ default:
+ // Anything else and we bail.
+ return false;
+ }
+
+ var previousToken = token.GetPreviousToken();
+
+ // We want only want to consume modifiers
+ if (previousToken.IsKind(SyntaxKind.None) || !IsOnStartLine(previousToken.SpanStart, text, startLine))
+ {
+ break;
+ }
+
+ token = previousToken;
+ }
+
+ startToken = token;
+ /* modifiers = new DeclarationModifiers ()
+ .WithIsUnsafe (isUnsafe)
+ .WithIsAbstract (isAbstract)
+ .WithIsOverride (true)
+ .WithIsSealed (isSealed);*/
+ return overrideToken.IsKind(SyntaxKind.OverrideKeyword) && IsOnStartLine(overrideToken.Parent.SpanStart, text, startLine);
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/PartialContextHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/PartialContextHandler.cs
new file mode 100644
index 0000000000..b9bf4bef0d
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/PartialContextHandler.cs
@@ -0,0 +1,178 @@
+//
+// PartialContextHandler.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.Linq;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CodeGeneration;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.CodeAnalysis.CSharp;
+using System;
+
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ class PartialContextHandler : CompletionContextHandler
+ {
+ public override bool IsTriggerCharacter (SourceText text, int position)
+ {
+ return IsTriggerAfterSpaceOrStartOfWordCharacter (text, position);
+ }
+
+ protected async override Task<IEnumerable<CompletionData>> GetItemsWorkerAsync (CompletionResult completionResult, CompletionEngine engine, CompletionContext completionContext, CompletionTriggerInfo info, CancellationToken cancellationToken)
+ {
+ var document = completionContext.Document;
+ var position = completionContext.Position;
+ var tree = await document.GetSyntaxTreeAsync (cancellationToken).ConfigureAwait (false);
+
+ //DeclarationModifiers modifiers;
+ SyntaxToken token;
+
+ var semanticModel = await document.GetSemanticModelAsync (cancellationToken).ConfigureAwait (false);
+ var enclosingSymbol = semanticModel.GetEnclosingSymbol (position, cancellationToken) as INamedTypeSymbol;
+
+ // Only inside classes and structs
+ if (enclosingSymbol == null || !(enclosingSymbol.TypeKind == TypeKind.Struct || enclosingSymbol.TypeKind == TypeKind.Class)) {
+ return Enumerable.Empty<CompletionData> ();
+ }
+
+ if (!IsPartialCompletionContext (tree, position, cancellationToken/*, out modifiers*/, out token)) {
+ if (enclosingSymbol != null && (token.IsKind (SyntaxKind.OpenBraceToken) || token.IsKind (SyntaxKind.CloseBraceToken) || token.IsKind (SyntaxKind.SemicolonToken))) {
+ return CreateCompletionData (engine, semanticModel, position, enclosingSymbol, token, false, cancellationToken);
+ }
+ return Enumerable.Empty<CompletionData> ();
+ }
+
+ return CreateCompletionData (engine, semanticModel, position, enclosingSymbol, token, true, cancellationToken);
+ }
+
+ protected virtual IEnumerable<CompletionData> CreateCompletionData (CompletionEngine engine, SemanticModel semanticModel, int position, INamedTypeSymbol enclosingType, SyntaxToken token, bool afterPartialKeyword, CancellationToken cancellationToken)
+ {
+ var symbols = semanticModel.LookupSymbols(position, container: enclosingType)
+ .OfType<IMethodSymbol>()
+ .Where(m => IsPartial(m) && m.PartialImplementationPart == null);
+
+ var list = new List<CompletionData> ();
+
+ var declarationBegin = afterPartialKeyword ? token.Parent.SpanStart : position - 1;
+ foreach (var m in symbols) {
+ var data = engine.Factory.CreatePartialCompletionData (
+ this,
+ declarationBegin,
+ enclosingType,
+ m,
+ afterPartialKeyword
+ );
+ list.Add (data);
+ }
+ return list;
+ }
+
+ static bool IsPartial(IMethodSymbol m)
+ {
+ if (m.DeclaredAccessibility != Accessibility.NotApplicable &&
+ m.DeclaredAccessibility != Accessibility.Private)
+ {
+ return false;
+ }
+
+ if (!m.ReturnsVoid)
+ {
+ return false;
+ }
+
+ if (m.IsVirtual)
+ {
+ return false;
+ }
+
+ var declarations = m.DeclaringSyntaxReferences.Select(r => r.GetSyntax()).OfType<MethodDeclarationSyntax>();
+ return declarations.Any(d => d.Body == null && d.Modifiers.Any(SyntaxKind.PartialKeyword));
+ }
+
+ static bool IsPartialCompletionContext(SyntaxTree tree, int position, CancellationToken cancellationToken, /*out DeclarationModifiers modifiers, */out SyntaxToken token)
+ {
+ var touchingToken = tree.FindTokenOnLeftOfPosition(position, cancellationToken);
+ var targetToken = touchingToken.GetPreviousTokenIfTouchingWord(position);
+ var text = tree.GetText(cancellationToken);
+
+ token = targetToken;
+
+ //modifiers = default(DeclarationModifiers);
+
+ if (targetToken.IsKind(SyntaxKind.VoidKeyword, SyntaxKind.PartialKeyword) ||
+ (targetToken.Kind() == SyntaxKind.IdentifierToken && targetToken.HasMatchingText(SyntaxKind.PartialKeyword)))
+ {
+ return !IsOnSameLine (touchingToken.GetNextToken (), touchingToken, text) &&
+ VerifyModifiers (tree, position, cancellationToken/*, out modifiers*/);
+ }
+
+ return false;
+ }
+
+ static bool VerifyModifiers(SyntaxTree tree, int position, CancellationToken cancellationToken/*, out DeclarationModifiers modifiers*/)
+ {
+ var touchingToken = tree.FindTokenOnLeftOfPosition(position, cancellationToken);
+ var token = touchingToken.GetPreviousToken();
+
+ bool foundPartial = touchingToken.IsKindOrHasMatchingText(SyntaxKind.PartialKeyword);
+ bool foundAsync = false;
+
+ while (IsOnSameLine(token, touchingToken, tree.GetText(cancellationToken)))
+ {
+ if (token.IsKind(SyntaxKind.ExternKeyword, SyntaxKind.PublicKeyword, SyntaxKind.ProtectedKeyword, SyntaxKind.InternalKeyword))
+ {
+ //modifiers = default(DeclarationModifiers);
+ return false;
+ }
+
+ if (token.IsKindOrHasMatchingText(SyntaxKind.AsyncKeyword))
+ {
+ foundAsync = true;
+ }
+
+ foundPartial = foundPartial || token.IsKindOrHasMatchingText(SyntaxKind.PartialKeyword);
+
+ token = token.GetPreviousToken();
+ }
+
+ /*modifiers = new DeclarationModifiers()
+ .WithPartial(true)
+ .WithAsync (foundAsync);*/
+ return foundPartial;
+ }
+
+ static bool IsOnSameLine(SyntaxToken syntaxToken, SyntaxToken touchingToken, SourceText text)
+ {
+ return !syntaxToken.IsKind(SyntaxKind.None)
+ && !touchingToken.IsKind(SyntaxKind.None)
+ && text.Lines.IndexOf(syntaxToken.SpanStart) == text.Lines.IndexOf(touchingToken.SpanStart);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/PreProcessorExpressionContextHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/PreProcessorExpressionContextHandler.cs
new file mode 100644
index 0000000000..281f3994e2
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/PreProcessorExpressionContextHandler.cs
@@ -0,0 +1,59 @@
+//
+// PreProcessorExpressionContextHandler.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.Threading.Tasks;
+using System.Collections.Generic;
+using System.Threading;
+using System.Linq;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ class PreProcessorExpressionContextHandler : CompletionContextHandler
+ {
+ public override bool IsTriggerCharacter (Microsoft.CodeAnalysis.Text.SourceText text, int position)
+ {
+ return IsTriggerAfterSpaceOrStartOfWordCharacter (text, position);
+ }
+
+ protected async override Task<IEnumerable<CompletionData>> GetItemsWorkerAsync (CompletionResult completionResult, CompletionEngine engine, CompletionContext completionContext, CompletionTriggerInfo info, CancellationToken cancellationToken)
+ {
+ var ctx = await completionContext.GetSyntaxContextAsync (engine.Workspace, cancellationToken).ConfigureAwait (false);
+ var model = await completionContext.GetSemanticModelAsync (cancellationToken).ConfigureAwait (false);
+
+ var result = new List<CompletionData> ();
+ if (ctx.IsPreProcessorExpressionContext) {
+ var parseOptions = model.SyntaxTree.Options as CSharpParseOptions;
+ foreach (var define in parseOptions.PreprocessorSymbolNames) {
+ result.Add(engine.Factory.CreateGenericData (this, define, GenericDataType.PreprocessorSymbol));
+ }
+ }
+ return result;
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/RoslynRecommendationsCompletionContextHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/RoslynRecommendationsCompletionContextHandler.cs
new file mode 100644
index 0000000000..7ba672d567
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/RoslynRecommendationsCompletionContextHandler.cs
@@ -0,0 +1,178 @@
+//
+// RoslynRecommendationsCompletionContextHandler.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2014 Xamarin Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.CodeAnalysis.Recommendations;
+using Microsoft.CodeAnalysis;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp;
+
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.Text;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+
+ // public class CompletionEngineCache
+ // {
+ // public List<INamespace> namespaces;
+ // public ICompletionData[] importCompletion;
+ // }
+
+ class RoslynRecommendationsCompletionContextHandler : CompletionContextHandler
+ {
+ public override bool IsTriggerCharacter (SourceText text, int position)
+ {
+ var ch = text [position];
+ return ch == '.' ||
+ ch == ' ' && position >= 1 && !char.IsWhiteSpace (text [position - 1]) ||
+ ch == '#' || // pre processor directives
+ ch == '>' && position >= 1 && text [position - 1] == '-' || // pointer member access
+ ch == ':' && position >= 1 && text [position - 1] == ':' || // alias name
+ IsStartingNewWord (text, position);
+ }
+
+ bool IsException (ITypeSymbol type)
+ {
+ if (type == null)
+ return false;
+ if (type.Name == "Exception" && type.ContainingNamespace.Name == "System")
+ return true;
+ return IsException (type.BaseType);
+ }
+
+ protected async override Task<IEnumerable<CompletionData>> GetItemsWorkerAsync (CompletionResult completionResult, CompletionEngine engine, CompletionContext completionContext, CompletionTriggerInfo info, CancellationToken cancellationToken)
+ {
+ var ctx = await completionContext.GetSyntaxContextAsync (engine.Workspace, cancellationToken).ConfigureAwait (false);
+ var semanticModel = await completionContext.GetSemanticModelAsync (cancellationToken).ConfigureAwait (false);
+ var result = new List<CompletionData> ();
+ if (info.TriggerCharacter == ' ') {
+ var newExpression = ObjectCreationContextHandler.GetObjectCreationNewExpression (ctx.SyntaxTree, completionContext.Position, cancellationToken);
+ if (newExpression == null && info.CompletionTriggerReason == CompletionTriggerReason.CharTyped && !ctx.LeftToken.IsKind (SyntaxKind.EqualsToken) && !ctx.LeftToken.IsKind (SyntaxKind.EqualsEqualsToken))
+ return Enumerable.Empty<CompletionData> ();
+
+ completionResult.AutoCompleteEmptyMatch = false;
+ }
+
+ var parent = ctx.TargetToken.Parent;
+ bool isInAttribute = ctx.CSharpSyntaxContext.IsAttributeNameContext;
+ bool isInBaseList = parent != null && parent.IsKind (SyntaxKind.BaseList);
+ bool isInUsingDirective = parent != null && parent.Parent != null && parent.Parent.IsKind (SyntaxKind.UsingDirective) && !parent.IsKind (SyntaxKind.QualifiedName);
+ var isInQuery = ctx.CSharpSyntaxContext.IsInQuery;
+ var completionDataLookup = new Dictionary<Tuple<string, SymbolKind>, ISymbolCompletionData> ();
+ bool isInCatchTypeExpression = parent.IsKind (SyntaxKind.CatchDeclaration) ||
+ parent.IsKind (SyntaxKind.QualifiedName) && parent != null && parent.Parent.IsKind (SyntaxKind.CatchDeclaration);
+ Action<ISymbolCompletionData> addData = d => {
+ var key = Tuple.Create (d.DisplayText, d.Symbol.Kind);
+ ISymbolCompletionData data;
+ if (completionDataLookup.TryGetValue (key, out data)) {
+ data.AddOverload (d);
+ return;
+ }
+ completionDataLookup.Add (key, d);
+ result.Add (d);
+ };
+
+ var completionCategoryLookup = new Dictionary<string, ICompletionCategory> ();
+ foreach (var symbol in Recommender.GetRecommendedSymbolsAtPosition (semanticModel, completionContext.Position, engine.Workspace, null, cancellationToken)) {
+ if (symbol.Kind == SymbolKind.NamedType) {
+ if (isInAttribute) {
+ var type = (ITypeSymbol)symbol;
+ if (type.IsAttribute ()) {
+ var v = type.Name.Substring (0, type.Name.Length - "Attribute".Length);
+ var needsEscaping = SyntaxFacts.GetKeywordKind(v) != SyntaxKind.None;
+ needsEscaping = needsEscaping || (isInQuery && SyntaxFacts.IsQueryContextualKeyword(SyntaxFacts.GetContextualKeywordKind(v)));
+ if (!needsEscaping) {
+ addData (engine.Factory.CreateSymbolCompletionData (this, symbol, v));
+ continue;
+ }
+ }
+ }
+ if (isInBaseList) {
+ var type = (ITypeSymbol)symbol;
+ if (type.IsSealed || type.IsStatic)
+ continue;
+ }
+ if (isInCatchTypeExpression) {
+ var type = (ITypeSymbol)symbol;
+ if (!IsException (type))
+ continue;
+ }
+ }
+
+ if (isInUsingDirective && symbol.Kind != SymbolKind.Namespace)
+ continue;
+
+ var newData = engine.Factory.CreateSymbolCompletionData (this, symbol, symbol.Name.EscapeIdentifier (isInQuery));
+ var categorySymbol = (ISymbol)symbol.ContainingType ?? symbol.ContainingNamespace;
+ if (categorySymbol != null) {
+ ICompletionCategory category;
+ var key = categorySymbol.ToDisplayString ();
+ if (!completionCategoryLookup.TryGetValue (key, out category)) {
+ completionCategoryLookup [key] = category = engine.Factory.CreateCompletionDataCategory (categorySymbol);
+ }
+ newData.CompletionCategory = category;
+ }
+ addData (newData);
+ }
+ return result;
+ }
+
+ protected override async Task<bool> IsSemanticTriggerCharacterAsync(Document document, int characterPosition, CancellationToken cancellationToken)
+ {
+ bool? result = await IsTriggerOnDotAsync(document, characterPosition, cancellationToken).ConfigureAwait(false);
+ if (result.HasValue)
+ {
+ return result.Value;
+ }
+
+ return true;
+ }
+
+ private async Task<bool?> IsTriggerOnDotAsync(Document document, int characterPosition, CancellationToken cancellationToken)
+ {
+ var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
+ if (text[characterPosition] != '.')
+ {
+ return null;
+ }
+
+ // don't want to trigger after a number. All other cases after dot are ok.
+ var tree = await document.GetCSharpSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
+ var token = tree.FindToken(characterPosition);
+ if (token.Kind() == SyntaxKind.DotToken)
+ {
+ token = token.GetPreviousToken();
+ }
+
+ return token.Kind() != SyntaxKind.NumericLiteralToken;
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/SenderCompletionContextHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/SenderCompletionContextHandler.cs
new file mode 100644
index 0000000000..045672e662
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/SenderCompletionContextHandler.cs
@@ -0,0 +1,122 @@
+//
+// SenderCompletionContextHandler.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.Threading.Tasks;
+using System.Collections.Generic;
+using System.Threading;
+using System.Linq;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ public class SenderCompletionContextHandler : CompletionContextHandler
+ {
+ protected async override Task<IEnumerable<CompletionData>> GetItemsWorkerAsync (CompletionResult completionResult, CompletionEngine engine, CompletionContext completionContext, CompletionTriggerInfo info, CancellationToken cancellationToken)
+ {
+ var position = completionContext.Position;
+ var document = completionContext.Document;
+ var ctx = await completionContext.GetSyntaxContextAsync (engine.Workspace, cancellationToken).ConfigureAwait (false);
+ var syntaxTree = ctx.SyntaxTree;
+ if (syntaxTree.IsInNonUserCode(position, cancellationToken) ||
+ syntaxTree.IsPreProcessorDirectiveContext(position, cancellationToken))
+ return Enumerable.Empty<CompletionData> ();
+ if (!syntaxTree.IsRightOfDotOrArrowOrColonColon(position, cancellationToken))
+ return Enumerable.Empty<CompletionData> ();
+ var ma = ctx.LeftToken.Parent as MemberAccessExpressionSyntax;
+ if (ma == null)
+ return Enumerable.Empty<CompletionData> ();
+
+ var model = ctx.CSharpSyntaxContext.SemanticModel;
+
+ var symbolInfo = model.GetSymbolInfo (ma.Expression);
+ if (symbolInfo.Symbol == null || symbolInfo.Symbol.Kind != SymbolKind.Parameter)
+ return Enumerable.Empty<CompletionData> ();
+ var list = new List<CompletionData> ();
+ var within = model.GetEnclosingNamedTypeOrAssembly(position, cancellationToken);
+ var addedSymbols = new HashSet<string> ();
+
+ foreach (var ano in ma.AncestorsAndSelf ().OfType<AnonymousMethodExpressionSyntax> ()) {
+ Analyze (engine, model, ma.Expression, within, list, ano.ParameterList, symbolInfo.Symbol, addedSymbols, cancellationToken);
+ }
+
+ foreach (var ano in ma.AncestorsAndSelf ().OfType<ParenthesizedLambdaExpressionSyntax> ()) {
+ Analyze (engine, model, ma.Expression, within, list, ano.ParameterList, symbolInfo.Symbol, addedSymbols, cancellationToken);
+ }
+
+ return list;
+ }
+
+ void Analyze (CompletionEngine engine,SemanticModel model, SyntaxNode node, ISymbol within, List<CompletionData> list, ParameterListSyntax parameterList, ISymbol symbol, HashSet<string> addedSymbols, CancellationToken cancellationToken)
+ {
+ var type = CheckParameterList (model, parameterList, symbol, cancellationToken);
+ if (type == null)
+ return;
+ var startType = type;
+
+ while (type.SpecialType != SpecialType.System_Object) {
+ foreach (var member in type.GetMembers ()) {
+ if (member.IsImplicitlyDeclared || member.IsStatic)
+ continue;
+ if (member.IsOrdinaryMethod () || member.Kind == SymbolKind.Field || member.Kind == SymbolKind.Property) {
+ if (member.IsAccessibleWithin (within)) {
+ var completionData = engine.Factory.CreateCastCompletionData(this, member, node, startType);
+ if (addedSymbols.Contains (completionData.DisplayText))
+ continue;
+ addedSymbols.Add (completionData.DisplayText);
+ list.Add (completionData);
+ }
+ }
+ }
+
+ type = type.BaseType;
+ }
+ }
+
+ static ITypeSymbol CheckParameterList (SemanticModel model, ParameterListSyntax listSyntax, ISymbol parameter, CancellationToken cancellationToken)
+ {
+ var param = listSyntax.Parameters.FirstOrDefault ();
+ if (param == null)
+ return null;
+ var declared = model.GetDeclaredSymbol (param, cancellationToken);
+ if (declared != parameter)
+ return null;
+ var assignmentExpr = listSyntax.Parent.Parent as AssignmentExpressionSyntax;
+ if (assignmentExpr == null || !assignmentExpr.IsKind (SyntaxKind.AddAssignmentExpression))
+ return null;
+ var left = assignmentExpr.Left as MemberAccessExpressionSyntax;
+ if (left == null)
+ return null;
+ var symbolInfo = model.GetSymbolInfo (left.Expression);
+ if (symbolInfo.Symbol == null || symbolInfo.Symbol is ITypeSymbol)
+ return null;
+ return model.GetTypeInfo (left.Expression).Type;
+ }
+ }
+}
+
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/SnippetContextHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/SnippetContextHandler.cs
new file mode 100644
index 0000000000..1dce7bbb1e
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/SnippetContextHandler.cs
@@ -0,0 +1,104 @@
+//
+// SnippetContextHandler.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.Threading.Tasks;
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using System.Linq;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ class SnippetContextHandler : CompletionContextHandler
+ {
+ protected async override Task<IEnumerable<CompletionData>> GetItemsWorkerAsync (CompletionResult completionResult, CompletionEngine engine, CompletionContext completionContext, CompletionTriggerInfo info, CancellationToken cancellationToken)
+ {
+ var document = completionContext.Document;
+ var position = completionContext.Position;
+
+ var syntaxTree = await document.GetCSharpSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
+
+ if (syntaxTree.IsInNonUserCode(position, cancellationToken) ||
+ syntaxTree.IsRightOfDotOrArrowOrColonColon(position, cancellationToken) ||
+ syntaxTree.GetContainingTypeOrEnumDeclaration(position, cancellationToken) is EnumDeclarationSyntax)
+ {
+ return Enumerable.Empty<CompletionData>();
+ }
+
+ // var span = new TextSpan(position, 0);
+// var semanticModel = await document.GetCSharpSemanticModelForSpanAsync(span, cancellationToken).ConfigureAwait(false);
+ if (syntaxTree.IsPreProcessorDirectiveContext(position, cancellationToken))
+ {
+ var directive = syntaxTree.GetRoot(cancellationToken).FindTokenOnLeftOfPosition(position, includeDirectives: true).GetAncestor<DirectiveTriviaSyntax>();
+ if (directive.DirectiveNameToken.IsKind(
+ SyntaxKind.IfKeyword,
+ SyntaxKind.RegionKeyword,
+ SyntaxKind.ElseKeyword,
+ SyntaxKind.ElifKeyword,
+ SyntaxKind.ErrorKeyword,
+ SyntaxKind.LineKeyword,
+ SyntaxKind.PragmaKeyword,
+ SyntaxKind.EndIfKeyword,
+ SyntaxKind.UndefKeyword,
+ SyntaxKind.EndRegionKeyword,
+ SyntaxKind.WarningKeyword))
+ {
+ return Enumerable.Empty<CompletionData>();
+ }
+
+ return await GetSnippetCompletionItemsAsync(cancellationToken).ConfigureAwait(false);
+ }
+ var tokenLeftOfPosition = syntaxTree.FindTokenOnLeftOfPosition (position, cancellationToken);
+
+ if (syntaxTree.IsGlobalStatementContext(position, cancellationToken) ||
+ syntaxTree.IsExpressionContext(position, tokenLeftOfPosition, true, cancellationToken) ||
+ syntaxTree.IsStatementContext(position, tokenLeftOfPosition, cancellationToken) ||
+ syntaxTree.IsTypeContext(position, cancellationToken) ||
+ syntaxTree.IsTypeDeclarationContext(position, tokenLeftOfPosition, cancellationToken) ||
+ syntaxTree.IsNamespaceContext(position, cancellationToken) ||
+ syntaxTree.IsMemberDeclarationContext(position, tokenLeftOfPosition, cancellationToken) ||
+ syntaxTree.IsLabelContext(position, cancellationToken))
+ {
+ return await GetSnippetCompletionItemsAsync(cancellationToken).ConfigureAwait(false);
+ }
+
+ return Enumerable.Empty<CompletionData>();
+ }
+
+ Task<IEnumerable<CompletionData>> GetSnippetCompletionItemsAsync(CancellationToken cancellationToken)
+ {
+ if (CompletionEngine.SnippetCallback == null)
+ return Task.FromResult (Enumerable.Empty<CompletionData>());
+ return CompletionEngine.SnippetCallback (cancellationToken);
+ }
+
+ }
+}
+
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/SpeculativeNameContextHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/SpeculativeNameContextHandler.cs
new file mode 100644
index 0000000000..43f9795739
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/SpeculativeNameContextHandler.cs
@@ -0,0 +1,125 @@
+//
+// SpeculativeNameContextHandler.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.Threading.Tasks;
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using System.Text;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ class SpeculativeNameContextHandler : CompletionContextHandler
+ {
+ protected async override Task<IEnumerable<CompletionData>> GetItemsWorkerAsync (CompletionResult completionResult, CompletionEngine engine, CompletionContext completionContext, CompletionTriggerInfo info, CancellationToken cancellationToken)
+ {
+ var tree = await completionContext.Document.GetCSharpSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
+ if (tree.IsInNonUserCode(completionContext.Position, cancellationToken) ||
+ tree.IsPreProcessorDirectiveContext(completionContext.Position, cancellationToken) ||
+ info.CompletionTriggerReason != CompletionTriggerReason.CompletionCommand)
+ return Enumerable.Empty<CompletionData>();
+
+ var token = tree.FindTokenOnLeftOfPosition(completionContext.Position, cancellationToken);
+ var semanticModel = await completionContext.Document.GetSemanticModelAsync (cancellationToken).ConfigureAwait(false);
+ var parent = token.Parent.AncestorsAndSelf ().OfType<GenericNameSyntax> ().FirstOrDefault () ?? token.Parent;
+
+ if (!parent.Parent.IsKind (SyntaxKind.IncompleteMember) &&
+ !IsLocal(parent) &&
+ !parent.Parent.IsKind (SyntaxKind.Parameter) &&
+ !parent.Parent.IsKind (SyntaxKind.ForEachStatement)) {
+ return Enumerable.Empty<CompletionData>();
+ }
+
+ if (info.TriggerCharacter != ' ' &&
+ parent.Parent.IsKind (SyntaxKind.ExpressionStatement)) {
+ return Enumerable.Empty<CompletionData>();
+ }
+ var list = new List<CompletionData> ();
+
+ if (parent.IsKind(SyntaxKind.PredefinedType)) {
+ switch (token.Kind()) {
+ case SyntaxKind.ObjectKeyword:
+ list.Add (engine.Factory.CreateGenericData(this, "o", GenericDataType.NameProposal));
+ list.Add (engine.Factory.CreateGenericData(this, "obj", GenericDataType.NameProposal));
+ return list;
+ case SyntaxKind.BoolKeyword:
+ list.Add (engine.Factory.CreateGenericData(this, "b", GenericDataType.NameProposal));
+ list.Add (engine.Factory.CreateGenericData(this, "pred", GenericDataType.NameProposal));
+ return list;
+ case SyntaxKind.CharKeyword:
+ list.Add (engine.Factory.CreateGenericData(this, "c", GenericDataType.NameProposal));
+ list.Add (engine.Factory.CreateGenericData(this, "ch", GenericDataType.NameProposal));
+ return list;
+ case SyntaxKind.StringKeyword:
+ list.Add (engine.Factory.CreateGenericData(this, "str", GenericDataType.NameProposal));
+ return list;
+ case SyntaxKind.DoubleKeyword:
+ case SyntaxKind.FloatKeyword:
+ case SyntaxKind.DecimalKeyword:
+ list.Add (engine.Factory.CreateGenericData(this, "d", GenericDataType.NameProposal));
+ list.Add (engine.Factory.CreateGenericData(this, "f", GenericDataType.NameProposal));
+ list.Add (engine.Factory.CreateGenericData(this, "m", GenericDataType.NameProposal));
+ return list;
+ default:
+ list.Add (engine.Factory.CreateGenericData(this, "i", GenericDataType.NameProposal));
+ list.Add (engine.Factory.CreateGenericData(this, "j", GenericDataType.NameProposal));
+ list.Add (engine.Factory.CreateGenericData(this, "k", GenericDataType.NameProposal));
+ return list;
+ }
+ } else {
+ var gns = parent as GenericNameSyntax;
+ var names = WordParser.BreakWords (gns != null ? gns.Identifier.ToString () : token.ToString ().Trim ());
+ var possibleName = new StringBuilder ();
+ for (int i = 0; i < names.Count; i++) {
+ possibleName.Length = 0;
+ for (int j = i; j < names.Count; j++) {
+ if (string.IsNullOrEmpty (names [j])) {
+ continue;
+ }
+ if (j == i) {
+ names [j] = Char.ToLower (names [j] [0]) + names [j].Substring (1);
+ }
+ possibleName.Append (names [j]);
+ }
+ list.Add (engine.Factory.CreateGenericData (this, possibleName.ToString (), GenericDataType.NameProposal));
+ }
+ }
+ return list;
+ }
+
+ bool IsLocal (SyntaxNode tokenParent)
+ {
+ if ((tokenParent.IsKind (SyntaxKind.GenericName) || tokenParent.IsKind (SyntaxKind.IdentifierName) || tokenParent.IsKind (SyntaxKind.PredefinedType)) &&
+ (tokenParent.Parent.IsKind (SyntaxKind.ExpressionStatement) || tokenParent.Parent.IsKind (SyntaxKind.VariableDeclaration)))
+ return true;
+ return false;
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/SpeculativeTContextHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/SpeculativeTContextHandler.cs
new file mode 100644
index 0000000000..c4cce6d676
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/SpeculativeTContextHandler.cs
@@ -0,0 +1,103 @@
+//
+// SpeculativeTContextHandler.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.Threading.Tasks;
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using System.Linq;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ class SpeculativeTContextHandler : CompletionContextHandler
+ {
+ protected async override Task<IEnumerable<CompletionData>> GetItemsWorkerAsync (CompletionResult completionResult, CompletionEngine engine, CompletionContext completionContext, CompletionTriggerInfo info, CancellationToken cancellationToken)
+ {
+ var document = completionContext.Document;
+ var position = completionContext.Position;
+
+ return await document.GetUnionResultsFromDocumentAndLinks(
+ UnionCompletionItemComparer.Instance,
+ async (doc, ct) => await GetSpeculativeTCompletions(engine, doc, position, ct).ConfigureAwait(false),
+ cancellationToken).ConfigureAwait(false);
+ }
+
+ private async Task<IEnumerable<CompletionData>> GetSpeculativeTCompletions(CompletionEngine engine, Document document, int position, CancellationToken cancellationToken)
+ {
+ var syntaxTree = await document.GetCSharpSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
+ if (syntaxTree.IsInNonUserCode(position, cancellationToken) ||
+ syntaxTree.IsPreProcessorDirectiveContext(position, cancellationToken))
+ {
+ return Enumerable.Empty<CompletionData>();
+ }
+
+ // If we're in a generic type argument context, use the start of the generic type name
+ // as the position for the rest of the context checks.
+ int testPosition = position;
+ var leftToken = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken);
+
+ var semanticModel = await document.GetCSharpSemanticModelForNodeAsync(leftToken.Parent, cancellationToken).ConfigureAwait(false);
+ if (syntaxTree.IsGenericTypeArgumentContext(position, leftToken, cancellationToken, semanticModel))
+ {
+ // Walk out until we find the start of the partial written generic
+ SyntaxToken nameToken;
+ while (syntaxTree.IsInPartiallyWrittenGeneric(testPosition, cancellationToken, out nameToken))
+ {
+ testPosition = nameToken.SpanStart;
+ }
+
+ // If the user types Foo<T, automatic brace completion will insert the close brace
+ // and the generic won't be "partially written".
+ if (testPosition == position)
+ {
+ var typeArgumentList = leftToken.GetAncestor<TypeArgumentListSyntax>();
+ if (typeArgumentList != null)
+ {
+ if (typeArgumentList.LessThanToken != default(SyntaxToken) && typeArgumentList.GreaterThanToken != default(SyntaxToken))
+ {
+ testPosition = typeArgumentList.LessThanToken.SpanStart;
+ }
+ }
+ }
+ }
+
+ if ((!leftToken.GetPreviousTokenIfTouchingWord(position).IsKindOrHasMatchingText(SyntaxKind.AsyncKeyword) &&
+ syntaxTree.IsMemberDeclarationContext(testPosition, contextOpt: null, validModifiers: SyntaxKindSet.AllMemberModifiers, validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken)) ||
+ syntaxTree.IsGlobalMemberDeclarationContext(testPosition, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
+ syntaxTree.IsGlobalStatementContext(testPosition, cancellationToken) ||
+ syntaxTree.IsDelegateReturnTypeContext(testPosition, syntaxTree.FindTokenOnLeftOfPosition(testPosition, cancellationToken), cancellationToken))
+ {
+ const string T = "T";
+ return new [] { engine.Factory.CreateGenericData (this, T, GenericDataType.Undefined) };
+ }
+ return Enumerable.Empty<CompletionData>();
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/XmlDocCommentContextHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/XmlDocCommentContextHandler.cs
new file mode 100644
index 0000000000..ce1b89b827
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ContextHandler/XmlDocCommentContextHandler.cs
@@ -0,0 +1,399 @@
+//
+// XmlDocCommentContextHandler.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.Threading.Tasks;
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.Text;
+using System.Linq;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ class XmlDocCommentContextHandler : CompletionContextHandler
+ {
+ protected async override Task<IEnumerable<CompletionData>> GetItemsWorkerAsync (CompletionResult completionResult, CompletionEngine engine, CompletionContext completionContext, CompletionTriggerInfo info, CancellationToken cancellationToken)
+ {
+ if (info.IsDebugger)
+ {
+ return null;
+ }
+ var document = completionContext.Document;
+ var position = completionContext.Position;
+
+ var tree = await document.GetCSharpSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
+ var token = tree.FindTokenOnLeftOfPosition(position, cancellationToken);
+ var parentTrivia = token.GetAncestor<DocumentationCommentTriviaSyntax>();
+
+ if (parentTrivia == null)
+ {
+ return null;
+ }
+
+ var items = new List<CompletionData>();
+ var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
+ var span = GetTextChangeSpan(text, position);
+
+ var attachedToken = parentTrivia.ParentTrivia.Token;
+ if (attachedToken.Kind() == SyntaxKind.None)
+ {
+ return null;
+ }
+
+ var semanticModel = await document.GetCSharpSemanticModelForNodeAsync(attachedToken.Parent, cancellationToken).ConfigureAwait(false);
+
+ ISymbol declaredSymbol = null;
+ var memberDeclaration = attachedToken.GetAncestor<MemberDeclarationSyntax>();
+ if (memberDeclaration != null)
+ {
+ declaredSymbol = semanticModel.GetDeclaredSymbol(memberDeclaration, cancellationToken);
+ }
+ else
+ {
+ var typeDeclaration = attachedToken.GetAncestor<TypeDeclarationSyntax>();
+ if (typeDeclaration != null)
+ {
+ declaredSymbol = semanticModel.GetDeclaredSymbol(typeDeclaration, cancellationToken);
+ }
+ }
+
+ if (declaredSymbol != null)
+ {
+ items.AddRange(GetTagsForSymbol(engine, declaredSymbol, span, parentTrivia, token));
+ }
+
+ if (token.Parent.Kind() == SyntaxKind.XmlEmptyElement || token.Parent.Kind() == SyntaxKind.XmlText ||
+ (token.Parent.IsKind(SyntaxKind.XmlElementEndTag) && token.IsKind(SyntaxKind.GreaterThanToken)) ||
+ (token.Parent.IsKind(SyntaxKind.XmlName) && token.Parent.IsParentKind(SyntaxKind.XmlEmptyElement)))
+ {
+ if (token.Parent.Parent.Kind() == SyntaxKind.XmlElement)
+ {
+ items.AddRange(GetNestedTags(engine, span));
+ }
+
+ if (token.Parent.Parent.Kind() == SyntaxKind.XmlElement && ((XmlElementSyntax)token.Parent.Parent).StartTag.Name.LocalName.ValueText == "list")
+ {
+ items.AddRange(GetListItems(engine, span));
+ }
+
+ if (token.Parent.IsParentKind(SyntaxKind.XmlEmptyElement) & token.Parent.Parent.IsParentKind(SyntaxKind.XmlElement))
+ {
+ var element = (XmlElementSyntax)token.Parent.Parent.Parent;
+ if (element.StartTag.Name.LocalName.ValueText == "list")
+ {
+ items.AddRange(GetListItems(engine, span));
+ }
+ }
+
+ if (token.Parent.Parent.Kind() == SyntaxKind.XmlElement && ((XmlElementSyntax)token.Parent.Parent).StartTag.Name.LocalName.ValueText == "listheader")
+ {
+ items.AddRange(GetListHeaderItems(engine, span));
+ }
+
+ if (token.Parent.Parent is DocumentationCommentTriviaSyntax)
+ {
+ items.AddRange(GetTopLevelSingleUseNames(engine, parentTrivia, span));
+ items.AddRange(GetTopLevelRepeatableItems(engine, span));
+ }
+ }
+
+ if (token.Parent.Kind() == SyntaxKind.XmlElementStartTag)
+ {
+ var startTag = (XmlElementStartTagSyntax)token.Parent;
+
+ if (token == startTag.GreaterThanToken && startTag.Name.LocalName.ValueText == "list")
+ {
+ items.AddRange(GetListItems(engine, span));
+ }
+
+ if (token == startTag.GreaterThanToken && startTag.Name.LocalName.ValueText == "listheader")
+ {
+ items.AddRange(GetListHeaderItems(engine, span));
+ }
+ }
+
+ items.AddRange(GetAlwaysVisibleItems(engine, span));
+ return items;
+ }
+
+ public override bool IsCommitCharacter (CompletionData ICompletionData, char ch, string textTypedSoFar)
+ {
+ if ((ch == '"' || ch == ' ')
+ && ICompletionData.DisplayText.Contains(ch))
+ {
+ return false;
+ }
+
+ return base.IsCommitCharacter(ICompletionData, ch, textTypedSoFar) || ch == '>' || ch == '\t';
+ }
+
+ public override bool IsTriggerCharacter (SourceText text, int position)
+ {
+ return text[position] == '<';
+ }
+//
+// public override bool SendEnterThroughToEditor(ICompletionData ICompletionData, string textTypedSoFar)
+// {
+// return false;
+// }
+
+ private IEnumerable<CompletionData> GetTopLevelSingleUseNames(CompletionEngine engine, DocumentationCommentTriviaSyntax parentTrivia, TextSpan span)
+ {
+ var names = new HashSet<string>(new[] { "summary", "remarks", "example", "completionlist" });
+
+ RemoveExistingTags(parentTrivia, names, (x) => x.StartTag.Name.LocalName.ValueText);
+
+ return names.Select(n => GetItem(engine, n, span));
+ }
+
+ private void RemoveExistingTags(DocumentationCommentTriviaSyntax parentTrivia, ISet<string> names, Func<XmlElementSyntax, string> selector)
+ {
+ if (parentTrivia != null)
+ {
+ foreach (var node in parentTrivia.Content)
+ {
+ var element = node as XmlElementSyntax;
+ if (element != null)
+ {
+ names.Remove(selector(element));
+ }
+ }
+ }
+ }
+
+ private IEnumerable<CompletionData> GetTagsForSymbol(CompletionEngine engine, ISymbol symbol, TextSpan filterSpan, DocumentationCommentTriviaSyntax trivia, SyntaxToken token)
+ {
+ if (symbol is IMethodSymbol)
+ {
+ return GetTagsForMethod(engine, (IMethodSymbol)symbol, filterSpan, trivia, token);
+ }
+
+ if (symbol is IPropertySymbol)
+ {
+ return GetTagsForProperty(engine, (IPropertySymbol)symbol, filterSpan, trivia);
+ }
+
+ if (symbol is INamedTypeSymbol)
+ {
+ return GetTagsForType(engine, (INamedTypeSymbol)symbol, filterSpan, trivia);
+ }
+
+ return SpecializedCollections.EmptyEnumerable<CompletionData>();
+ }
+
+ private IEnumerable<CompletionData> GetTagsForType(CompletionEngine engine, INamedTypeSymbol symbol, TextSpan filterSpan, DocumentationCommentTriviaSyntax trivia)
+ {
+ var items = new List<CompletionData>();
+
+ var typeParameters = symbol.TypeParameters.Select(p => p.Name).ToSet();
+
+ RemoveExistingTags(trivia, typeParameters, x => AttributeSelector(x, "typeparam"));
+
+ items.AddRange(typeParameters.Select(t => engine.Factory.CreateXmlDocCompletionData (
+ this,
+ FormatParameter("typeparam", t))));
+ return items;
+ }
+
+ private string AttributeSelector(XmlElementSyntax element, string attribute)
+ {
+ if (!element.StartTag.IsMissing && !element.EndTag.IsMissing)
+ {
+ var startTag = element.StartTag;
+ var nameAttribute = startTag.Attributes.OfType<XmlNameAttributeSyntax>().FirstOrDefault(a => a.Name.LocalName.ValueText == "name");
+ if (nameAttribute != null)
+ {
+ if (startTag.Name.LocalName.ValueText == attribute)
+ {
+ return nameAttribute.Identifier.Identifier.ValueText;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private IEnumerable<CompletionData> GetTagsForProperty(CompletionEngine engine, IPropertySymbol symbol, TextSpan filterSpan, DocumentationCommentTriviaSyntax trivia)
+ {
+ var items = new List<CompletionData>();
+
+ var typeParameters = symbol.GetTypeArguments().Select(p => p.Name).ToSet();
+
+ RemoveExistingTags(trivia, typeParameters, x => AttributeSelector(x, "typeparam"));
+
+ items.AddRange(typeParameters.Select(t => engine.Factory.CreateXmlDocCompletionData(this, "typeparam", null, "name$" + t)));
+ items.Add(engine.Factory.CreateXmlDocCompletionData(this, "value"));
+ return items;
+ }
+
+ private IEnumerable<CompletionData> GetTagsForMethod(CompletionEngine engine, IMethodSymbol symbol, TextSpan filterSpan, DocumentationCommentTriviaSyntax trivia, SyntaxToken token)
+ {
+ var items = new List<CompletionData>();
+
+ var parameters = symbol.GetParameters().Select(p => p.Name).ToSet();
+ var typeParameters = symbol.TypeParameters.Select(t => t.Name).ToSet();
+
+ // User is trying to write a name, try to suggest only names.
+ if (token.Parent.IsKind(SyntaxKind.XmlNameAttribute) ||
+ (token.Parent.IsKind(SyntaxKind.IdentifierName) && token.Parent.IsParentKind(SyntaxKind.XmlNameAttribute)))
+ {
+ string parentElementName = null;
+
+ var emptyElement = token.GetAncestor<XmlEmptyElementSyntax>();
+ if (emptyElement != null)
+ {
+ parentElementName = emptyElement.Name.LocalName.Text;
+ }
+
+ // We're writing the name of a paramref or typeparamref
+ if (parentElementName == "paramref")
+ {
+ items.AddRange(parameters.Select(p => engine.Factory.CreateXmlDocCompletionData (this, p)));
+ }
+ else if (parentElementName == "typeparamref")
+ {
+ items.AddRange(typeParameters.Select(t => engine.Factory.CreateXmlDocCompletionData (this, t)));
+ }
+
+ return items;
+ }
+
+ var returns = true;
+
+ RemoveExistingTags(trivia, parameters, x => AttributeSelector(x, "param"));
+ RemoveExistingTags(trivia, typeParameters, x => AttributeSelector(x, "typeparam"));
+
+ foreach (var node in trivia.Content)
+ {
+ var element = node as XmlElementSyntax;
+ if (element != null && !element.StartTag.IsMissing && !element.EndTag.IsMissing)
+ {
+ var startTag = element.StartTag;
+
+ if (startTag.Name.LocalName.ValueText == "returns")
+ {
+ returns = false;
+ break;
+ }
+ }
+ }
+
+ items.AddRange(parameters.Select(p => engine.Factory.CreateXmlDocCompletionData (this, FormatParameter("param", p))));
+ items.AddRange(typeParameters.Select(t => engine.Factory.CreateXmlDocCompletionData (this, FormatParameter("typeparam", t))));
+
+ if (returns && !symbol.ReturnsVoid)
+ {
+ items.Add(engine.Factory.CreateXmlDocCompletionData (this, "returns"));
+ }
+
+ return items;
+ }
+
+
+ readonly Dictionary<string, string[]> _tagMap = new Dictionary<string, string[]> {
+ { "exception", new[] { "<exception cref=\"", "\"" } },
+ { "!--", new[] { "<!--", "-->" } },
+ { "![CDATA[", new[] { "<![CDATA[", "]]>" } },
+ { "include", new[] { "<include file=\'", "\' path=\'[@name=\"\"]\'/>" } },
+ { "permission", new[] { "<permission cref=\"", "\"" } },
+ { "see", new[] { "<see cref=\"", "\"/>" } },
+ { "seealso", new[] { "<seealso cref=\"", "\"/>" } },
+ { "list", new[] { "<list type=\"", "\"" } },
+ { "paramref", new[] { "<paramref name=\"", "\"/>" } },
+ { "typeparamref", new[] { "<typeparamref name=\"", "\"/>" } },
+ { "completionlist", new[] { "<completionlist cref=\"", "\"/>" } },
+ };
+
+ readonly string[][] _attributeMap = {
+ new [] { "exception", "cref", "cref=\"", "\"" },
+ new [] { "permission", "cref", "cref=\"", "\"" },
+ new [] { "see", "cref", "cref=\"", "\"" },
+ new [] { "seealso", "cref", "cref=\"", "\"" },
+ new [] { "list", "type", "type=\"", "\"" },
+ new [] { "param", "name", "name=\"", "\"" },
+ new [] { "include", "file", "file=\"", "\"" },
+ new [] { "include", "path", "path=\"", "\"" }
+ };
+
+ protected CompletionData GetItem(CompletionEngine engine, string n, TextSpan span)
+ {
+ if (_tagMap.ContainsKey(n))
+ {
+ var value = _tagMap[n];
+ return engine.Factory.CreateXmlDocCompletionData (this, n, null, value [0] + "$" + value [1]);
+ }
+ return engine.Factory.CreateXmlDocCompletionData (this, n);
+ }
+
+ protected IEnumerable<CompletionData> GetAttributeItem(CompletionEngine engine, string n, TextSpan span)
+ {
+ var items = _attributeMap.Where(x => x[0] == n).Select(x => engine.Factory.CreateXmlDocCompletionData(this, x[1],null, x[2] + "$" + x[3]));
+ if (items.Any ())
+ return items;
+
+ return new [] { engine.Factory.CreateXmlDocCompletionData (this, n) };
+ }
+
+ protected IEnumerable<CompletionData> GetAlwaysVisibleItems(CompletionEngine engine, TextSpan filterSpan)
+ {
+ return new[] { "see", "seealso", "![CDATA[", "!--" }
+ .Select(t => GetItem(engine, t, filterSpan));
+ }
+
+ protected IEnumerable<CompletionData> GetNestedTags(CompletionEngine engine, TextSpan filterSpan)
+ {
+ return new[] { "c", "code", "para", "list", "paramref", "typeparamref" }
+ .Select(t => GetItem(engine, t, filterSpan));
+ }
+
+ protected IEnumerable<CompletionData> GetTopLevelRepeatableItems(CompletionEngine engine, TextSpan filterSpan)
+ {
+ return new[] { "exception", "include", "permission" }
+ .Select(t => GetItem(engine, t, filterSpan));
+ }
+
+ protected IEnumerable<CompletionData> GetListItems(CompletionEngine engine, TextSpan span)
+ {
+ return new[] { "listheader", "term", "item", "description" }
+ .Select(t => GetItem(engine, t, span));
+ }
+
+ protected IEnumerable<CompletionData> GetListHeaderItems(CompletionEngine engine, TextSpan span)
+ {
+ return new[] { "term", "description" }
+ .Select(t => GetItem(engine, t, span));
+ }
+
+ protected string FormatParameter(string kind, string name)
+ {
+ return string.Format("{0} name=\"{1}\"", kind, name);
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/DisplayFlags.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/DisplayFlags.cs
new file mode 100644
index 0000000000..f910d187f7
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/DisplayFlags.cs
@@ -0,0 +1,42 @@
+//
+// DisplayFlags.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2011 Xamarin Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ [Flags]
+ public enum DisplayFlags
+ {
+ None = 0,
+ Hidden = 1,
+ Obsolete = 2,
+ DescriptionHasMarkup = 4,
+ NamedArgument = 8,
+ IsImportCompletion = 16,
+ MarkedBold = 32
+ }
+}
+
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/EditorBrowsableBehavior.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/EditorBrowsableBehavior.cs
new file mode 100644
index 0000000000..07e261a383
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/EditorBrowsableBehavior.cs
@@ -0,0 +1,35 @@
+//
+// EditorBrowsableBehavior.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2014 Xamarin Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ public enum EditorBrowsableBehavior
+ {
+ Ignore,
+ Normal,
+ IncludeAdvanced
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ICompletionCategory.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ICompletionCategory.cs
new file mode 100644
index 0000000000..6b613941ea
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ICompletionCategory.cs
@@ -0,0 +1,37 @@
+//
+// CompletionCategory.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2011 Xamarin Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ public interface ICompletionCategory
+ {
+ string DisplayText { get; set; }
+
+ string Icon { get; set; }
+ }
+}
+
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ICompletionDataFactory.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ICompletionDataFactory.cs
new file mode 100644
index 0000000000..3f3585b49f
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ICompletionDataFactory.cs
@@ -0,0 +1,77 @@
+//
+// ICompletionDataFactory.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2011 Xamarin Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System.Collections.Generic;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ public enum GenericDataType
+ {
+ AttributeTarget,
+ Undefined,
+ Keyword,
+ PreprocessorKeyword,
+ PreprocessorSymbol,
+ NameProposal,
+ NamedParameter
+ }
+
+ public interface ICompletionDataFactory
+ {
+ CompletionData CreateGenericData (ICompletionKeyHandler keyHandler, string data, GenericDataType genericDataType = GenericDataType.Undefined);
+
+ CompletionData CreateFormatItemCompletionData (ICompletionKeyHandler keyHandler, string format, string description, object example);
+
+ CompletionData CreateXmlDocCompletionData (ICompletionKeyHandler keyHandler, string tag, string description = null, string tagInsertionText = null);
+
+ ISymbolCompletionData CreateSymbolCompletionData (ICompletionKeyHandler keyHandler, ISymbol symbol);
+ ISymbolCompletionData CreateSymbolCompletionData (ICompletionKeyHandler keyHandler, ISymbol symbol, string text);
+
+ /// <summary>
+ /// Creates enum member completion data.
+ /// Form: Type.Member
+ /// Used for generating enum members Foo.A, Foo.B where the enum 'Foo' is valid.
+ /// </summary>
+ ISymbolCompletionData CreateEnumMemberCompletionData (ICompletionKeyHandler keyHandler, ISymbol typeAlias, IFieldSymbol field);
+
+ CompletionData CreateNewOverrideCompletionData (ICompletionKeyHandler keyHandler, int declarationBegin, ITypeSymbol currentType, ISymbol m, bool afterKeyword);
+
+ CompletionData CreatePartialCompletionData (ICompletionKeyHandler keyHandler, int declarationBegin, ITypeSymbol currentType, IMethodSymbol method, bool afterKeyword);
+
+ /// <summary>
+ /// Creates the event creation completion data.
+ /// </summary>
+ CompletionData CreateNewMethodDelegate (ICompletionKeyHandler keyHandler, ITypeSymbol delegateType, string varName, INamedTypeSymbol curType);
+
+ CompletionData CreateAnonymousMethod (ICompletionKeyHandler keyHandler, string displayText, string description, string textBeforeCaret, string textAfterCaret);
+
+ CompletionData CreateObjectCreation (ICompletionKeyHandler keyHandler, ITypeSymbol typeToCreate, ISymbol symbol, int declarationBegin, bool afterKeyword);
+
+ CompletionData CreateCastCompletionData (ICompletionKeyHandler keyHandler, ISymbol member, SyntaxNode nodeToCast, ITypeSymbol targetType);
+
+ ICompletionCategory CreateCompletionDataCategory (ISymbol forSymbol);
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ICompletionKeyHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ICompletionKeyHandler.cs
new file mode 100644
index 0000000000..953516095b
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/ICompletionKeyHandler.cs
@@ -0,0 +1,56 @@
+//
+// ICompletionKeyHandler.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 Microsoft.CodeAnalysis.Text;
+using MonoDevelop.Ide.CodeCompletion;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ public interface ICompletionKeyHandler
+ {
+ /// <summary>
+ /// Returns true if the character typed should be used to filter the specified completion
+ /// item. A character will be checked to see if it should filter an item. If not, it will be
+ /// checked to see if it should commit that item. If it does neither, then completion will
+ /// be dismissed.
+ /// </summary>
+ bool IsFilterCharacter(CompletionData completionItem, char ch, string textTypedSoFar);
+
+ /// <summary>
+ /// Returns true if the character is one that can commit the specified completion item. A
+ /// character will be checked to see if it should filter an item. If not, it will be checked
+ /// to see if it should commit that item. If it does neither, then completion will be
+ /// dismissed.
+ /// </summary>
+ bool IsCommitCharacter(CompletionData completionItem, char ch, string textTypedSoFar);
+
+ /// <summary>
+ /// Returns true if the enter key that was typed should also be sent through to the editor
+ /// after committing the provided completion item.
+ /// </summary>
+ bool SendEnterThroughToEditor(CompletionData completionItem, string textTypedSoFar);
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AbstractKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AbstractKeywordRecommender.cs
new file mode 100644
index 0000000000..1cf1e8e40a
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AbstractKeywordRecommender.cs
@@ -0,0 +1,55 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class AbstractKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ private static readonly ISet<SyntaxKind> s_validMemberModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.ExternKeyword,
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.NewKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.ProtectedKeyword,
+ SyntaxKind.UnsafeKeyword,
+ SyntaxKind.OverrideKeyword,
+ };
+
+ private static readonly ISet<SyntaxKind> s_validTypeModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.NewKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.PrivateKeyword,
+ SyntaxKind.ProtectedKeyword,
+ SyntaxKind.UnsafeKeyword
+ };
+
+ public AbstractKeywordRecommender()
+ : base(SyntaxKind.AbstractKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsGlobalStatementContext ||
+ context.IsMemberDeclarationContext(
+ validModifiers: s_validMemberModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassOnlyTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken) ||
+ context.IsTypeDeclarationContext(
+ validModifiers: s_validTypeModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AbstractSyntacticSingleKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AbstractSyntacticSingleKeywordRecommender.cs
new file mode 100644
index 0000000000..584dd1d9b8
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AbstractSyntacticSingleKeywordRecommender.cs
@@ -0,0 +1,79 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal abstract partial class AbstractSyntacticSingleKeywordRecommender : IKeywordRecommender<CSharpSyntaxContext>
+ {
+ private readonly bool _isValidInPreprocessorContext;
+
+ protected internal SyntaxKind KeywordKind { get; private set; }
+
+ internal bool ShouldFormatOnCommit { get; private set; }
+
+ protected AbstractSyntacticSingleKeywordRecommender(
+ SyntaxKind keywordKind,
+ bool isValidInPreprocessorContext = false,
+ bool shouldFormatOnCommit = false)
+ {
+ this.KeywordKind = keywordKind;
+ _isValidInPreprocessorContext = isValidInPreprocessorContext;
+ this.ShouldFormatOnCommit = shouldFormatOnCommit;
+ }
+
+ protected abstract bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken);
+
+ public IEnumerable<RecommendedKeyword> RecommendKeywords(
+ int position,
+ CSharpSyntaxContext context,
+ CancellationToken cancellationToken)
+ {
+ var syntaxKind = this.RecommendKeyword(position, context, cancellationToken);
+ if (syntaxKind.HasValue)
+ {
+ return new [] {
+ new RecommendedKeyword(SyntaxFacts.GetText(syntaxKind.Value), shouldFormatOnCommit: this.ShouldFormatOnCommit)
+ };
+ }
+
+ return null;
+ }
+
+ internal IEnumerable<RecommendedKeyword> RecommendKeywords_Test(int position, CSharpSyntaxContext context)
+ {
+ var syntaxKind = this.RecommendKeyword(position, context, CancellationToken.None);
+ if (syntaxKind.HasValue)
+ {
+ return new [] {
+ new RecommendedKeyword(SyntaxFacts.GetText(syntaxKind.Value))
+ };
+ }
+
+ return null;
+ }
+
+ private SyntaxKind? RecommendKeyword(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ // NOTE: The collector ensures that we're not in "NonUserCode" like comments, strings, inactive code
+ // for perf reasons.
+ var syntaxTree = context.SemanticModel.SyntaxTree;
+ if (!_isValidInPreprocessorContext &&
+ context.IsPreProcessorDirectiveContext)
+ {
+ return null;
+ }
+
+ if (!IsValidContext(position, context, cancellationToken))
+ {
+ return null;
+ }
+
+ return this.KeywordKind;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AddKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AddKeywordRecommender.cs
new file mode 100644
index 0000000000..672e0502a2
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AddKeywordRecommender.cs
@@ -0,0 +1,22 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class AddKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public AddKeywordRecommender()
+ : base(SyntaxKind.AddKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return context.TargetToken.IsAccessorDeclarationContext<EventDeclarationSyntax>(position, SyntaxKind.AddKeyword);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AliasKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AliasKeywordRecommender.cs
new file mode 100644
index 0000000000..3d6b87302a
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AliasKeywordRecommender.cs
@@ -0,0 +1,36 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class AliasKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public AliasKeywordRecommender()
+ : base(SyntaxKind.AliasKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ // cases:
+ // extern |
+ // extern a|
+ var token = context.TargetToken;
+
+ if (token.Kind() == SyntaxKind.ExternKeyword)
+ {
+ // members can be 'extern' but we don't want
+ // 'alias' to show up in a 'type'.
+ return token.GetAncestor<TypeDeclarationSyntax>() == null;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AsKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AsKeywordRecommender.cs
new file mode 100644
index 0000000000..3d667239aa
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AsKeywordRecommender.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class AsKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public AsKeywordRecommender()
+ : base(SyntaxKind.AsKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return !context.IsInNonUserCode && context.IsIsOrAsContext;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AscendingKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AscendingKeywordRecommender.cs
new file mode 100644
index 0000000000..ec3e54f007
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AscendingKeywordRecommender.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class AscendingKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public AscendingKeywordRecommender()
+ : base(SyntaxKind.AscendingKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return context.TargetToken.IsOrderByDirectionContext();
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AssemblyKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AssemblyKeywordRecommender.cs
new file mode 100644
index 0000000000..31cb8b3a47
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AssemblyKeywordRecommender.cs
@@ -0,0 +1,32 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class AssemblyKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public AssemblyKeywordRecommender()
+ : base(SyntaxKind.AssemblyKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ if (context.IsTypeAttributeContext(cancellationToken))
+ {
+ var token = context.LeftToken;
+ var type = token.GetAncestor<MemberDeclarationSyntax>();
+
+ return type == null || type.IsParentKind(SyntaxKind.CompilationUnit);
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AsyncKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AsyncKeywordRecommender.cs
new file mode 100644
index 0000000000..49abcb325d
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AsyncKeywordRecommender.cs
@@ -0,0 +1,39 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class AsyncKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public AsyncKeywordRecommender() :
+ base(SyntaxKind.AsyncKeyword, isValidInPreprocessorContext: false)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ if (context.IsAnyExpressionContext)
+ {
+ return true;
+ }
+
+ return !context.TargetToken.IsKindOrHasMatchingText(SyntaxKind.PartialKeyword)
+ && InMemberDeclarationContext(position, context, cancellationToken);
+ }
+
+ private static bool InMemberDeclarationContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return context.SyntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken)
+ || context.IsMemberDeclarationContext(
+ validModifiers: SyntaxKindSet.AllMemberModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassStructTypeDeclarations,
+ canBePartial: true,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AwaitKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AwaitKeywordRecommender.cs
new file mode 100644
index 0000000000..c2604eaad6
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/AwaitKeywordRecommender.cs
@@ -0,0 +1,54 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class AwaitKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public AwaitKeywordRecommender()
+ : base(SyntaxKind.AwaitKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ if (context.IsAnyExpressionContext || context.IsStatementContext)
+ {
+ foreach (var node in context.LeftToken.GetAncestors<SyntaxNode>())
+ {
+ if (node.IsAnyLambdaOrAnonymousMethod())
+ {
+ return true;
+ }
+
+ if (node.IsKind(SyntaxKind.QueryExpression))
+ {
+ return false;
+ }
+
+ if (node.IsKind(SyntaxKind.LockStatement))
+ {
+ var lockStatement = (LockStatementSyntax)node;
+ if (lockStatement.Statement != null &&
+ !lockStatement.Statement.IsMissing &&
+ lockStatement.Statement.Span.Contains(position))
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/BaseKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/BaseKeywordRecommender.cs
new file mode 100644
index 0000000000..68206249d8
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/BaseKeywordRecommender.cs
@@ -0,0 +1,69 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Symbols;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class BaseKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public BaseKeywordRecommender()
+ : base(SyntaxKind.BaseKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ // We need to at least be in a type declaration context. This prevents us from showing
+ // calls to 'base' in things like top level repl statements and whatnot.
+ if (context.ContainingTypeDeclaration != null)
+ {
+ return
+ IsConstructorInitializerContext(position, context, cancellationToken) ||
+ IsInstanceExpressionOrStatement(context);
+ }
+
+ return false;
+ }
+
+ private static bool IsInstanceExpressionOrStatement(CSharpSyntaxContext context)
+ {
+ if (context.IsInstanceContext)
+ {
+ return context.IsNonAttributeExpressionContext || context.IsStatementContext;
+ }
+
+ return false;
+ }
+
+ private bool IsConstructorInitializerContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ // cases:
+ // Foo() : |
+
+ var token = context.TargetToken;
+
+ if (token.Kind() == SyntaxKind.ColonToken &&
+ token.Parent is ConstructorInitializerSyntax &&
+ token.Parent.IsParentKind(SyntaxKind.ConstructorDeclaration) &&
+ token.Parent.GetParent().IsParentKind(SyntaxKind.ClassDeclaration))
+ {
+ var constructor = token.GetAncestor<ConstructorDeclarationSyntax>();
+ if (constructor.Modifiers.Any(t => t.Kind () == SyntaxKind.StaticKeyword))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/BoolKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/BoolKeywordRecommender.cs
new file mode 100644
index 0000000000..44c5b2322f
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/BoolKeywordRecommender.cs
@@ -0,0 +1,47 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class BoolKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public BoolKeywordRecommender()
+ : base(SyntaxKind.BoolKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ return
+ context.IsAnyExpressionContext ||
+ context.IsDefiniteCastTypeContext ||
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ context.IsObjectCreationTypeContext ||
+ context.IsGenericTypeArgumentContext ||
+ context.IsIsOrAsTypeContext ||
+ context.IsLocalVariableDeclarationContext ||
+ context.IsFixedVariableDeclarationContext ||
+ context.IsParameterTypeContext ||
+ context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext ||
+ context.IsImplicitOrExplicitOperatorTypeContext ||
+ context.IsPrimaryFunctionExpressionContext ||
+ context.IsCrefContext ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.StackAllocKeyword, cancellationToken) ||
+ context.IsDelegateReturnTypeContext ||
+ syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(
+ validModifiers: SyntaxKindSet.AllMemberModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/BreakKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/BreakKeywordRecommender.cs
new file mode 100644
index 0000000000..8275ac1434
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/BreakKeywordRecommender.cs
@@ -0,0 +1,54 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class BreakKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public BreakKeywordRecommender()
+ : base(SyntaxKind.BreakKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ IsInBreakableConstructContext(context) ||
+ context.TargetToken.IsAfterYieldKeyword();
+ }
+
+ private static bool IsInBreakableConstructContext(CSharpSyntaxContext context)
+ {
+ if (!context.IsStatementContext)
+ {
+ return false;
+ }
+
+ // allowed if we're inside a loop/switch construct.
+
+ var token = context.LeftToken;
+ foreach (var v in token.GetAncestors<SyntaxNode>())
+ {
+ if (v.IsAnyLambdaOrAnonymousMethod())
+ {
+ // if we hit a lambda while walking up, then we can't
+ // 'continue' any outer loops.
+ return false;
+ }
+
+ if (v.IsBreakableConstruct())
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ByKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ByKeywordRecommender.cs
new file mode 100644
index 0000000000..07d5db338e
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ByKeywordRecommender.cs
@@ -0,0 +1,53 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class ByKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public ByKeywordRecommender()
+ : base(SyntaxKind.ByKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ // cases:
+ // group e |
+ // group e b|
+
+ var token = context.LeftToken;
+ var group = token.GetAncestor<GroupClauseSyntax>();
+
+ if (group == null)
+ {
+ return false;
+ }
+
+ var lastToken = group.GroupExpression.GetLastToken(includeSkipped: true);
+
+ // group e |
+ if (!token.IntersectsWith(position) &&
+ token == lastToken)
+ {
+ return true;
+ }
+
+ // group e b|
+ if (token.IntersectsWith(position) &&
+ token.Kind() == SyntaxKind.IdentifierToken &&
+ token.GetPreviousToken(includeSkipped: true) == lastToken)
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ByteKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ByteKeywordRecommender.cs
new file mode 100644
index 0000000000..c40eb00cd7
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ByteKeywordRecommender.cs
@@ -0,0 +1,49 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class ByteKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public ByteKeywordRecommender()
+ : base(SyntaxKind.ByteKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+
+ return
+ context.IsAnyExpressionContext ||
+ context.IsDefiniteCastTypeContext ||
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ context.IsObjectCreationTypeContext ||
+ context.IsGenericTypeArgumentContext ||
+ context.IsEnumBaseListContext ||
+ context.IsIsOrAsTypeContext ||
+ context.IsLocalVariableDeclarationContext ||
+ context.IsFixedVariableDeclarationContext ||
+ context.IsParameterTypeContext ||
+ context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext ||
+ context.IsImplicitOrExplicitOperatorTypeContext ||
+ context.IsPrimaryFunctionExpressionContext ||
+ context.IsCrefContext ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.StackAllocKeyword, cancellationToken) ||
+ context.IsDelegateReturnTypeContext ||
+ syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(
+ validModifiers: SyntaxKindSet.AllMemberModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/CaseKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/CaseKeywordRecommender.cs
new file mode 100644
index 0000000000..c8e9c0f1db
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/CaseKeywordRecommender.cs
@@ -0,0 +1,41 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class CaseKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public CaseKeywordRecommender()
+ : base(SyntaxKind.CaseKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.TargetToken.IsSwitchLabelContext() ||
+ IsAfterGotoInSwitchContext(context);
+ }
+
+ internal static bool IsAfterGotoInSwitchContext(CSharpSyntaxContext context)
+ {
+ var token = context.TargetToken;
+
+ if (token.Kind() == SyntaxKind.GotoKeyword &&
+ token.GetAncestor<SwitchStatementSyntax>() != null)
+ {
+ // todo: what if we're in a lambda... or a try/finally or
+ // something? Might want to filter this out.
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/CatchKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/CatchKeywordRecommender.cs
new file mode 100644
index 0000000000..26433adf28
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/CatchKeywordRecommender.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class CatchKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public CatchKeywordRecommender()
+ : base(SyntaxKind.CatchKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return context.SyntaxTree.IsCatchOrFinallyContext(position, context.LeftToken, cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/CharKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/CharKeywordRecommender.cs
new file mode 100644
index 0000000000..a0d530742c
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/CharKeywordRecommender.cs
@@ -0,0 +1,47 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class CharKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public CharKeywordRecommender()
+ : base(SyntaxKind.CharKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ return
+ context.IsAnyExpressionContext ||
+ context.IsDefiniteCastTypeContext ||
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ context.IsObjectCreationTypeContext ||
+ context.IsGenericTypeArgumentContext ||
+ context.IsIsOrAsTypeContext ||
+ context.IsLocalVariableDeclarationContext ||
+ context.IsFixedVariableDeclarationContext ||
+ context.IsParameterTypeContext ||
+ context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext ||
+ context.IsImplicitOrExplicitOperatorTypeContext ||
+ context.IsPrimaryFunctionExpressionContext ||
+ context.IsCrefContext ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.StackAllocKeyword, cancellationToken) ||
+ context.IsDelegateReturnTypeContext ||
+ syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(
+ validModifiers: SyntaxKindSet.AllMemberModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/CheckedKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/CheckedKeywordRecommender.cs
new file mode 100644
index 0000000000..bdcb72853d
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/CheckedKeywordRecommender.cs
@@ -0,0 +1,24 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class CheckedKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public CheckedKeywordRecommender()
+ : base(SyntaxKind.CheckedKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ context.IsNonAttributeExpressionContext;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ChecksumKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ChecksumKeywordRecommender.cs
new file mode 100644
index 0000000000..14aceee892
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ChecksumKeywordRecommender.cs
@@ -0,0 +1,28 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class ChecksumKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public ChecksumKeywordRecommender()
+ : base(SyntaxKind.ChecksumKeyword, isValidInPreprocessorContext: true)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ // # pragma |
+ // # pragma w|
+ var previousToken1 = context.TargetToken;
+ var previousToken2 = previousToken1.GetPreviousToken(includeSkipped: true);
+
+ return
+ previousToken1.Kind() == SyntaxKind.PragmaKeyword &&
+ previousToken2.Kind() == SyntaxKind.HashToken;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ClassKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ClassKeywordRecommender.cs
new file mode 100644
index 0000000000..47a255c363
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ClassKeywordRecommender.cs
@@ -0,0 +1,44 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class ClassKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ private static readonly ISet<SyntaxKind> s_validModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.NewKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.ProtectedKeyword,
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.PrivateKeyword,
+ SyntaxKind.AbstractKeyword,
+ SyntaxKind.SealedKeyword,
+ SyntaxKind.StaticKeyword,
+ SyntaxKind.UnsafeKeyword
+ };
+
+ public ClassKeywordRecommender()
+ : base(SyntaxKind.ClassKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ return
+ context.IsGlobalStatementContext ||
+ context.IsTypeDeclarationContext(
+ validModifiers: s_validModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassStructTypeDeclarations,
+ canBePartial: true,
+ cancellationToken: cancellationToken) ||
+ syntaxTree.IsTypeParameterConstraintStartContext(position, context.LeftToken, cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ConstKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ConstKeywordRecommender.cs
new file mode 100644
index 0000000000..49710fe6ed
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ConstKeywordRecommender.cs
@@ -0,0 +1,65 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class ConstKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ private static readonly ISet<SyntaxKind> s_validModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.NewKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.ProtectedKeyword,
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.PrivateKeyword,
+ };
+
+ private static readonly ISet<SyntaxKind> s_validGlobalModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.NewKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.PrivateKeyword,
+ };
+
+ public ConstKeywordRecommender()
+ : base(SyntaxKind.ConstKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ IsMemberDeclarationContext(context, cancellationToken) ||
+ IsLocalVariableDeclaration(context);
+ }
+
+ private bool IsMemberDeclarationContext(CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.SyntaxTree.IsGlobalMemberDeclarationContext(context.Position, s_validGlobalModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(
+ validModifiers: s_validModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+
+ private bool IsLocalVariableDeclaration(CSharpSyntaxContext context)
+ {
+ // cases:
+ // void Foo() {
+ // |
+ //
+ // |
+ return
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ContinueKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ContinueKeywordRecommender.cs
new file mode 100644
index 0000000000..e2f1aeb73b
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ContinueKeywordRecommender.cs
@@ -0,0 +1,47 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class ContinueKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public ContinueKeywordRecommender()
+ : base(SyntaxKind.ContinueKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ if (!context.IsStatementContext)
+ {
+ return false;
+ }
+
+ // allowed if we're inside a loop construct.
+
+ var leaf = context.LeftToken;
+ foreach (var v in leaf.GetAncestors<SyntaxNode>())
+ {
+ if (v.IsAnyLambdaOrAnonymousMethod())
+ {
+ // if we hit a lambda while walking up, then we can't
+ // 'continue' any outer loops.
+ return false;
+ }
+
+ if (v.IsContinuableConstruct())
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DecimalKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DecimalKeywordRecommender.cs
new file mode 100644
index 0000000000..e0280702fb
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DecimalKeywordRecommender.cs
@@ -0,0 +1,47 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class DecimalKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public DecimalKeywordRecommender()
+ : base(SyntaxKind.DecimalKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ return
+ context.IsAnyExpressionContext ||
+ context.IsDefiniteCastTypeContext ||
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ context.IsObjectCreationTypeContext ||
+ context.IsGenericTypeArgumentContext ||
+ context.IsIsOrAsTypeContext ||
+ context.IsLocalVariableDeclarationContext ||
+ context.IsFixedVariableDeclarationContext ||
+ context.IsParameterTypeContext ||
+ context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext ||
+ context.IsImplicitOrExplicitOperatorTypeContext ||
+ context.IsPrimaryFunctionExpressionContext ||
+ context.IsCrefContext ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.StackAllocKeyword, cancellationToken) ||
+ context.IsDelegateReturnTypeContext ||
+ syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(
+ validModifiers: SyntaxKindSet.AllMemberModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DefaultKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DefaultKeywordRecommender.cs
new file mode 100644
index 0000000000..935b5b62eb
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DefaultKeywordRecommender.cs
@@ -0,0 +1,42 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class DefaultKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public DefaultKeywordRecommender()
+ : base(SyntaxKind.DefaultKeyword, isValidInPreprocessorContext: true)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ IsValidPreProcessorContext(context) ||
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ context.IsAnyExpressionContext ||
+ context.TargetToken.IsSwitchLabelContext();
+ }
+
+ private static bool IsValidPreProcessorContext(CSharpSyntaxContext context)
+ {
+ // cases:
+ // #line |
+ // #line d|
+ // # line |
+ // # line d|
+
+ var previousToken1 = context.TargetToken;
+ var previousToken2 = previousToken1.GetPreviousToken(includeSkipped: true);
+
+ return
+ previousToken1.Kind() == SyntaxKind.LineKeyword &&
+ previousToken2.Kind() == SyntaxKind.HashToken;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DefineKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DefineKeywordRecommender.cs
new file mode 100644
index 0000000000..5ff0f4412a
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DefineKeywordRecommender.cs
@@ -0,0 +1,25 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class DefineKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public DefineKeywordRecommender()
+ : base(SyntaxKind.DefineKeyword, isValidInPreprocessorContext: true)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ return
+ syntaxTree.IsBeforeFirstToken(position, cancellationToken) &&
+ context.IsPreProcessorKeywordContext;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DelegateKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DelegateKeywordRecommender.cs
new file mode 100644
index 0000000000..42049a0b93
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DelegateKeywordRecommender.cs
@@ -0,0 +1,53 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class DelegateKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ private static readonly ISet<SyntaxKind> s_validModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.PrivateKeyword,
+ SyntaxKind.ProtectedKeyword,
+ SyntaxKind.UnsafeKeyword
+ };
+
+ public DelegateKeywordRecommender()
+ : base(SyntaxKind.DelegateKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsGlobalStatementContext ||
+ (context.IsNonAttributeExpressionContext && !context.IsConstantExpressionContext) ||
+ IsAfterAsyncKeywordInExpressionContext(context, cancellationToken) ||
+ context.IsTypeDeclarationContext(
+ validModifiers: s_validModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+
+ private static bool IsAfterAsyncKeywordInExpressionContext(CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.TargetToken.IsKindOrHasMatchingText(SyntaxKind.AsyncKeyword) &&
+ context.SyntaxTree.IsExpressionContext(
+ context.TargetToken.SpanStart,
+ context.TargetToken,
+ attributes: false,
+ cancellationToken: cancellationToken,
+ semanticModelOpt: context.SemanticModel);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DescendingKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DescendingKeywordRecommender.cs
new file mode 100644
index 0000000000..0c62897af0
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DescendingKeywordRecommender.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class DescendingKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public DescendingKeywordRecommender()
+ : base(SyntaxKind.DescendingKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return context.TargetToken.IsOrderByDirectionContext();
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DisableKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DisableKeywordRecommender.cs
new file mode 100644
index 0000000000..87e5166b5d
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DisableKeywordRecommender.cs
@@ -0,0 +1,30 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class DisableKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public DisableKeywordRecommender()
+ : base(SyntaxKind.DisableKeyword, isValidInPreprocessorContext: true)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ // # pragma warning |
+ // # pragma warning d|
+ var previousToken1 = context.TargetToken;
+ var previousToken2 = previousToken1.GetPreviousToken(includeSkipped: true);
+ var previousToken3 = previousToken2.GetPreviousToken(includeSkipped: true);
+
+ return
+ previousToken1.Kind() == SyntaxKind.WarningKeyword &&
+ previousToken2.Kind() == SyntaxKind.PragmaKeyword &&
+ previousToken3.Kind() == SyntaxKind.HashToken;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DoKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DoKeywordRecommender.cs
new file mode 100644
index 0000000000..ca525b4868
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DoKeywordRecommender.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class DoKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public DoKeywordRecommender()
+ : base(SyntaxKind.DoKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DoubleKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DoubleKeywordRecommender.cs
new file mode 100644
index 0000000000..51330c929c
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DoubleKeywordRecommender.cs
@@ -0,0 +1,47 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class DoubleKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public DoubleKeywordRecommender()
+ : base(SyntaxKind.DoubleKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ return
+ context.IsAnyExpressionContext ||
+ context.IsDefiniteCastTypeContext ||
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ context.IsObjectCreationTypeContext ||
+ context.IsGenericTypeArgumentContext ||
+ context.IsIsOrAsTypeContext ||
+ context.IsLocalVariableDeclarationContext ||
+ context.IsFixedVariableDeclarationContext ||
+ context.IsParameterTypeContext ||
+ context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext ||
+ context.IsImplicitOrExplicitOperatorTypeContext ||
+ context.IsPrimaryFunctionExpressionContext ||
+ context.IsCrefContext ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.StackAllocKeyword, cancellationToken) ||
+ context.IsDelegateReturnTypeContext ||
+ syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(
+ validModifiers: SyntaxKindSet.AllMemberModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DynamicKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DynamicKeywordRecommender.cs
new file mode 100644
index 0000000000..1a6c2bf517
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/DynamicKeywordRecommender.cs
@@ -0,0 +1,64 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Roslyn.Utilities;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class DynamicKeywordRecommender : IKeywordRecommender<CSharpSyntaxContext>
+ {
+ private bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ if (context.IsPreProcessorDirectiveContext)
+ {
+ return false;
+ }
+
+ return IsDynamicTypeContext(position, context, cancellationToken);
+ }
+
+ public IEnumerable<RecommendedKeyword> RecommendKeywords(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ if (IsValidContext(position, context, cancellationToken))
+ {
+ yield return new RecommendedKeyword("dynamic");
+ }
+ }
+
+ protected static bool IsDynamicTypeContext(
+ int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+
+ // first do quick exit check
+ if (syntaxTree.IsDefinitelyNotTypeContext(position, cancellationToken))
+ {
+ return false;
+ }
+
+ return
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ context.IsDefiniteCastTypeContext ||
+ syntaxTree.IsPossibleCastTypeContext(position, context.LeftToken, cancellationToken) ||
+ context.IsObjectCreationTypeContext ||
+ context.IsGenericTypeArgumentContext ||
+ context.IsIsOrAsTypeContext ||
+ syntaxTree.IsDefaultExpressionContext(position, context.LeftToken, cancellationToken) ||
+ context.IsLocalVariableDeclarationContext ||
+ context.IsParameterTypeContext ||
+ context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext ||
+ context.IsDelegateReturnTypeContext ||
+ syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(
+ validModifiers: SyntaxKindSet.AllMemberModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ElifKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ElifKeywordRecommender.cs
new file mode 100644
index 0000000000..0d2b5d21b9
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ElifKeywordRecommender.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class ElifKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public ElifKeywordRecommender()
+ : base(SyntaxKind.ElifKeyword, isValidInPreprocessorContext: true)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return context.IsPreProcessorKeywordContext;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ElseKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ElseKeywordRecommender.cs
new file mode 100644
index 0000000000..9c6acbd43d
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ElseKeywordRecommender.cs
@@ -0,0 +1,63 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class ElseKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public ElseKeywordRecommender()
+ : base(SyntaxKind.ElseKeyword, isValidInPreprocessorContext: true)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ if (context.IsPreProcessorKeywordContext)
+ {
+ return true;
+ }
+
+ var token = context.TargetToken;
+
+ var statement = token.GetAncestor<StatementSyntax>();
+ var ifStatement = statement.GetAncestorOrThis<IfStatementSyntax>();
+
+ if (statement == null || ifStatement == null)
+ {
+ return false;
+ }
+
+ // cases:
+ // if (foo)
+ // Console.WriteLine();
+ // |
+ // if (foo)
+ // Console.WriteLine();
+ // e|
+ if (token.IsKind(SyntaxKind.SemicolonToken) && ifStatement.Statement.GetLastToken(includeSkipped: true) == token)
+ {
+ return true;
+ }
+
+ // if (foo) {
+ // Console.WriteLine();
+ // } |
+ // if (foo) {
+ // Console.WriteLine();
+ // } e|
+ if (token.IsKind(SyntaxKind.CloseBraceToken) && ifStatement.Statement is BlockSyntax && token == ((BlockSyntax)ifStatement.Statement).CloseBraceToken)
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/EndIfKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/EndIfKeywordRecommender.cs
new file mode 100644
index 0000000000..0042a43660
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/EndIfKeywordRecommender.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class EndIfKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public EndIfKeywordRecommender()
+ : base(SyntaxKind.EndIfKeyword, isValidInPreprocessorContext: true)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return context.IsPreProcessorKeywordContext;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/EndRegionKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/EndRegionKeywordRecommender.cs
new file mode 100644
index 0000000000..741c814181
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/EndRegionKeywordRecommender.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class EndRegionKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public EndRegionKeywordRecommender()
+ : base(SyntaxKind.EndRegionKeyword, isValidInPreprocessorContext: true, shouldFormatOnCommit: true)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return context.IsPreProcessorKeywordContext;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/EnumKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/EnumKeywordRecommender.cs
new file mode 100644
index 0000000000..69d31fad97
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/EnumKeywordRecommender.cs
@@ -0,0 +1,37 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class EnumKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ private static readonly ISet<SyntaxKind> s_validModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.PrivateKeyword,
+ SyntaxKind.ProtectedKeyword,
+ };
+
+ public EnumKeywordRecommender()
+ : base(SyntaxKind.EnumKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsGlobalStatementContext ||
+ context.IsTypeDeclarationContext(
+ validModifiers: s_validModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/EqualsKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/EqualsKeywordRecommender.cs
new file mode 100644
index 0000000000..e2f4bd912a
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/EqualsKeywordRecommender.cs
@@ -0,0 +1,45 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class EqualsKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public EqualsKeywordRecommender()
+ : base(SyntaxKind.EqualsKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ // cases:
+ // join a in expr o1 |
+ // join a in expr o1 e|
+
+ var token = context.TargetToken;
+
+ var join = token.GetAncestor<JoinClauseSyntax>();
+ if (join == null)
+ {
+ return false;
+ }
+
+ var lastToken = join.LeftExpression.GetLastToken(includeSkipped: true);
+
+ // join a in expr |
+ if (join.LeftExpression.Width() > 0 &&
+ token == lastToken)
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ErrorKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ErrorKeywordRecommender.cs
new file mode 100644
index 0000000000..5e21f268ae
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ErrorKeywordRecommender.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class ErrorKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public ErrorKeywordRecommender()
+ : base(SyntaxKind.ErrorKeyword, isValidInPreprocessorContext: true)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return context.IsPreProcessorKeywordContext;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/EventKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/EventKeywordRecommender.cs
new file mode 100644
index 0000000000..04c955265e
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/EventKeywordRecommender.cs
@@ -0,0 +1,44 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class EventKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ private static readonly ISet<SyntaxKind> s_validModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.NewKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.ProtectedKeyword,
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.PrivateKeyword,
+ SyntaxKind.StaticKeyword,
+ SyntaxKind.VirtualKeyword,
+ SyntaxKind.SealedKeyword,
+ SyntaxKind.OverrideKeyword,
+ SyntaxKind.AbstractKeyword,
+ SyntaxKind.ExternKeyword,
+ SyntaxKind.UnsafeKeyword
+ };
+
+ public EventKeywordRecommender()
+ : base(SyntaxKind.EventKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ return
+ context.IsGlobalStatementContext ||
+ syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(validModifiers: s_validModifiers, validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken) ||
+ context.IsMemberAttributeContext(SyntaxKindSet.ClassInterfaceStructTypeDeclarations, cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ExplicitKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ExplicitKeywordRecommender.cs
new file mode 100644
index 0000000000..7931912b56
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ExplicitKeywordRecommender.cs
@@ -0,0 +1,41 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class ExplicitKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ private static readonly ISet<SyntaxKind> s_validMemberModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.StaticKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.ExternKeyword,
+ SyntaxKind.UnsafeKeyword,
+ };
+
+ public ExplicitKeywordRecommender()
+ : base(SyntaxKind.ExplicitKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ if (context.IsMemberDeclarationContext(validModifiers: s_validMemberModifiers, validTypeDeclarations: SyntaxKindSet.ClassStructTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken))
+ {
+ // operators must be both public and static
+ var modifiers = context.PrecedingModifiers;
+
+ return
+ modifiers.Contains(SyntaxKind.PublicKeyword) &&
+ modifiers.Contains(SyntaxKind.StaticKeyword);
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ExternKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ExternKeywordRecommender.cs
new file mode 100644
index 0000000000..7c6f740ab9
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ExternKeywordRecommender.cs
@@ -0,0 +1,100 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class ExternKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ private static readonly ISet<SyntaxKind> s_validModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.NewKeyword,
+ SyntaxKind.OverrideKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.PrivateKeyword,
+ SyntaxKind.ProtectedKeyword,
+ SyntaxKind.SealedKeyword,
+ SyntaxKind.StaticKeyword,
+ SyntaxKind.UnsafeKeyword,
+ SyntaxKind.VirtualKeyword,
+ };
+
+ private static readonly ISet<SyntaxKind> s_validGlobalModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.NewKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.PrivateKeyword,
+ SyntaxKind.StaticKeyword,
+ SyntaxKind.UnsafeKeyword,
+ };
+
+ public ExternKeywordRecommender()
+ : base(SyntaxKind.ExternKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ return
+ IsExternAliasContext(context) ||
+ context.IsGlobalStatementContext ||
+ syntaxTree.IsGlobalMemberDeclarationContext(position, s_validGlobalModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(
+ validModifiers: s_validModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+
+ private static bool IsExternAliasContext(CSharpSyntaxContext context)
+ {
+ // cases:
+ // root: |
+
+ // root: e|
+
+ // extern alias a;
+ // |
+
+ // extern alias a;
+ // e|
+
+ // all the above, but inside a namespace.
+ // usings and other constructs *cannot* precede.
+
+ var token = context.TargetToken;
+
+ // root: |
+ if (token.Kind() == SyntaxKind.None)
+ {
+ // root namespace
+ return true;
+ }
+
+ if (token.Kind() == SyntaxKind.OpenBraceToken &&
+ token.Parent.IsKind(SyntaxKind.NamespaceDeclaration))
+ {
+ return true;
+ }
+
+ // extern alias a;
+ // |
+ if (token.Kind() == SyntaxKind.SemicolonToken &&
+ token.Parent.IsKind(SyntaxKind.ExternAliasDirective))
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/FalseKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/FalseKeywordRecommender.cs
new file mode 100644
index 0000000000..1fa404a42b
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/FalseKeywordRecommender.cs
@@ -0,0 +1,26 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class FalseKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public FalseKeywordRecommender()
+ : base(SyntaxKind.FalseKeyword, isValidInPreprocessorContext: true)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsAnyExpressionContext ||
+ context.IsPreProcessorExpressionContext ||
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ context.TargetToken.IsUnaryOperatorContext();
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/FieldKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/FieldKeywordRecommender.cs
new file mode 100644
index 0000000000..cb50cf176b
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/FieldKeywordRecommender.cs
@@ -0,0 +1,30 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class FieldKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ // interfaces don't have members that you can put a [field:] attribute on
+ private static readonly ISet<SyntaxKind> s_validTypeDeclarations = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.StructDeclaration,
+ SyntaxKind.ClassDeclaration,
+ SyntaxKind.EnumDeclaration,
+ };
+
+ public FieldKeywordRecommender()
+ : base(SyntaxKind.FieldKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return context.IsMemberAttributeContext(s_validTypeDeclarations, cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/FinallyKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/FinallyKeywordRecommender.cs
new file mode 100644
index 0000000000..0ac6a78e75
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/FinallyKeywordRecommender.cs
@@ -0,0 +1,22 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class FinallyKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public FinallyKeywordRecommender()
+ : base(SyntaxKind.FinallyKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ return syntaxTree.IsCatchOrFinallyContext(position, context.LeftToken, cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/FixedKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/FixedKeywordRecommender.cs
new file mode 100644
index 0000000000..371916e0a6
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/FixedKeywordRecommender.cs
@@ -0,0 +1,50 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class FixedKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ private static readonly ISet<SyntaxKind> s_validModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.NewKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.ProtectedKeyword,
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.PrivateKeyword,
+ SyntaxKind.UnsafeKeyword,
+ };
+
+ public FixedKeywordRecommender()
+ : base(SyntaxKind.FixedKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ IsUnsafeStatementContext(context) ||
+ IsMemberDeclarationContext(context, cancellationToken);
+ }
+
+ private static bool IsMemberDeclarationContext(CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.TargetToken.IsUnsafeContext() &&
+ (context.SyntaxTree.IsGlobalMemberDeclarationContext(context.Position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(validModifiers: s_validModifiers, validTypeDeclarations: SyntaxKindSet.StructOnlyTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken));
+ }
+
+ private static bool IsUnsafeStatementContext(CSharpSyntaxContext context)
+ {
+ return
+ context.TargetToken.IsUnsafeContext() &&
+ context.IsStatementContext;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/FloatKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/FloatKeywordRecommender.cs
new file mode 100644
index 0000000000..e5759bfcd8
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/FloatKeywordRecommender.cs
@@ -0,0 +1,47 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class FloatKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public FloatKeywordRecommender()
+ : base(SyntaxKind.FloatKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ return
+ context.IsAnyExpressionContext ||
+ context.IsDefiniteCastTypeContext ||
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ context.IsObjectCreationTypeContext ||
+ context.IsGenericTypeArgumentContext ||
+ context.IsIsOrAsTypeContext ||
+ context.IsLocalVariableDeclarationContext ||
+ context.IsFixedVariableDeclarationContext ||
+ context.IsParameterTypeContext ||
+ context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext ||
+ context.IsImplicitOrExplicitOperatorTypeContext ||
+ context.IsPrimaryFunctionExpressionContext ||
+ context.IsCrefContext ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.StackAllocKeyword, cancellationToken) ||
+ context.IsDelegateReturnTypeContext ||
+ syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(
+ validModifiers: SyntaxKindSet.AllMemberModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ForEachKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ForEachKeywordRecommender.cs
new file mode 100644
index 0000000000..f39dc09669
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ForEachKeywordRecommender.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class ForEachKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public ForEachKeywordRecommender()
+ : base(SyntaxKind.ForEachKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ForKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ForKeywordRecommender.cs
new file mode 100644
index 0000000000..6013d73f54
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ForKeywordRecommender.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class ForKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public ForKeywordRecommender()
+ : base(SyntaxKind.ForKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/FromKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/FromKeywordRecommender.cs
new file mode 100644
index 0000000000..c9db4eda7f
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/FromKeywordRecommender.cs
@@ -0,0 +1,24 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class FromKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public FromKeywordRecommender()
+ : base(SyntaxKind.FromKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ return
+ context.IsGlobalStatementContext ||
+ syntaxTree.IsValidContextForFromClause(position, context.LeftToken, cancellationToken, semanticModelOpt: context.SemanticModel);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/GetKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/GetKeywordRecommender.cs
new file mode 100644
index 0000000000..22fe43af57
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/GetKeywordRecommender.cs
@@ -0,0 +1,24 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class GetKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public GetKeywordRecommender()
+ : base(SyntaxKind.GetKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.TargetToken.IsAccessorDeclarationContext<PropertyDeclarationSyntax>(position, SyntaxKind.GetKeyword) ||
+ context.TargetToken.IsAccessorDeclarationContext<IndexerDeclarationSyntax>(position, SyntaxKind.GetKeyword);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/GlobalKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/GlobalKeywordRecommender.cs
new file mode 100644
index 0000000000..7cb8df670e
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/GlobalKeywordRecommender.cs
@@ -0,0 +1,42 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class GlobalKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public GlobalKeywordRecommender()
+ : base(SyntaxKind.GlobalKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+
+ if (syntaxTree.IsMemberDeclarationContext(position, context.LeftToken, cancellationToken))
+ {
+ var token = context.TargetToken;
+
+ if (token.GetAncestor<EnumDeclarationSyntax>() == null)
+ {
+ return true;
+ }
+ }
+
+ return
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ context.IsAnyExpressionContext ||
+ context.IsObjectCreationTypeContext ||
+ context.IsIsOrAsTypeContext ||
+ syntaxTree.IsUsingAliasContext(position, cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/GotoKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/GotoKeywordRecommender.cs
new file mode 100644
index 0000000000..e252d97856
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/GotoKeywordRecommender.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class GotoKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public GotoKeywordRecommender()
+ : base(SyntaxKind.GotoKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/GroupKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/GroupKeywordRecommender.cs
new file mode 100644
index 0000000000..401c5825d6
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/GroupKeywordRecommender.cs
@@ -0,0 +1,32 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class GroupKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public GroupKeywordRecommender()
+ : base(SyntaxKind.GroupKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var token = context.TargetToken;
+
+ // var q = from x in y
+ // |
+ if (!token.IntersectsWith(position) &&
+ token.IsLastTokenOfQueryClause())
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/HiddenKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/HiddenKeywordRecommender.cs
new file mode 100644
index 0000000000..f7047a9dcf
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/HiddenKeywordRecommender.cs
@@ -0,0 +1,32 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class HiddenKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public HiddenKeywordRecommender()
+ : base(SyntaxKind.HiddenKeyword, isValidInPreprocessorContext: true)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ // cases:
+ // #line |
+ // #line h|
+ // # line |
+ // # line h|
+
+ var previousToken1 = context.TargetToken;
+ var previousToken2 = previousToken1.GetPreviousToken(includeSkipped: true);
+
+ return
+ previousToken1.Kind() == SyntaxKind.LineKeyword &&
+ previousToken2.Kind() == SyntaxKind.HashToken;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/IfKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/IfKeywordRecommender.cs
new file mode 100644
index 0000000000..dd5839e814
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/IfKeywordRecommender.cs
@@ -0,0 +1,24 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class IfKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public IfKeywordRecommender()
+ : base(SyntaxKind.IfKeyword, isValidInPreprocessorContext: true)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsPreProcessorKeywordContext ||
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ImplicitKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ImplicitKeywordRecommender.cs
new file mode 100644
index 0000000000..ba32b643c0
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ImplicitKeywordRecommender.cs
@@ -0,0 +1,41 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class ImplicitKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ private static readonly ISet<SyntaxKind> s_validMemberModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.StaticKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.ExternKeyword,
+ SyntaxKind.UnsafeKeyword,
+ };
+
+ public ImplicitKeywordRecommender()
+ : base(SyntaxKind.ImplicitKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ if (context.IsMemberDeclarationContext(validModifiers: s_validMemberModifiers, validTypeDeclarations: SyntaxKindSet.ClassStructTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken))
+ {
+ // operators must be both public and static
+ var modifiers = context.PrecedingModifiers;
+
+ return
+ modifiers.Contains(SyntaxKind.PublicKeyword) &&
+ modifiers.Contains(SyntaxKind.StaticKeyword);
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/InKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/InKeywordRecommender.cs
new file mode 100644
index 0000000000..f8c14fef53
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/InKeywordRecommender.cs
@@ -0,0 +1,112 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class InKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public InKeywordRecommender()
+ : base(SyntaxKind.InKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ IsValidContextInForEachClause(context) ||
+ IsValidContextInFromClause(context, cancellationToken) ||
+ IsValidContextInJoinClause(context, cancellationToken) ||
+ context.TargetToken.IsTypeParameterVarianceContext();
+ }
+
+ private bool IsValidContextInForEachClause(CSharpSyntaxContext context)
+ {
+ // cases:
+ // foreach (var v |
+ // foreach (var v i|
+
+ var token = context.TargetToken;
+
+ if (token.Kind() == SyntaxKind.IdentifierToken)
+ {
+ var statement = token.GetAncestor<ForEachStatementSyntax>();
+ if (statement != null && token == statement.Identifier)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private bool IsValidContextInFromClause(CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var token = context.TargetToken;
+
+ if (token.Kind() == SyntaxKind.IdentifierToken)
+ {
+ // case:
+ // from x |
+ if (token.GetPreviousToken(includeSkipped: true).IsKindOrHasMatchingText(SyntaxKind.FromKeyword))
+ {
+ var typeSyntax = token.Parent as TypeSyntax;
+ if (!typeSyntax.IsPotentialTypeName(context.SemanticModel, cancellationToken))
+ {
+ return true;
+ }
+ }
+
+ var fromClause = token.Parent as FromClauseSyntax;
+ if (fromClause != null)
+ {
+ // case:
+ // from int x |
+ if (token == fromClause.Identifier && fromClause.Type != null)
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private bool IsValidContextInJoinClause(CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var token = context.TargetToken;
+
+ if (token.Kind() == SyntaxKind.IdentifierToken)
+ {
+ var joinClause = token.Parent.FirstAncestorOrSelf<JoinClauseSyntax>();
+ if (joinClause != null)
+ {
+ // case:
+ // join int x |
+ if (token == joinClause.Identifier && joinClause.Type != null)
+ {
+ return true;
+ }
+
+ // case:
+ // join x |
+ if (joinClause.Type != null &&
+ joinClause.Type.IsKind(SyntaxKind.IdentifierName) &&
+ token == ((IdentifierNameSyntax)joinClause.Type).Identifier &&
+ !joinClause.Type.IsPotentialTypeName(context.SemanticModel, cancellationToken))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/IntKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/IntKeywordRecommender.cs
new file mode 100644
index 0000000000..91dc5e0fe8
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/IntKeywordRecommender.cs
@@ -0,0 +1,48 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class IntKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public IntKeywordRecommender()
+ : base(SyntaxKind.IntKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ return
+ context.IsAnyExpressionContext ||
+ context.IsDefiniteCastTypeContext ||
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ context.IsObjectCreationTypeContext ||
+ context.IsGenericTypeArgumentContext ||
+ context.IsEnumBaseListContext ||
+ context.IsIsOrAsTypeContext ||
+ context.IsLocalVariableDeclarationContext ||
+ context.IsFixedVariableDeclarationContext ||
+ context.IsParameterTypeContext ||
+ context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext ||
+ context.IsImplicitOrExplicitOperatorTypeContext ||
+ context.IsPrimaryFunctionExpressionContext ||
+ context.IsCrefContext ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.StackAllocKeyword, cancellationToken) ||
+ context.IsDelegateReturnTypeContext ||
+ syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(
+ validModifiers: SyntaxKindSet.AllMemberModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/InterfaceKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/InterfaceKeywordRecommender.cs
new file mode 100644
index 0000000000..56107f6ff0
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/InterfaceKeywordRecommender.cs
@@ -0,0 +1,38 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class InterfaceKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ private static readonly ISet<SyntaxKind> s_validModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.PrivateKeyword,
+ SyntaxKind.ProtectedKeyword,
+ SyntaxKind.UnsafeKeyword
+ };
+
+ public InterfaceKeywordRecommender()
+ : base(SyntaxKind.InterfaceKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsGlobalStatementContext ||
+ context.IsTypeDeclarationContext(
+ validModifiers: s_validModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassStructTypeDeclarations,
+ canBePartial: true,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/InternalKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/InternalKeywordRecommender.cs
new file mode 100644
index 0000000000..9e975f2735
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/InternalKeywordRecommender.cs
@@ -0,0 +1,69 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class InternalKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public InternalKeywordRecommender()
+ : base(SyntaxKind.InternalKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsGlobalStatementContext ||
+ IsValidContextForAccessor(context) ||
+ IsValidContextForType(context, cancellationToken) ||
+ IsValidContextForMember(context, cancellationToken);
+ }
+
+ private static bool IsValidContextForAccessor(CSharpSyntaxContext context)
+ {
+ if (context.TargetToken.IsAccessorDeclarationContext<PropertyDeclarationSyntax>(context.Position) ||
+ context.TargetToken.IsAccessorDeclarationContext<IndexerDeclarationSyntax>(context.Position))
+ {
+ return CheckPreviousAccessibilityModifiers(context);
+ }
+
+ return false;
+ }
+
+ private static bool IsValidContextForMember(CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ if (context.SyntaxTree.IsGlobalMemberDeclarationContext(context.Position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(validModifiers: SyntaxKindSet.AllMemberModifiers, validTypeDeclarations: SyntaxKindSet.ClassStructTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken))
+ {
+ return CheckPreviousAccessibilityModifiers(context);
+ }
+
+ return false;
+ }
+
+ private static bool IsValidContextForType(CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ if (context.IsTypeDeclarationContext(validModifiers: SyntaxKindSet.AllTypeModifiers, validTypeDeclarations: SyntaxKindSet.ClassStructTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken))
+ {
+ return CheckPreviousAccessibilityModifiers(context);
+ }
+
+ return false;
+ }
+
+ private static bool CheckPreviousAccessibilityModifiers(CSharpSyntaxContext context)
+ {
+ // internal things can be protected.
+ var precedingModifiers = context.PrecedingModifiers;
+ return
+ !precedingModifiers.Contains(SyntaxKind.PublicKeyword) &&
+ !precedingModifiers.Contains(SyntaxKind.InternalKeyword) &&
+ !precedingModifiers.Contains(SyntaxKind.PrivateKeyword);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/IntoKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/IntoKeywordRecommender.cs
new file mode 100644
index 0000000000..d19d8dcf64
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/IntoKeywordRecommender.cs
@@ -0,0 +1,117 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class IntoKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public IntoKeywordRecommender()
+ : base(SyntaxKind.IntoKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ IsValidContextForJoin(context) ||
+ IsValidContextForSelect(context) ||
+ IsValidContextForGroup(context);
+ }
+
+ private bool IsValidContextForSelect(CSharpSyntaxContext context)
+ {
+ var token = context.TargetToken;
+
+ var select = token.GetAncestor<SelectClauseSyntax>();
+ if (select == null)
+ {
+ return false;
+ }
+
+ if (select.Expression.Width() == 0)
+ {
+ return false;
+ }
+
+ var lastToken = select.Expression.GetLastToken(includeSkipped: true);
+
+ if (lastToken == token)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ private bool IsValidContextForGroup(CSharpSyntaxContext context)
+ {
+ var token = context.TargetToken;
+
+ var group = token.GetAncestor<GroupClauseSyntax>();
+ if (group == null)
+ {
+ return false;
+ }
+
+ if (group.ByExpression.Width() == 0 ||
+ group.GroupExpression.Width() == 0)
+ {
+ return false;
+ }
+
+ var lastToken = group.ByExpression.GetLastToken(includeSkipped: true);
+
+ if (lastToken == token)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ private static bool IsValidContextForJoin(CSharpSyntaxContext context)
+ {
+ // cases:
+ // join a in expr o1 equals o2 |
+ // join a in expr o1 equals o2 i|
+
+ var token = context.TargetToken;
+ var join = token.GetAncestor<JoinClauseSyntax>();
+
+ if (join == null)
+ {
+ // happens for:
+ // join a in expr on o1 equals o2 e|
+ if (!token.IntersectsWith(context.Position))
+ {
+ return false;
+ }
+
+ token = token.GetPreviousToken(includeSkipped: true);
+ join = token.GetAncestor<JoinClauseSyntax>();
+
+ if (join == null)
+ {
+ return false;
+ }
+ }
+
+ var lastToken = join.RightExpression.GetLastToken(includeSkipped: true);
+
+ // join a in expr on o1 equals o2 |
+ if (token == lastToken &&
+ !lastToken.IntersectsWith(context.Position))
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/IsKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/IsKeywordRecommender.cs
new file mode 100644
index 0000000000..e9359aae81
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/IsKeywordRecommender.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class IsKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public IsKeywordRecommender()
+ : base(SyntaxKind.IsKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ // cases:
+ // expr |
+ return !context.IsInNonUserCode && context.IsIsOrAsContext;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/JoinKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/JoinKeywordRecommender.cs
new file mode 100644
index 0000000000..d61b937922
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/JoinKeywordRecommender.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class JoinKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public JoinKeywordRecommender()
+ : base(SyntaxKind.JoinKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return context.SyntaxTree.IsValidContextForJoinClause(position, context.LeftToken, cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/LetKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/LetKeywordRecommender.cs
new file mode 100644
index 0000000000..f76d9b97ca
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/LetKeywordRecommender.cs
@@ -0,0 +1,32 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class LetKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public LetKeywordRecommender()
+ : base(SyntaxKind.LetKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var token = context.TargetToken;
+
+ // var q = from x in y
+ // |
+ if (!token.IntersectsWith(position) &&
+ token.IsLastTokenOfQueryClause())
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/LineKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/LineKeywordRecommender.cs
new file mode 100644
index 0000000000..f481ef3436
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/LineKeywordRecommender.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class LineKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public LineKeywordRecommender()
+ : base(SyntaxKind.LineKeyword, isValidInPreprocessorContext: true)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return context.IsPreProcessorKeywordContext;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/LockKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/LockKeywordRecommender.cs
new file mode 100644
index 0000000000..0ce70f2edb
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/LockKeywordRecommender.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class LockKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public LockKeywordRecommender()
+ : base(SyntaxKind.LockKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/LongKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/LongKeywordRecommender.cs
new file mode 100644
index 0000000000..c2e8ce8963
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/LongKeywordRecommender.cs
@@ -0,0 +1,48 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class LongKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public LongKeywordRecommender()
+ : base(SyntaxKind.LongKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ return
+ context.IsAnyExpressionContext ||
+ context.IsDefiniteCastTypeContext ||
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ context.IsObjectCreationTypeContext ||
+ context.IsGenericTypeArgumentContext ||
+ context.IsEnumBaseListContext ||
+ context.IsIsOrAsTypeContext ||
+ context.IsLocalVariableDeclarationContext ||
+ context.IsFixedVariableDeclarationContext ||
+ context.IsParameterTypeContext ||
+ context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext ||
+ context.IsImplicitOrExplicitOperatorTypeContext ||
+ context.IsPrimaryFunctionExpressionContext ||
+ context.IsCrefContext ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.StackAllocKeyword, cancellationToken) ||
+ context.IsDelegateReturnTypeContext ||
+ syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(
+ validModifiers: SyntaxKindSet.AllMemberModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/MethodKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/MethodKeywordRecommender.cs
new file mode 100644
index 0000000000..e9e61a1240
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/MethodKeywordRecommender.cs
@@ -0,0 +1,43 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class MethodKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public MethodKeywordRecommender()
+ : base(SyntaxKind.MethodKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ if (context.IsMemberAttributeContext(SyntaxKindSet.ClassInterfaceStructTypeDeclarations, cancellationToken))
+ {
+ return true;
+ }
+
+ var token = context.TargetToken;
+
+ if (token.Kind() == SyntaxKind.OpenBracketToken &&
+ token.Parent.IsKind(SyntaxKind.AttributeList))
+ {
+ if (token.GetAncestor<PropertyDeclarationSyntax>() != null ||
+ token.GetAncestor<EventDeclarationSyntax>() != null)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ModuleKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ModuleKeywordRecommender.cs
new file mode 100644
index 0000000000..f86d6d6a11
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ModuleKeywordRecommender.cs
@@ -0,0 +1,32 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class ModuleKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public ModuleKeywordRecommender()
+ : base(SyntaxKind.ModuleKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ if (context.IsTypeAttributeContext(cancellationToken))
+ {
+ var token = context.LeftToken;
+ var type = token.GetAncestor<MemberDeclarationSyntax>();
+
+ return type == null || type.IsParentKind(SyntaxKind.CompilationUnit);
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/NameOfKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/NameOfKeywordRecommender.cs
new file mode 100644
index 0000000000..dc6589997f
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/NameOfKeywordRecommender.cs
@@ -0,0 +1,34 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class NameOfKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public NameOfKeywordRecommender()
+ : base(SyntaxKind.NameOfKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsAnyExpressionContext ||
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ IsAttributeArgumentContext(context);
+ }
+
+ private bool IsAttributeArgumentContext(CSharpSyntaxContext context)
+ {
+ return
+ context.IsAnyExpressionContext &&
+ context.LeftToken.GetAncestor<AttributeSyntax>() != null;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/NamespaceKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/NamespaceKeywordRecommender.cs
new file mode 100644
index 0000000000..f2cd4ef2fc
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/NamespaceKeywordRecommender.cs
@@ -0,0 +1,160 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class NamespaceKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public NamespaceKeywordRecommender()
+ : base(SyntaxKind.NamespaceKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+
+ // namespaces are illegal in interactive code:
+ if (syntaxTree.IsInteractiveOrScript())
+ {
+ return false;
+ }
+
+ // cases:
+ // root: |
+
+ // root: n|
+
+ // extern alias a;
+ // |
+
+ // extern alias a;
+ // n|
+
+ // using Foo;
+ // |
+
+ // using Foo;
+ // n|
+
+ // using Foo = Bar;
+ // |
+
+ // using Foo = Bar;
+ // n|
+
+ // namespace N {}
+ // |
+
+ // namespace N {}
+ // n|
+
+ // class C {}
+ // |
+
+ // class C {}
+ // n|
+
+ var leftToken = context.LeftToken;
+ var token = context.TargetToken;
+
+ // root: n|
+
+ // ns Foo { n|
+
+ // extern alias a;
+ // n|
+
+ // using Foo;
+ // n|
+
+ // using Foo = Bar;
+ // n|
+
+ // a namespace can't come before usings/externs
+ // a child namespace can't come before usings/externs
+ if (leftToken.GetNextToken(includeSkipped: true).IsUsingOrExternKeyword())
+ {
+ return false;
+ }
+
+ // root: |
+ if (token.Kind() == SyntaxKind.None)
+ {
+ // root namespace
+ var root = syntaxTree.GetRoot(cancellationToken) as CompilationUnitSyntax;
+ if (root.Externs.Count > 0 ||
+ root.Usings.Count > 0)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ if (token.Kind() == SyntaxKind.OpenBraceToken &&
+ token.Parent.IsKind(SyntaxKind.NamespaceDeclaration))
+ {
+ return true;
+ }
+
+ // extern alias a;
+ // |
+
+ // using Foo;
+ // |
+ if (token.Kind() == SyntaxKind.SemicolonToken)
+ {
+ if (token.Parent.IsKind(SyntaxKind.ExternAliasDirective, SyntaxKind.UsingDirective))
+ {
+ return true;
+ }
+ }
+
+ // class C {}
+ // |
+ if (token.Kind() == SyntaxKind.CloseBraceToken)
+ {
+ if (token.Parent is TypeDeclarationSyntax &&
+ !(token.Parent.GetParent() is TypeDeclarationSyntax))
+ {
+ return true;
+ }
+ else if (token.Parent.IsKind(SyntaxKind.NamespaceDeclaration))
+ {
+ return true;
+ }
+ }
+
+ // delegate void D();
+ // |
+
+ if (token.Kind() == SyntaxKind.SemicolonToken)
+ {
+ if (token.Parent.IsKind(SyntaxKind.DelegateDeclaration) &&
+ !(token.Parent.GetParent() is TypeDeclarationSyntax))
+ {
+ return true;
+ }
+ }
+
+ // [assembly: foo]
+ // |
+
+ if (token.Kind() == SyntaxKind.CloseBracketToken &&
+ token.Parent.IsKind(SyntaxKind.AttributeList) &&
+ token.Parent.IsParentKind(SyntaxKind.CompilationUnit))
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/NewKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/NewKeywordRecommender.cs
new file mode 100644
index 0000000000..f5c8c5742f
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/NewKeywordRecommender.cs
@@ -0,0 +1,123 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Symbols;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class NewKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ private static readonly ISet<SyntaxKind> s_validMemberModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.AbstractKeyword,
+ SyntaxKind.ExternKeyword,
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.PrivateKeyword,
+ SyntaxKind.ProtectedKeyword,
+ SyntaxKind.ReadOnlyKeyword,
+ SyntaxKind.SealedKeyword,
+ SyntaxKind.StaticKeyword,
+ SyntaxKind.UnsafeKeyword,
+ SyntaxKind.VirtualKeyword,
+ SyntaxKind.VolatileKeyword,
+ };
+
+ protected static readonly ISet<SyntaxKind> ValidTypeModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.AbstractKeyword,
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.PrivateKeyword,
+ SyntaxKind.ProtectedKeyword,
+ SyntaxKind.SealedKeyword,
+ SyntaxKind.StaticKeyword,
+ SyntaxKind.UnsafeKeyword
+ };
+
+ public NewKeywordRecommender()
+ : base(SyntaxKind.NewKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return IsValid (position, context, cancellationToken);
+ }
+
+ public bool IsValid(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ IsNewConstraintContext(context, cancellationToken) ||
+ context.IsAnyExpressionContext ||
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ IsMemberDeclarationContext(context, cancellationToken) ||
+ IsTypeDeclarationContext(context, cancellationToken);
+ }
+
+ private bool IsTypeDeclarationContext(CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ if (context.IsTypeDeclarationContext(validModifiers: ValidTypeModifiers, validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken))
+ {
+ // we must be on a nested type.
+ var token = context.LeftToken;
+ return token.GetAncestors<TypeDeclarationSyntax>()
+ .Any(t => token.SpanStart > t.OpenBraceToken.Span.End &&
+ token.Span.End < t.CloseBraceToken.SpanStart);
+ }
+
+ return false;
+ }
+
+ private bool IsMemberDeclarationContext(CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.SyntaxTree.IsGlobalMemberDeclarationContext(context.Position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(
+ validModifiers: s_validMemberModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+
+ private static bool IsNewConstraintContext(CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ // cases:
+ // where T : |
+ // where T : class, |
+ // where T : Foo, |
+ // note: 'new()' can't come after a 'struct' constraint.
+
+ if (context.SyntaxTree.IsTypeParameterConstraintStartContext(context.Position, context.LeftToken, cancellationToken))
+ {
+ return true;
+ }
+
+ var token = context.TargetToken;
+
+ if (token.Kind() == SyntaxKind.CommaToken &&
+ token.Parent.IsKind(SyntaxKind.TypeParameterConstraintClause))
+ {
+ var constraintClause = token.Parent as TypeParameterConstraintClauseSyntax;
+ if (!constraintClause.Constraints
+ .OfType<ClassOrStructConstraintSyntax>()
+ .Any(c => c.ClassOrStructKeyword.Kind() == SyntaxKind.StructKeyword))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/NullKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/NullKeywordRecommender.cs
new file mode 100644
index 0000000000..3200470fb2
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/NullKeywordRecommender.cs
@@ -0,0 +1,52 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class NullKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public NullKeywordRecommender()
+ : base(SyntaxKind.NullKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+
+ return
+ context.IsAnyExpressionContext ||
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ IsInSelectCaseContext(context, cancellationToken);
+ }
+
+ private bool IsInSelectCaseContext(CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var token = context.TargetToken;
+ if (token.Kind() != SyntaxKind.CaseKeyword)
+ {
+ return false;
+ }
+
+ var switchStatement = token.GetAncestor<SwitchStatementSyntax>();
+ if (switchStatement != null)
+ {
+ var info = context.SemanticModel.GetTypeInfo(switchStatement.Expression, cancellationToken);
+ if (info.Type != null &&
+ info.Type.IsValueType)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ObjectKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ObjectKeywordRecommender.cs
new file mode 100644
index 0000000000..15293d61e3
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ObjectKeywordRecommender.cs
@@ -0,0 +1,46 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class ObjectKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public ObjectKeywordRecommender()
+ : base(SyntaxKind.ObjectKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ return
+ context.IsNonAttributeExpressionContext ||
+ context.IsDefiniteCastTypeContext ||
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ context.IsObjectCreationTypeContext ||
+ context.IsGenericTypeArgumentContext ||
+ context.IsIsOrAsTypeContext ||
+ context.IsLocalVariableDeclarationContext ||
+ context.IsParameterTypeContext ||
+ context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext ||
+ context.IsImplicitOrExplicitOperatorTypeContext ||
+ context.IsTypeOfExpressionContext ||
+ context.IsCrefContext ||
+ syntaxTree.IsDefaultExpressionContext(position, context.LeftToken, cancellationToken) ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) ||
+ context.IsDelegateReturnTypeContext ||
+ syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(
+ validModifiers: SyntaxKindSet.AllMemberModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/OnKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/OnKeywordRecommender.cs
new file mode 100644
index 0000000000..0665f64786
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/OnKeywordRecommender.cs
@@ -0,0 +1,49 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class OnKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public OnKeywordRecommender()
+ : base(SyntaxKind.OnKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ // cases:
+ // join a in expr |
+ // join a in expr o|
+ // join a.b c in expr |
+ // join a.b c in expr o|
+
+ var token = context.TargetToken;
+
+ var join = token.GetAncestor<JoinClauseSyntax>();
+ if (join == null)
+ {
+ return false;
+ }
+
+ // join a in expr |
+ // join a.b c in expr |
+
+ var lastToken = join.InExpression.GetLastToken(includeSkipped: true);
+
+ if (join.InExpression.Width() > 0 &&
+ token == lastToken)
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/OperatorKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/OperatorKeywordRecommender.cs
new file mode 100644
index 0000000000..2cc67a5057
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/OperatorKeywordRecommender.cs
@@ -0,0 +1,36 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class OperatorKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+// private static readonly ISet<SyntaxKind> s_validMemberModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+// {
+// SyntaxKind.StaticKeyword,
+// SyntaxKind.PublicKeyword,
+// SyntaxKind.ExternKeyword,
+// };
+
+ public OperatorKeywordRecommender()
+ : base(SyntaxKind.OperatorKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ // cases:
+ // public static implicit |
+ // public static explicit |
+ var token = context.TargetToken;
+
+ return
+ token.Kind() == SyntaxKind.ImplicitKeyword ||
+ token.Kind() == SyntaxKind.ExplicitKeyword;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/OrderByKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/OrderByKeywordRecommender.cs
new file mode 100644
index 0000000000..7bc71ac08c
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/OrderByKeywordRecommender.cs
@@ -0,0 +1,32 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class OrderByKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public OrderByKeywordRecommender()
+ : base(SyntaxKind.OrderByKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var token = context.TargetToken;
+
+ // var q = from x in y
+ // |
+ if (!token.IntersectsWith(position) &&
+ token.IsLastTokenOfQueryClause())
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/OutKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/OutKeywordRecommender.cs
new file mode 100644
index 0000000000..8b81204b29
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/OutKeywordRecommender.cs
@@ -0,0 +1,30 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class OutKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public OutKeywordRecommender()
+ : base(SyntaxKind.OutKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+
+ // TODO(cyrusn): lambda/anon methods can have out/ref parameters
+ return
+ context.TargetToken.IsTypeParameterVarianceContext() ||
+ syntaxTree.IsParameterModifierContext(position, context.LeftToken, cancellationToken) ||
+ syntaxTree.IsAnonymousMethodParameterModifierContext(position, context.LeftToken, cancellationToken) ||
+ syntaxTree.IsPossibleLambdaParameterModifierContext(position, context.LeftToken, cancellationToken) ||
+ context.TargetToken.IsConstructorOrMethodParameterArgumentContext() ||
+ context.TargetToken.IsXmlCrefParameterModifierContext();
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/OverrideKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/OverrideKeywordRecommender.cs
new file mode 100644
index 0000000000..d01a356317
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/OverrideKeywordRecommender.cs
@@ -0,0 +1,38 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class OverrideKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ private static readonly ISet<SyntaxKind> s_validMemberModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.ExternKeyword,
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.ProtectedKeyword,
+ SyntaxKind.UnsafeKeyword,
+ SyntaxKind.SealedKeyword,
+ SyntaxKind.AbstractKeyword,
+ };
+
+ public OverrideKeywordRecommender()
+ : base(SyntaxKind.OverrideKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return context.IsMemberDeclarationContext(
+ validModifiers: s_validMemberModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ParamKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ParamKeywordRecommender.cs
new file mode 100644
index 0000000000..6d4cd307f1
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ParamKeywordRecommender.cs
@@ -0,0 +1,37 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class ParamKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public ParamKeywordRecommender()
+ : base(SyntaxKind.ParamKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var token = context.TargetToken;
+
+ if (token.Kind() == SyntaxKind.OpenBracketToken &&
+ token.Parent.IsKind(SyntaxKind.AttributeList))
+ {
+ if (token.GetAncestor<PropertyDeclarationSyntax>() != null ||
+ token.GetAncestor<EventDeclarationSyntax>() != null)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ParamsKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ParamsKeywordRecommender.cs
new file mode 100644
index 0000000000..dbb7434508
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ParamsKeywordRecommender.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class ParamsKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public ParamsKeywordRecommender()
+ : base(SyntaxKind.ParamsKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return context.SyntaxTree.IsParameterModifierContext(context.Position, context.LeftToken, cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/PartialKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/PartialKeywordRecommender.cs
new file mode 100644
index 0000000000..e9105b4619
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/PartialKeywordRecommender.cs
@@ -0,0 +1,64 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class PartialKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ private static readonly ISet<SyntaxKind> s_validMemberModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.AsyncKeyword,
+ SyntaxKind.StaticKeyword
+ };
+
+ public PartialKeywordRecommender()
+ : base(SyntaxKind.PartialKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsGlobalStatementContext ||
+ IsMemberDeclarationContext(context, cancellationToken) ||
+ IsTypeDeclarationContext(context, cancellationToken);
+ }
+
+ private bool IsMemberDeclarationContext(CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ if (context.IsMemberDeclarationContext(validModifiers: s_validMemberModifiers, validTypeDeclarations: SyntaxKindSet.ClassStructTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken))
+ {
+ var token = context.LeftToken;
+ var decl = token.GetAncestor<TypeDeclarationSyntax>();
+
+ // partial methods must be in partial types
+ if (!decl.Modifiers.Any(t => t.IsKindOrHasMatchingText(SyntaxKind.PartialKeyword)))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ private bool IsTypeDeclarationContext(CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return context.IsTypeDeclarationContext(
+ validModifiers: SyntaxKindSet.AllTypeModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/PragmaKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/PragmaKeywordRecommender.cs
new file mode 100644
index 0000000000..556d2ee144
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/PragmaKeywordRecommender.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class PragmaKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public PragmaKeywordRecommender()
+ : base(SyntaxKind.PragmaKeyword, isValidInPreprocessorContext: true)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return context.IsPreProcessorKeywordContext;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/PrivateKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/PrivateKeywordRecommender.cs
new file mode 100644
index 0000000000..26a1f1d8f2
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/PrivateKeywordRecommender.cs
@@ -0,0 +1,87 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class PrivateKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public PrivateKeywordRecommender()
+ : base(SyntaxKind.PrivateKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsGlobalStatementContext ||
+ IsValidContextForAccessor(context) ||
+ IsValidContextForType(context, cancellationToken) ||
+ IsValidContextForMember(context, cancellationToken);
+ }
+
+ private static bool IsValidContextForAccessor(CSharpSyntaxContext context)
+ {
+ if (context.TargetToken.IsAccessorDeclarationContext<PropertyDeclarationSyntax>(context.Position) ||
+ context.TargetToken.IsAccessorDeclarationContext<IndexerDeclarationSyntax>(context.Position))
+ {
+ return CheckPreviousAccessibilityModifiers(context);
+ }
+
+ return false;
+ }
+
+ private static bool IsValidContextForMember(CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ if (context.SyntaxTree.IsGlobalMemberDeclarationContext(context.Position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(validModifiers: SyntaxKindSet.AllMemberModifiers, validTypeDeclarations: SyntaxKindSet.ClassStructTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken))
+ {
+ var modifiers = context.PrecedingModifiers;
+
+ // can't have private + abstract/virtual/override/sealed
+ if (modifiers.Contains(SyntaxKind.AbstractKeyword) ||
+ modifiers.Contains(SyntaxKind.VirtualKeyword) ||
+ modifiers.Contains(SyntaxKind.OverrideKeyword) ||
+ modifiers.Contains(SyntaxKind.SealedKeyword))
+ {
+ return false;
+ }
+
+ return CheckPreviousAccessibilityModifiers(context);
+ }
+
+ return false;
+ }
+
+ private static bool IsValidContextForType(CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ if (context.IsTypeDeclarationContext(validModifiers: SyntaxKindSet.AllTypeModifiers, validTypeDeclarations: SyntaxKindSet.ClassStructTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken))
+ {
+ // private things can't be in namespaces.
+ var typeDecl = context.ContainingTypeDeclaration;
+ if (typeDecl == null)
+ {
+ return false;
+ }
+
+ return CheckPreviousAccessibilityModifiers(context);
+ }
+
+ return false;
+ }
+
+ private static bool CheckPreviousAccessibilityModifiers(CSharpSyntaxContext context)
+ {
+ var precedingModifiers = context.PrecedingModifiers;
+ return
+ !precedingModifiers.Contains(SyntaxKind.PublicKeyword) &&
+ !precedingModifiers.Contains(SyntaxKind.InternalKeyword) &&
+ !precedingModifiers.Contains(SyntaxKind.ProtectedKeyword) &&
+ !precedingModifiers.Contains(SyntaxKind.PrivateKeyword);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/PropertyKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/PropertyKeywordRecommender.cs
new file mode 100644
index 0000000000..6be63e7031
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/PropertyKeywordRecommender.cs
@@ -0,0 +1,22 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class PropertyKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public PropertyKeywordRecommender()
+ : base(SyntaxKind.PropertyKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return context.IsMemberAttributeContext(SyntaxKindSet.ClassInterfaceStructTypeDeclarations, cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ProtectedKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ProtectedKeywordRecommender.cs
new file mode 100644
index 0000000000..3c7d897e22
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ProtectedKeywordRecommender.cs
@@ -0,0 +1,74 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class ProtectedKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public ProtectedKeywordRecommender()
+ : base(SyntaxKind.ProtectedKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ IsValidContextForAccessor(context) ||
+ IsValidContextForType(context, cancellationToken) ||
+ IsValidContextForMember(context, cancellationToken);
+ }
+
+ private static bool IsValidContextForAccessor(CSharpSyntaxContext context)
+ {
+ if (context.TargetToken.IsAccessorDeclarationContext<PropertyDeclarationSyntax>(context.Position) ||
+ context.TargetToken.IsAccessorDeclarationContext<IndexerDeclarationSyntax>(context.Position))
+ {
+ return CheckPreviousAccessibilityModifiers(context);
+ }
+
+ return false;
+ }
+
+ private static bool IsValidContextForMember(CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ if (context.IsMemberDeclarationContext(validModifiers: SyntaxKindSet.AllMemberModifiers, validTypeDeclarations: SyntaxKindSet.ClassOnlyTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken))
+ {
+ return CheckPreviousAccessibilityModifiers(context);
+ }
+
+ return false;
+ }
+
+ private static bool IsValidContextForType(CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ if (context.IsTypeDeclarationContext(validModifiers: SyntaxKindSet.AllTypeModifiers, validTypeDeclarations: SyntaxKindSet.ClassOnlyTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken))
+ {
+ // protected things can't be in namespaces.
+ var typeDecl = context.ContainingTypeDeclaration;
+ if (typeDecl == null)
+ {
+ return false;
+ }
+
+ return CheckPreviousAccessibilityModifiers(context);
+ }
+
+ return false;
+ }
+
+ private static bool CheckPreviousAccessibilityModifiers(CSharpSyntaxContext context)
+ {
+ // We can show up after 'internal'.
+ var precedingModifiers = context.PrecedingModifiers;
+ return
+ !precedingModifiers.Contains(SyntaxKind.PublicKeyword) &&
+ !precedingModifiers.Contains(SyntaxKind.ProtectedKeyword) &&
+ !precedingModifiers.Contains(SyntaxKind.PrivateKeyword);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/PublicKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/PublicKeywordRecommender.cs
new file mode 100644
index 0000000000..21debc6f36
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/PublicKeywordRecommender.cs
@@ -0,0 +1,60 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class PublicKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public PublicKeywordRecommender()
+ : base(SyntaxKind.PublicKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsGlobalStatementContext ||
+ IsValidContextForType(context, cancellationToken) ||
+ IsValidContextForMember(context, cancellationToken);
+ }
+
+ private static bool IsValidContextForMember(CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ if (context.SyntaxTree.IsGlobalMemberDeclarationContext(context.Position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(
+ validModifiers: SyntaxKindSet.AllMemberModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken))
+ {
+ return CheckPreviousAccessibilityModifiers(context);
+ }
+
+ return false;
+ }
+
+ private static bool IsValidContextForType(CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ if (context.IsTypeDeclarationContext(validModifiers: SyntaxKindSet.AllTypeModifiers, validTypeDeclarations: SyntaxKindSet.ClassStructTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken))
+ {
+ return CheckPreviousAccessibilityModifiers(context);
+ }
+
+ return false;
+ }
+
+ private static bool CheckPreviousAccessibilityModifiers(CSharpSyntaxContext context)
+ {
+ var precedingModifiers = context.PrecedingModifiers;
+ return
+ !precedingModifiers.Contains(SyntaxKind.PublicKeyword) &&
+ !precedingModifiers.Contains(SyntaxKind.InternalKeyword) &&
+ !precedingModifiers.Contains(SyntaxKind.ProtectedKeyword) &&
+ !precedingModifiers.Contains(SyntaxKind.PrivateKeyword);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ReadOnlyKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ReadOnlyKeywordRecommender.cs
new file mode 100644
index 0000000000..0e845ed6e9
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ReadOnlyKeywordRecommender.cs
@@ -0,0 +1,40 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class ReadOnlyKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ private static readonly ISet<SyntaxKind> s_validMemberModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.NewKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.ProtectedKeyword,
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.PrivateKeyword,
+ SyntaxKind.StaticKeyword,
+ };
+
+ public ReadOnlyKeywordRecommender()
+ : base(SyntaxKind.ReadOnlyKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsGlobalStatementContext ||
+ context.SyntaxTree.IsGlobalMemberDeclarationContext(context.Position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(
+ validModifiers: s_validMemberModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/RefKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/RefKeywordRecommender.cs
new file mode 100644
index 0000000000..83081f1ebd
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/RefKeywordRecommender.cs
@@ -0,0 +1,27 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class RefKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public RefKeywordRecommender()
+ : base(SyntaxKind.RefKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ return
+ syntaxTree.IsParameterModifierContext(position, context.LeftToken, cancellationToken) ||
+ syntaxTree.IsAnonymousMethodParameterModifierContext(position, context.LeftToken, cancellationToken) ||
+ syntaxTree.IsPossibleLambdaParameterModifierContext(position, context.LeftToken, cancellationToken) ||
+ context.TargetToken.IsConstructorOrMethodParameterArgumentContext() ||
+ context.TargetToken.IsXmlCrefParameterModifierContext();
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ReferenceKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ReferenceKeywordRecommender.cs
new file mode 100644
index 0000000000..5fb24646fe
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ReferenceKeywordRecommender.cs
@@ -0,0 +1,26 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class ReferenceKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public ReferenceKeywordRecommender()
+ : base(SyntaxKind.ReferenceKeyword, isValidInPreprocessorContext: true)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ return
+ syntaxTree.IsInteractiveOrScript() &&
+ syntaxTree.IsBeforeFirstToken(position, cancellationToken) &&
+ context.IsPreProcessorKeywordContext;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/RegionKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/RegionKeywordRecommender.cs
new file mode 100644
index 0000000000..67fffd3948
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/RegionKeywordRecommender.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class RegionKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public RegionKeywordRecommender()
+ : base(SyntaxKind.RegionKeyword, isValidInPreprocessorContext: true, shouldFormatOnCommit: true)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return context.IsPreProcessorKeywordContext;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/RemoveKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/RemoveKeywordRecommender.cs
new file mode 100644
index 0000000000..e9abae057c
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/RemoveKeywordRecommender.cs
@@ -0,0 +1,22 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class RemoveKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public RemoveKeywordRecommender()
+ : base(SyntaxKind.RemoveKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return context.TargetToken.IsAccessorDeclarationContext<EventDeclarationSyntax>(position, SyntaxKind.RemoveKeyword);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/RestoreKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/RestoreKeywordRecommender.cs
new file mode 100644
index 0000000000..715edd7c22
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/RestoreKeywordRecommender.cs
@@ -0,0 +1,30 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class RestoreKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public RestoreKeywordRecommender()
+ : base(SyntaxKind.RestoreKeyword, isValidInPreprocessorContext: true)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ // # pragma warning |
+ // # pragma warning r|
+ var previousToken1 = context.TargetToken;
+ var previousToken2 = previousToken1.GetPreviousToken(includeSkipped: true);
+ var previousToken3 = previousToken2.GetPreviousToken(includeSkipped: true);
+
+ return
+ previousToken1.Kind() == SyntaxKind.WarningKeyword &&
+ previousToken2.Kind() == SyntaxKind.PragmaKeyword &&
+ previousToken3.Kind() == SyntaxKind.HashToken;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ReturnKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ReturnKeywordRecommender.cs
new file mode 100644
index 0000000000..da927d71be
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ReturnKeywordRecommender.cs
@@ -0,0 +1,33 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class ReturnKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public ReturnKeywordRecommender()
+ : base(SyntaxKind.ReturnKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsStatementContext ||
+ context.TargetToken.IsAfterYieldKeyword() ||
+ IsAttributeContext(context, cancellationToken);
+ }
+
+ private static bool IsAttributeContext(CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsMemberAttributeContext(SyntaxKindSet.ClassInterfaceStructTypeDeclarations, cancellationToken) ||
+ (context.SyntaxTree.IsInteractiveOrScript() && context.IsTypeAttributeContext(cancellationToken));
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/SByteKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/SByteKeywordRecommender.cs
new file mode 100644
index 0000000000..c8edd8e150
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/SByteKeywordRecommender.cs
@@ -0,0 +1,48 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class SByteKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public SByteKeywordRecommender()
+ : base(SyntaxKind.SByteKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ return
+ context.IsNonAttributeExpressionContext ||
+ context.IsDefiniteCastTypeContext ||
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ context.IsObjectCreationTypeContext ||
+ context.IsGenericTypeArgumentContext ||
+ context.IsEnumBaseListContext ||
+ context.IsIsOrAsTypeContext ||
+ context.IsLocalVariableDeclarationContext ||
+ context.IsFixedVariableDeclarationContext ||
+ context.IsParameterTypeContext ||
+ context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext ||
+ context.IsImplicitOrExplicitOperatorTypeContext ||
+ context.IsPrimaryFunctionExpressionContext ||
+ context.IsCrefContext ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.StackAllocKeyword, cancellationToken) ||
+ context.IsDelegateReturnTypeContext ||
+ syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(
+ validModifiers: SyntaxKindSet.AllMemberModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/SealedKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/SealedKeywordRecommender.cs
new file mode 100644
index 0000000000..ac89445eee
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/SealedKeywordRecommender.cs
@@ -0,0 +1,55 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class SealedKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ private static readonly ISet<SyntaxKind> s_validMemberModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.ExternKeyword,
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.NewKeyword,
+ SyntaxKind.OverrideKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.ProtectedKeyword,
+ SyntaxKind.UnsafeKeyword,
+ };
+
+ private static readonly ISet<SyntaxKind> s_validTypeModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.NewKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.PrivateKeyword,
+ SyntaxKind.ProtectedKeyword,
+ SyntaxKind.UnsafeKeyword
+ };
+
+ public SealedKeywordRecommender()
+ : base(SyntaxKind.SealedKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsGlobalStatementContext ||
+ context.IsMemberDeclarationContext(
+ validModifiers: s_validMemberModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassOnlyTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken) ||
+ context.IsTypeDeclarationContext(
+ validModifiers: s_validTypeModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/SelectKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/SelectKeywordRecommender.cs
new file mode 100644
index 0000000000..76afd5cc48
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/SelectKeywordRecommender.cs
@@ -0,0 +1,38 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class SelectKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public SelectKeywordRecommender()
+ : base(SyntaxKind.SelectKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var token = context.TargetToken;
+
+ // for orderby, ascending is the default so select should be available in the orderby direction context
+ if (token.IsOrderByDirectionContext())
+ {
+ return true;
+ }
+
+ // var q = from x in y
+ // |
+ if (!token.IntersectsWith(position) &&
+ token.IsLastTokenOfQueryClause())
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/SetKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/SetKeywordRecommender.cs
new file mode 100644
index 0000000000..bd37ed26a0
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/SetKeywordRecommender.cs
@@ -0,0 +1,24 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class SetKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public SetKeywordRecommender()
+ : base(SyntaxKind.SetKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.TargetToken.IsAccessorDeclarationContext<PropertyDeclarationSyntax>(position, SyntaxKind.SetKeyword) ||
+ context.TargetToken.IsAccessorDeclarationContext<IndexerDeclarationSyntax>(position, SyntaxKind.SetKeyword);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ShortKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ShortKeywordRecommender.cs
new file mode 100644
index 0000000000..e017ee200c
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ShortKeywordRecommender.cs
@@ -0,0 +1,48 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class ShortKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public ShortKeywordRecommender()
+ : base(SyntaxKind.ShortKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ return
+ context.IsAnyExpressionContext ||
+ context.IsDefiniteCastTypeContext ||
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ context.IsObjectCreationTypeContext ||
+ context.IsGenericTypeArgumentContext ||
+ context.IsEnumBaseListContext ||
+ context.IsIsOrAsTypeContext ||
+ context.IsLocalVariableDeclarationContext ||
+ context.IsFixedVariableDeclarationContext ||
+ context.IsParameterTypeContext ||
+ context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext ||
+ context.IsImplicitOrExplicitOperatorTypeContext ||
+ context.IsPrimaryFunctionExpressionContext ||
+ context.IsCrefContext ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.StackAllocKeyword, cancellationToken) ||
+ context.IsDelegateReturnTypeContext ||
+ syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(
+ validModifiers: SyntaxKindSet.AllMemberModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/SizeOfKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/SizeOfKeywordRecommender.cs
new file mode 100644
index 0000000000..b789d84171
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/SizeOfKeywordRecommender.cs
@@ -0,0 +1,24 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class SizeOfKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public SizeOfKeywordRecommender()
+ : base(SyntaxKind.SizeOfKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsNonAttributeExpressionContext ||
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/StackAllocKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/StackAllocKeywordRecommender.cs
new file mode 100644
index 0000000000..eb66b49210
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/StackAllocKeywordRecommender.cs
@@ -0,0 +1,42 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class StackAllocKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public StackAllocKeywordRecommender()
+ : base(SyntaxKind.StackAllocKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ // type t = |
+ var token = context.TargetToken;
+ if (token.IsUnsafeContext())
+ {
+ if (token.Kind() == SyntaxKind.EqualsToken &&
+ token.Parent.IsKind(SyntaxKind.EqualsValueClause) &&
+ token.Parent.IsParentKind(SyntaxKind.VariableDeclarator) &&
+ token.Parent.Parent.IsParentKind(SyntaxKind.VariableDeclaration))
+ {
+ var variableDeclaration = (VariableDeclarationSyntax)token.Parent.Parent.Parent;
+ if (variableDeclaration.IsParentKind(SyntaxKind.LocalDeclarationStatement) ||
+ variableDeclaration.IsParentKind(SyntaxKind.ForStatement))
+ {
+ return variableDeclaration.Type.IsVar || variableDeclaration.Type.IsKind(SyntaxKind.PointerType);
+ }
+ }
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/StaticKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/StaticKeywordRecommender.cs
new file mode 100644
index 0000000000..8daf3e29b5
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/StaticKeywordRecommender.cs
@@ -0,0 +1,83 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class StaticKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ private static readonly ISet<SyntaxKind> s_validTypeModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.NewKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.PrivateKeyword,
+ SyntaxKind.ProtectedKeyword,
+ SyntaxKind.UnsafeKeyword,
+ };
+
+ private static readonly ISet<SyntaxKind> s_validMemberModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.AsyncKeyword,
+ SyntaxKind.ExternKeyword,
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.NewKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.PrivateKeyword,
+ SyntaxKind.ProtectedKeyword,
+ SyntaxKind.ReadOnlyKeyword,
+ SyntaxKind.UnsafeKeyword,
+ SyntaxKind.VolatileKeyword,
+ };
+
+ private static readonly ISet<SyntaxKind> s_validGlobalMemberModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.ExternKeyword,
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.NewKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.PrivateKeyword,
+ SyntaxKind.ReadOnlyKeyword,
+ SyntaxKind.UnsafeKeyword,
+ SyntaxKind.VolatileKeyword,
+ };
+
+ public StaticKeywordRecommender()
+ : base(SyntaxKind.StaticKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsGlobalStatementContext ||
+ context.TargetToken.IsUsingKeywordInUsingDirective() ||
+ IsValidContextForType(context, cancellationToken) ||
+ IsValidContextForMember(context, cancellationToken);
+ }
+
+ private static bool IsValidContextForMember(CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.SyntaxTree.IsGlobalMemberDeclarationContext(context.Position, s_validGlobalMemberModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(
+ validModifiers: s_validMemberModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+
+ private static bool IsValidContextForType(CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return context.IsTypeDeclarationContext(
+ validModifiers: s_validTypeModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/StringKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/StringKeywordRecommender.cs
new file mode 100644
index 0000000000..a064a15360
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/StringKeywordRecommender.cs
@@ -0,0 +1,46 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class StringKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public StringKeywordRecommender()
+ : base(SyntaxKind.StringKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ return
+ context.IsAnyExpressionContext ||
+ context.IsDefiniteCastTypeContext ||
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ context.IsObjectCreationTypeContext ||
+ context.IsGenericTypeArgumentContext ||
+ context.IsIsOrAsTypeContext ||
+ context.IsLocalVariableDeclarationContext ||
+ context.IsParameterTypeContext ||
+ context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext ||
+ context.IsImplicitOrExplicitOperatorTypeContext ||
+ context.IsTypeOfExpressionContext ||
+ context.IsCrefContext ||
+ syntaxTree.IsDefaultExpressionContext(position, context.LeftToken, cancellationToken) ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) ||
+ context.IsDelegateReturnTypeContext ||
+ syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(
+ validModifiers: SyntaxKindSet.AllMemberModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/StructKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/StructKeywordRecommender.cs
new file mode 100644
index 0000000000..ad11333782
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/StructKeywordRecommender.cs
@@ -0,0 +1,40 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class StructKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ private static readonly ISet<SyntaxKind> s_validModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.PrivateKeyword,
+ SyntaxKind.ProtectedKeyword,
+ SyntaxKind.UnsafeKeyword
+ };
+
+ public StructKeywordRecommender()
+ : base(SyntaxKind.StructKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ return
+ context.IsGlobalStatementContext ||
+ context.IsTypeDeclarationContext(
+ validModifiers: s_validModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassStructTypeDeclarations,
+ canBePartial: true,
+ cancellationToken: cancellationToken) ||
+ syntaxTree.IsTypeParameterConstraintStartContext(position, context.LeftToken, cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/SwitchKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/SwitchKeywordRecommender.cs
new file mode 100644
index 0000000000..628463284a
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/SwitchKeywordRecommender.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class SwitchKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public SwitchKeywordRecommender()
+ : base(SyntaxKind.SwitchKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ThisKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ThisKeywordRecommender.cs
new file mode 100644
index 0000000000..ded002dfd5
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ThisKeywordRecommender.cs
@@ -0,0 +1,97 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Symbols;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class ThisKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public ThisKeywordRecommender()
+ : base(SyntaxKind.ThisKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ IsInstanceExpressionOrStatement(context) ||
+ IsExtensionMethodParameterContext(context, cancellationToken) ||
+ IsConstructorInitializerContext(context);
+ }
+
+ private static bool IsInstanceExpressionOrStatement(CSharpSyntaxContext context)
+ {
+ if (context.IsInstanceContext)
+ {
+ return context.IsNonAttributeExpressionContext || context.IsStatementContext;
+ }
+
+ return false;
+ }
+
+ private bool IsConstructorInitializerContext(CSharpSyntaxContext context)
+ {
+ // cases:
+ // Foo() : |
+
+ var token = context.TargetToken;
+
+ if (token.Kind() == SyntaxKind.ColonToken &&
+ token.Parent is ConstructorInitializerSyntax &&
+ token.Parent.IsParentKind(SyntaxKind.ConstructorDeclaration))
+ {
+ var constructor = token.GetAncestor<ConstructorDeclarationSyntax>();
+ if (constructor.Modifiers.Any(m => m.IsKind (SyntaxKind.StaticKeyword)))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ private static bool IsExtensionMethodParameterContext(CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ // TODO(cyrusn): lambda/anon methods can have out/ref parameters
+ if (!context.SyntaxTree.IsParameterModifierContext(context.Position, context.LeftToken, cancellationToken, allowableIndex: 0))
+ {
+ return false;
+ }
+
+ var token = context.LeftToken;
+ var method = token.GetAncestor<MethodDeclarationSyntax>();
+ var typeDecl = method.GetAncestorOrThis<TypeDeclarationSyntax>();
+
+ if (method == null || typeDecl == null)
+ {
+ return false;
+ }
+
+ if (typeDecl.Kind() != SyntaxKind.ClassDeclaration)
+ {
+ return false;
+ }
+
+ if (!method.Modifiers.Any(t => t.Kind() == SyntaxKind.StaticKeyword))
+ {
+ return false;
+ }
+
+ if (!typeDecl.Modifiers.Any(t => t.Kind() == SyntaxKind.StaticKeyword))
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ThrowKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ThrowKeywordRecommender.cs
new file mode 100644
index 0000000000..9572ebe9ab
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ThrowKeywordRecommender.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class ThrowKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public ThrowKeywordRecommender()
+ : base(SyntaxKind.ThrowKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/TrueKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/TrueKeywordRecommender.cs
new file mode 100644
index 0000000000..a190aae0b8
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/TrueKeywordRecommender.cs
@@ -0,0 +1,26 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class TrueKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public TrueKeywordRecommender()
+ : base(SyntaxKind.TrueKeyword, isValidInPreprocessorContext: true)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsAnyExpressionContext ||
+ context.IsPreProcessorExpressionContext ||
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ context.TargetToken.IsUnaryOperatorContext();
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/TryKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/TryKeywordRecommender.cs
new file mode 100644
index 0000000000..27945aa024
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/TryKeywordRecommender.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class TryKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public TryKeywordRecommender()
+ : base(SyntaxKind.TryKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/TypeKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/TypeKeywordRecommender.cs
new file mode 100644
index 0000000000..4d9bf61e9d
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/TypeKeywordRecommender.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class TypeKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public TypeKeywordRecommender()
+ : base(SyntaxKind.TypeKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return context.IsTypeAttributeContext(cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/TypeOfKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/TypeOfKeywordRecommender.cs
new file mode 100644
index 0000000000..c68e66d8e5
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/TypeOfKeywordRecommender.cs
@@ -0,0 +1,34 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class TypeOfKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public TypeOfKeywordRecommender()
+ : base(SyntaxKind.TypeOfKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ (context.IsAnyExpressionContext && !context.IsConstantExpressionContext) ||
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ IsAttributeArgumentContext(context);
+ }
+
+ private bool IsAttributeArgumentContext(CSharpSyntaxContext context)
+ {
+ return
+ context.IsAnyExpressionContext &&
+ context.LeftToken.GetAncestor<AttributeSyntax>() != null;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/TypeVarKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/TypeVarKeywordRecommender.cs
new file mode 100644
index 0000000000..cac63dd97a
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/TypeVarKeywordRecommender.cs
@@ -0,0 +1,51 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class TypeVarKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public TypeVarKeywordRecommender()
+ : base(SyntaxKind.TypeVarKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var token = context.TargetToken;
+
+ if (token.Kind() == SyntaxKind.OpenBracketToken &&
+ token.Parent.IsKind(SyntaxKind.AttributeList))
+ {
+ var typeParameters = token.GetAncestor<TypeParameterListSyntax>();
+ var type = typeParameters.GetAncestorOrThis<TypeDeclarationSyntax>();
+
+ if (type != null && type.TypeParameterList == typeParameters)
+ {
+ return true;
+ }
+
+ var @delegate = typeParameters.GetAncestorOrThis<DelegateDeclarationSyntax>();
+ if (@delegate != null && @delegate.TypeParameterList == typeParameters)
+ {
+ return true;
+ }
+
+ var method = typeParameters.GetAncestorOrThis<MethodDeclarationSyntax>();
+ if (method != null && method.TypeParameterList == typeParameters)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/UIntKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/UIntKeywordRecommender.cs
new file mode 100644
index 0000000000..282af54e04
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/UIntKeywordRecommender.cs
@@ -0,0 +1,48 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class UIntKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public UIntKeywordRecommender()
+ : base(SyntaxKind.UIntKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ return
+ context.IsAnyExpressionContext ||
+ context.IsDefiniteCastTypeContext ||
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ context.IsObjectCreationTypeContext ||
+ context.IsGenericTypeArgumentContext ||
+ context.IsEnumBaseListContext ||
+ context.IsIsOrAsTypeContext ||
+ context.IsLocalVariableDeclarationContext ||
+ context.IsFixedVariableDeclarationContext ||
+ context.IsParameterTypeContext ||
+ context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext ||
+ context.IsImplicitOrExplicitOperatorTypeContext ||
+ context.IsPrimaryFunctionExpressionContext ||
+ context.IsCrefContext ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.StackAllocKeyword, cancellationToken) ||
+ context.IsDelegateReturnTypeContext ||
+ syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(
+ validModifiers: SyntaxKindSet.AllMemberModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ULongKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ULongKeywordRecommender.cs
new file mode 100644
index 0000000000..08c52123d8
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/ULongKeywordRecommender.cs
@@ -0,0 +1,48 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class ULongKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public ULongKeywordRecommender()
+ : base(SyntaxKind.ULongKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ return
+ context.IsAnyExpressionContext ||
+ context.IsDefiniteCastTypeContext ||
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ context.IsObjectCreationTypeContext ||
+ context.IsGenericTypeArgumentContext ||
+ context.IsEnumBaseListContext ||
+ context.IsIsOrAsTypeContext ||
+ context.IsLocalVariableDeclarationContext ||
+ context.IsFixedVariableDeclarationContext ||
+ context.IsParameterTypeContext ||
+ context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext ||
+ context.IsImplicitOrExplicitOperatorTypeContext ||
+ context.IsPrimaryFunctionExpressionContext ||
+ context.IsCrefContext ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.StackAllocKeyword, cancellationToken) ||
+ context.IsDelegateReturnTypeContext ||
+ syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(
+ validModifiers: SyntaxKindSet.AllMemberModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/UShortKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/UShortKeywordRecommender.cs
new file mode 100644
index 0000000000..42406d6d2a
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/UShortKeywordRecommender.cs
@@ -0,0 +1,48 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class UShortKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public UShortKeywordRecommender()
+ : base(SyntaxKind.UShortKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ return
+ context.IsAnyExpressionContext ||
+ context.IsDefiniteCastTypeContext ||
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ context.IsObjectCreationTypeContext ||
+ context.IsGenericTypeArgumentContext ||
+ context.IsEnumBaseListContext ||
+ context.IsIsOrAsTypeContext ||
+ context.IsLocalVariableDeclarationContext ||
+ context.IsFixedVariableDeclarationContext ||
+ context.IsParameterTypeContext ||
+ context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext ||
+ context.IsImplicitOrExplicitOperatorTypeContext ||
+ context.IsPrimaryFunctionExpressionContext ||
+ context.IsCrefContext ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.ConstKeyword, cancellationToken) ||
+ syntaxTree.IsAfterKeyword(position, SyntaxKind.StackAllocKeyword, cancellationToken) ||
+ context.IsDelegateReturnTypeContext ||
+ syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(
+ validModifiers: SyntaxKindSet.AllMemberModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/UncheckedKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/UncheckedKeywordRecommender.cs
new file mode 100644
index 0000000000..118703f82e
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/UncheckedKeywordRecommender.cs
@@ -0,0 +1,24 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class UncheckedKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public UncheckedKeywordRecommender()
+ : base(SyntaxKind.UncheckedKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ context.IsNonAttributeExpressionContext;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/UndefKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/UndefKeywordRecommender.cs
new file mode 100644
index 0000000000..e887b40db7
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/UndefKeywordRecommender.cs
@@ -0,0 +1,25 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class UndefKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public UndefKeywordRecommender()
+ : base(SyntaxKind.UndefKeyword, isValidInPreprocessorContext: true)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ return
+ syntaxTree.IsBeforeFirstToken(position, cancellationToken) &&
+ context.IsPreProcessorKeywordContext;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/UnsafeKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/UnsafeKeywordRecommender.cs
new file mode 100644
index 0000000000..1a1accb611
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/UnsafeKeywordRecommender.cs
@@ -0,0 +1,74 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class UnsafeKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ private static readonly ISet<SyntaxKind> s_validTypeModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.AbstractKeyword,
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.NewKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.PrivateKeyword,
+ SyntaxKind.ProtectedKeyword,
+ SyntaxKind.SealedKeyword,
+ SyntaxKind.StaticKeyword,
+ };
+
+ private static readonly ISet<SyntaxKind> s_validMemberModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.AbstractKeyword,
+ SyntaxKind.ExternKeyword,
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.NewKeyword,
+ SyntaxKind.OverrideKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.PrivateKeyword,
+ SyntaxKind.ProtectedKeyword,
+ SyntaxKind.ReadOnlyKeyword,
+ SyntaxKind.SealedKeyword,
+ SyntaxKind.StaticKeyword,
+ SyntaxKind.VirtualKeyword,
+ SyntaxKind.VolatileKeyword,
+ };
+
+ private static readonly ISet<SyntaxKind> s_validGlobalMemberModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.ExternKeyword,
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.NewKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.PrivateKeyword,
+ SyntaxKind.ReadOnlyKeyword,
+ SyntaxKind.StaticKeyword,
+ SyntaxKind.VolatileKeyword,
+ };
+
+ public UnsafeKeywordRecommender()
+ : base(SyntaxKind.UnsafeKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ return
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ context.IsTypeDeclarationContext(validModifiers: s_validTypeModifiers, validTypeDeclarations: SyntaxKindSet.ClassStructTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken) ||
+ syntaxTree.IsGlobalMemberDeclarationContext(position, s_validGlobalMemberModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(
+ validModifiers: s_validMemberModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/UsingKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/UsingKeywordRecommender.cs
new file mode 100644
index 0000000000..f1dc791e6f
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/UsingKeywordRecommender.cs
@@ -0,0 +1,139 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class UsingKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public UsingKeywordRecommender()
+ : base(SyntaxKind.UsingKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ // cases:
+ // using (foo) { }
+ // using Foo;
+ // using Foo = Bar;
+ return
+ context.IsStatementContext ||
+ context.IsGlobalStatementContext ||
+ IsUsingDirectiveContext(context, cancellationToken);
+ }
+
+ private static bool IsUsingDirectiveContext(CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ // cases:
+ // root: |
+
+ // root: u|
+
+ // extern alias a;
+ // |
+
+ // extern alias a;
+ // u|
+
+ // using Foo;
+ // |
+
+ // using Foo;
+ // u|
+
+ // using Foo = Bar;
+ // |
+
+ // using Foo = Bar;
+ // u|
+
+ // t valid:
+ // namespace N {}
+ // |
+
+ // namespace N {}
+ // u|
+
+ // class C {}
+ // |
+
+ // class C {}
+ // u|
+
+ // |
+ // extern alias a;
+
+ // u|
+ // extern alias a;
+
+ var originalToken = context.LeftToken;
+ var token = context.TargetToken;
+
+ // root: u|
+
+ // ns Foo { u|
+
+ // extern alias a;
+ // u|
+
+ // using Foo;
+ // u|
+
+ // using Foo = Bar;
+ // u|
+
+ // root: |
+ if (token.Kind() == SyntaxKind.None)
+ {
+ // root namespace
+
+ // a using can't come before externs
+ var nextToken = originalToken.GetNextToken(includeSkipped: true);
+ if (nextToken.Kind() == SyntaxKind.ExternKeyword ||
+ ((CompilationUnitSyntax)context.SyntaxTree.GetRoot(cancellationToken)).Externs.Count > 0)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ if (token.Kind() == SyntaxKind.OpenBraceToken &&
+ token.Parent.IsKind(SyntaxKind.NamespaceDeclaration))
+ {
+ var ns = (NamespaceDeclarationSyntax)token.Parent;
+
+ // a child using can't come before externs
+ var nextToken = originalToken.GetNextToken(includeSkipped: true);
+ if (nextToken.Kind() == SyntaxKind.ExternKeyword)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ // extern alias a;
+ // |
+
+ // using Foo;
+ // |
+ if (token.Kind() == SyntaxKind.SemicolonToken)
+ {
+ if (token.Parent.IsKind(SyntaxKind.ExternAliasDirective) ||
+ token.Parent.IsKind(SyntaxKind.UsingDirective))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/VarKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/VarKeywordRecommender.cs
new file mode 100644
index 0000000000..4f08a6f772
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/VarKeywordRecommender.cs
@@ -0,0 +1,35 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Roslyn.Utilities;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class VarKeywordRecommender : IKeywordRecommender<CSharpSyntaxContext>
+ {
+ public VarKeywordRecommender()
+ {
+ }
+
+ private bool IsValidContext(CSharpSyntaxContext context)
+ {
+ if (context.IsStatementContext ||
+ context.IsGlobalStatementContext)
+ {
+ return true;
+ }
+
+ return context.IsLocalVariableDeclarationContext;
+ }
+
+ public IEnumerable<RecommendedKeyword> RecommendKeywords(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ if (IsValidContext(context))
+ {
+ yield return new RecommendedKeyword("var");
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/VirtualKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/VirtualKeywordRecommender.cs
new file mode 100644
index 0000000000..d98e7b1616
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/VirtualKeywordRecommender.cs
@@ -0,0 +1,37 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class VirtualKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ private static readonly ISet<SyntaxKind> s_validMemberModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.ExternKeyword,
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.NewKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.ProtectedKeyword,
+ SyntaxKind.UnsafeKeyword,
+ };
+
+ public VirtualKeywordRecommender()
+ : base(SyntaxKind.VirtualKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return context.IsMemberDeclarationContext(
+ validModifiers: s_validMemberModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassOnlyTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/VoidKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/VoidKeywordRecommender.cs
new file mode 100644
index 0000000000..4228baa2ea
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/VoidKeywordRecommender.cs
@@ -0,0 +1,112 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class VoidKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ private static readonly ISet<SyntaxKind> s_validModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.NewKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.ProtectedKeyword,
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.PrivateKeyword,
+ SyntaxKind.StaticKeyword,
+ SyntaxKind.VirtualKeyword,
+ SyntaxKind.SealedKeyword,
+ SyntaxKind.OverrideKeyword,
+ SyntaxKind.AbstractKeyword,
+ SyntaxKind.ExternKeyword,
+ SyntaxKind.UnsafeKeyword,
+ SyntaxKind.AsyncKeyword
+ };
+
+ public VoidKeywordRecommender()
+ : base(SyntaxKind.VoidKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ return
+ IsMemberReturnTypeContext(position, context, cancellationToken) ||
+ context.IsGlobalStatementContext ||
+ context.IsTypeOfExpressionContext ||
+ syntaxTree.IsSizeOfExpressionContext(position, context.LeftToken, cancellationToken) ||
+ context.IsDelegateReturnTypeContext ||
+ IsUnsafeLocalVariableDeclarationContext(context) ||
+ IsUnsafeParameterTypeContext(context) ||
+ IsUnsafeCastTypeContext(context) ||
+ IsUnsafeDefaultExpressionContext(context, cancellationToken) ||
+ context.IsFixedVariableDeclarationContext;
+ }
+
+ private bool IsUnsafeDefaultExpressionContext(CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.TargetToken.IsUnsafeContext() &&
+ context.SyntaxTree.IsDefaultExpressionContext(context.Position, context.LeftToken, cancellationToken);
+ }
+
+ private bool IsUnsafeCastTypeContext(CSharpSyntaxContext context)
+ {
+ if (context.TargetToken.IsUnsafeContext())
+ {
+ if (context.IsDefiniteCastTypeContext)
+ {
+ return true;
+ }
+
+ var token = context.TargetToken;
+
+ if (token.Kind() == SyntaxKind.OpenParenToken &&
+ token.Parent.IsKind(SyntaxKind.ParenthesizedExpression))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private bool IsUnsafeParameterTypeContext(CSharpSyntaxContext context)
+ {
+ return
+ context.TargetToken.IsUnsafeContext() &&
+ context.IsParameterTypeContext;
+ }
+
+ private bool IsUnsafeLocalVariableDeclarationContext(CSharpSyntaxContext context)
+ {
+ if (context.TargetToken.IsUnsafeContext())
+ {
+ return
+ context.IsLocalVariableDeclarationContext ||
+ context.IsStatementContext;
+ }
+
+ return false;
+ }
+
+ private bool IsMemberReturnTypeContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var syntaxTree = context.SyntaxTree;
+ return
+ syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(
+ validModifiers: s_validModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructTypeDeclarations,
+ canBePartial: true,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/VolatileKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/VolatileKeywordRecommender.cs
new file mode 100644
index 0000000000..c23381f2a2
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/VolatileKeywordRecommender.cs
@@ -0,0 +1,40 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class VolatileKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ private static readonly ISet<SyntaxKind> s_validMemberModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.NewKeyword,
+ SyntaxKind.PublicKeyword,
+ SyntaxKind.ProtectedKeyword,
+ SyntaxKind.InternalKeyword,
+ SyntaxKind.PrivateKeyword,
+ SyntaxKind.StaticKeyword,
+ };
+
+ public VolatileKeywordRecommender()
+ : base(SyntaxKind.VolatileKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ context.IsGlobalStatementContext ||
+ context.SyntaxTree.IsGlobalMemberDeclarationContext(context.Position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) ||
+ context.IsMemberDeclarationContext(
+ validModifiers: s_validMemberModifiers,
+ validTypeDeclarations: SyntaxKindSet.ClassStructTypeDeclarations,
+ canBePartial: false,
+ cancellationToken: cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/WarningKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/WarningKeywordRecommender.cs
new file mode 100644
index 0000000000..60cfc7c5c2
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/WarningKeywordRecommender.cs
@@ -0,0 +1,34 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class WarningKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public WarningKeywordRecommender()
+ : base(SyntaxKind.WarningKeyword, isValidInPreprocessorContext: true)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ // # warning
+ if (context.IsPreProcessorKeywordContext)
+ {
+ return true;
+ }
+
+ // # pragma |
+ // # pragma w|
+ var previousToken1 = context.TargetToken;
+ var previousToken2 = previousToken1.GetPreviousToken(includeSkipped: true);
+
+ return
+ previousToken1.Kind() == SyntaxKind.PragmaKeyword &&
+ previousToken2.Kind() == SyntaxKind.HashToken;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/WhenKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/WhenKeywordRecommender.cs
new file mode 100644
index 0000000000..bccfe04c80
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/WhenKeywordRecommender.cs
@@ -0,0 +1,21 @@
+//// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+//
+//using System.Threading;
+//using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+//using Microsoft.CodeAnalysis.CSharp;
+//
+//namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+//{
+// internal class WhenKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+// {
+// public WhenKeywordRecommender()
+// : base(SyntaxKind.WhenKeyword, isValidInPreprocessorContext: true)
+// {
+// }
+//
+// protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+// {
+// return context.IsCatchFilterContext;
+// }
+// }
+//}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/WhereKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/WhereKeywordRecommender.cs
new file mode 100644
index 0000000000..e0ba199c16
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/WhereKeywordRecommender.cs
@@ -0,0 +1,142 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Symbols;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class WhereKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public WhereKeywordRecommender()
+ : base(SyntaxKind.WhereKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return
+ IsQueryContext(context) ||
+ IsTypeParameterConstraintContext(context);
+ }
+
+ private bool IsTypeParameterConstraintContext(CSharpSyntaxContext context)
+ {
+ // cases:
+ // class C<T> |
+ // class C<T> : IFoo |
+ // class C<T> where T : IFoo |
+ // delegate void D<T> |
+ // delegate void D<T> where T : IFoo |
+ // void Foo<T>() |
+ // void Foo<T>() where T : IFoo |
+
+ var token = context.TargetToken;
+
+ // class C<T> |
+
+ if (token.Kind() == SyntaxKind.GreaterThanToken)
+ {
+ var typeParameters = token.GetAncestor<TypeParameterListSyntax>();
+ if (typeParameters != null && token == typeParameters.GetLastToken(includeSkipped: true))
+ {
+ var decl = typeParameters.GetAncestorOrThis<TypeDeclarationSyntax>();
+ if (decl != null && decl.TypeParameterList == typeParameters)
+ {
+ return true;
+ }
+ }
+ }
+
+ // delegate void D<T>() |
+ if (token.Kind() == SyntaxKind.CloseParenToken &&
+ token.Parent.IsKind(SyntaxKind.ParameterList) &&
+ token.Parent.IsParentKind(SyntaxKind.DelegateDeclaration))
+ {
+ var decl = token.GetAncestor<DelegateDeclarationSyntax>();
+ if (decl != null && decl.TypeParameterList != null)
+ {
+ return true;
+ }
+ }
+
+ // void Foo<T>() |
+
+ if (token.Kind() == SyntaxKind.CloseParenToken &&
+ token.Parent.IsKind(SyntaxKind.ParameterList) &&
+ token.Parent.IsParentKind(SyntaxKind.MethodDeclaration))
+ {
+ var decl = token.GetAncestor<MethodDeclarationSyntax>();
+ if (decl != null && decl.Arity > 0)
+ {
+ return true;
+ }
+ }
+
+ // class C<T> : IFoo |
+ var baseList = token.GetAncestor<BaseListSyntax>();
+ if (baseList.GetParent() is TypeDeclarationSyntax)
+ {
+ var typeDecl = baseList.GetParent() as TypeDeclarationSyntax;
+ if (typeDecl.TypeParameterList != null &&
+ typeDecl.BaseList.Types.Any(t => token == t.GetLastToken(includeSkipped: true)))
+ {
+ // token is IdentifierName "where"
+ // only suggest "where" if token's previous token is also "where"
+ if (token.Parent is IdentifierNameSyntax && token.HasMatchingText(SyntaxKind.WhereKeyword))
+ {
+ // Check for zero-width tokens in case there is a missing comma in the base list.
+ // For example: class C<T> : Foo where where |
+ return token
+ .GetPreviousToken(includeZeroWidth: true)
+ .IsKindOrHasMatchingText(SyntaxKind.WhereKeyword);
+ }
+
+ // System.|
+ // Not done typing the qualified name
+ if (token.IsKind(SyntaxKind.DotToken))
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ // class C<T> where T : IFoo |
+ // delegate void D<T> where T : IFoo |
+ var constraintClause = token.GetAncestor<TypeParameterConstraintClauseSyntax>();
+
+ if (constraintClause != null)
+ {
+ if (constraintClause.Constraints.Any(c => token == c.GetLastToken(includeSkipped: true)))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static bool IsQueryContext(CSharpSyntaxContext context)
+ {
+ var token = context.TargetToken;
+
+ // var q = from x in y
+ // |
+ if (!token.IntersectsWith(context.Position) &&
+ token.IsLastTokenOfQueryClause())
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/WhileKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/WhileKeywordRecommender.cs
new file mode 100644
index 0000000000..8185dc4a93
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/WhileKeywordRecommender.cs
@@ -0,0 +1,50 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class WhileKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public WhileKeywordRecommender()
+ : base(SyntaxKind.WhileKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ if (context.IsStatementContext ||
+ context.IsGlobalStatementContext)
+ {
+ return true;
+ }
+
+ // do {
+ // } |
+
+ // do {
+ // } w|
+
+ // Note: the case of
+ // do
+ // Foo();
+ // |
+ // is taken care of in the IsStatementContext case.
+
+ var token = context.TargetToken;
+
+ if (token.Kind() == SyntaxKind.CloseBraceToken &&
+ token.Parent.IsKind(SyntaxKind.Block) &&
+ token.Parent.IsParentKind(SyntaxKind.DoStatement))
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/YieldKeywordRecommender.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/YieldKeywordRecommender.cs
new file mode 100644
index 0000000000..cf875303bd
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Completion/KeywordRecommender/YieldKeywordRecommender.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion.KeywordRecommenders
+{
+ internal class YieldKeywordRecommender : AbstractSyntacticSingleKeywordRecommender
+ {
+ public YieldKeywordRecommender()
+ : base(SyntaxKind.YieldKeyword)
+ {
+ }
+
+ protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
+ {
+ return context.IsStatementContext;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/AbstractExtractMethodService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/AbstractExtractMethodService.cs
new file mode 100644
index 0000000000..2db2d4dc1f
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/AbstractExtractMethodService.cs
@@ -0,0 +1,45 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.Options;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public abstract class AbstractExtractMethodService<TValidator, TExtractor, TResult> : IExtractMethodService
+ where TValidator : SelectionValidator
+ where TExtractor : MethodExtractor
+ where TResult : SelectionResult
+ {
+ protected abstract TValidator CreateSelectionValidator(SemanticDocument document, TextSpan textSpan, OptionSet options);
+ protected abstract TExtractor CreateMethodExtractor(TResult selectionResult);
+
+ public async Task<ExtractMethodResult> ExtractMethodAsync(
+ Document document,
+ TextSpan textSpan,
+ OptionSet options,
+ CancellationToken cancellationToken)
+ {
+ options = options ?? document.Project.Solution.Workspace.Options;
+
+ var semanticDocument = await SemanticDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false);
+
+ var validator = this.CreateSelectionValidator(semanticDocument, textSpan, options);
+
+ var selectionResult = await validator.GetValidSelectionAsync(cancellationToken).ConfigureAwait(false);
+ if (!selectionResult.ContainsValidContext)
+ {
+ return new FailedExtractMethodResult(selectionResult.Status);
+ }
+
+ cancellationToken.ThrowIfCancellationRequested();
+
+ // extract method
+ var extractor = this.CreateMethodExtractor((TResult)selectionResult);
+
+ return await extractor.ExtractMethodAsync(cancellationToken).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/AbstractSyntaxTriviaService.Result.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/AbstractSyntaxTriviaService.Result.cs
new file mode 100644
index 0000000000..6ce04841d1
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/AbstractSyntaxTriviaService.Result.cs
@@ -0,0 +1,296 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Roslyn.Utilities;
+using ICSharpCode.NRefactory6.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public abstract partial class AbstractSyntaxTriviaService
+ {
+ private class Result : ITriviaSavedResult
+ {
+ private static readonly AnnotationResolver s_defaultAnnotationResolver = ResolveAnnotation;
+ private static readonly TriviaResolver s_defaultTriviaResolver = ResolveTrivia;
+
+ private readonly SyntaxNode _root;
+ private readonly int _endOfLineKind;
+
+ private readonly Dictionary<TriviaLocation, SyntaxAnnotation> _annotations;
+ private readonly Dictionary<TriviaLocation, IEnumerable<SyntaxTrivia>> _triviaList;
+
+ public Result(
+ SyntaxNode root,
+ int endOfLineKind,
+ Dictionary<TriviaLocation, SyntaxAnnotation> annotations,
+ Dictionary<TriviaLocation, IEnumerable<SyntaxTrivia>> triviaList)
+ {
+ //Contract.ThrowIfNull(root);
+ //Contract.ThrowIfNull(annotations);
+ //Contract.ThrowIfNull(triviaList);
+
+ _root = root;
+ _endOfLineKind = endOfLineKind;
+
+ _annotations = annotations;
+ _triviaList = triviaList;
+ }
+
+ public SyntaxNode Root
+ {
+ get { return _root; }
+ }
+
+ public SyntaxNode RestoreTrivia(
+ SyntaxNode root,
+ AnnotationResolver annotationResolver = null,
+ TriviaResolver triviaResolver = null)
+ {
+ var tokens = RecoverTokensAtEdges(root, annotationResolver);
+ var map = CreateOldToNewTokensMap(tokens, triviaResolver);
+
+ return root.ReplaceTokens(map.Keys, (o, n) => map[o]);
+ }
+
+ private Dictionary<SyntaxToken, SyntaxToken> CreateOldToNewTokensMap(
+ Dictionary<TriviaLocation, PreviousNextTokenPair> tokenPairs,
+ Dictionary<TriviaLocation, LeadingTrailingTriviaPair> triviaPairs)
+ {
+ var map = new Dictionary<SyntaxToken, SyntaxToken>();
+ foreach (var pair in CreateUniqueTokenTriviaPairs(tokenPairs, triviaPairs))
+ {
+ var localCopy = pair;
+ var previousToken = map.GetOrAdd(localCopy.Item1.PreviousToken, _ => localCopy.Item1.PreviousToken);
+ map[localCopy.Item1.PreviousToken] = previousToken.WithTrailingTrivia(localCopy.Item2.TrailingTrivia);
+
+ var nextToken = map.GetOrAdd(localCopy.Item1.NextToken, _ => localCopy.Item1.NextToken);
+ map[localCopy.Item1.NextToken] = nextToken.WithLeadingTrivia(localCopy.Item2.LeadingTrivia);
+ }
+
+ return map;
+ }
+
+ private LeadingTrailingTriviaPair GetTrailingAndLeadingTrivia(TriviaLocation locationKind, PreviousNextTokenPair tokenPair, IEnumerable<SyntaxTrivia> trivia)
+ {
+ var list = trivia.ToList();
+
+ // there are some noisy trivia
+ var index = GetFirstEndOfLineIndex(list);
+
+ return new LeadingTrailingTriviaPair
+ {
+ TrailingTrivia = CreateTriviaListFromTo(list, 0, index),
+ LeadingTrivia = CreateTriviaListFromTo(list, index + 1, list.Count - 1)
+ };
+ }
+
+ private int GetFirstEndOfLineIndex(List<SyntaxTrivia> list)
+ {
+ for (int i = 0; i < list.Count; i++)
+ {
+ if (list[i].RawKind == _endOfLineKind)
+ {
+ return i;
+ }
+ }
+
+ return list.Count - 1;
+ }
+
+ private Dictionary<TriviaLocation, SyntaxToken> RecoverTokensAtEdges(
+ SyntaxNode root,
+ AnnotationResolver annotationResolver)
+ {
+ var resolver = annotationResolver ?? s_defaultAnnotationResolver;
+
+ var tokens = Enumerable.Range((int)TriviaLocation.BeforeBeginningOfSpan, TriviaLocationsCount)
+ .Cast<TriviaLocation>()
+ .ToDictionary(
+ location => location,
+ location => resolver(root, location, _annotations[location]));
+
+ // check variable assumption. ordering of two pairs can't be changed
+ //Contract.ThrowIfFalse(
+ // (tokens[TriviaLocation.BeforeBeginningOfSpan].RawKind == 0 && tokens[TriviaLocation.AfterEndOfSpan].RawKind == 0) ||
+ // (tokens[TriviaLocation.BeforeBeginningOfSpan].RawKind == 0 /* && don't care */) ||
+ // (/* don't care && */ tokens[TriviaLocation.AfterEndOfSpan].RawKind == 0) ||
+ // (tokens[TriviaLocation.BeforeBeginningOfSpan].Span.End <= tokens[TriviaLocation.AfterEndOfSpan].SpanStart));
+
+ //Contract.ThrowIfFalse(
+ // (tokens[TriviaLocation.AfterBeginningOfSpan].RawKind == 0 && tokens[TriviaLocation.BeforeEndOfSpan].RawKind == 0) ||
+ // (tokens[TriviaLocation.AfterBeginningOfSpan].RawKind == 0 /* && don't care */) ||
+ // (/* don't care && */ tokens[TriviaLocation.BeforeEndOfSpan].RawKind == 0) ||
+ // (tokens[TriviaLocation.AfterBeginningOfSpan] == tokens[TriviaLocation.BeforeEndOfSpan]) ||
+ // (tokens[TriviaLocation.AfterBeginningOfSpan].GetPreviousToken(includeZeroWidth: true) == tokens[TriviaLocation.BeforeEndOfSpan]) ||
+ // (tokens[TriviaLocation.AfterBeginningOfSpan].Span.End <= tokens[TriviaLocation.BeforeEndOfSpan].SpanStart));
+
+ return tokens;
+ }
+
+ private Dictionary<SyntaxToken, SyntaxToken> CreateOldToNewTokensMap(
+ Dictionary<TriviaLocation, SyntaxToken> tokens,
+ TriviaResolver triviaResolver)
+ {
+ var tokenPairs = CreatePreviousNextTokenPairs(tokens);
+ var tokenToLeadingTrailingTriviaMap = CreateTokenLeadingTrailingTriviaMap(tokens);
+
+ var resolver = triviaResolver ?? s_defaultTriviaResolver;
+
+ var triviaPairs = Enumerable.Range((int)TriviaLocation.BeforeBeginningOfSpan, TriviaLocationsCount)
+ .Cast<TriviaLocation>()
+ .ToDictionary(
+ location => location,
+ location => CreateTriviaPairs(
+ location,
+ tokenPairs[location],
+ resolver(location, tokenPairs[location], tokenToLeadingTrailingTriviaMap)));
+
+ return CreateOldToNewTokensMap(tokenPairs, triviaPairs);
+ }
+
+ private LeadingTrailingTriviaPair CreateTriviaPairs(
+ TriviaLocation locationKind,
+ PreviousNextTokenPair tokenPair,
+ IEnumerable<SyntaxTrivia> trivia)
+ {
+ // beginning of the tree
+ if (tokenPair.PreviousToken.RawKind == 0)
+ {
+ return new LeadingTrailingTriviaPair { TrailingTrivia = SpecializedCollections.EmptyEnumerable<SyntaxTrivia>(), LeadingTrivia = trivia };
+ }
+
+ return GetTrailingAndLeadingTrivia(locationKind, tokenPair, trivia);
+ }
+
+ private IEnumerable<Tuple<PreviousNextTokenPair, LeadingTrailingTriviaPair>> CreateUniqueTokenTriviaPairs(
+ Dictionary<TriviaLocation, PreviousNextTokenPair> tokenPairs,
+ Dictionary<TriviaLocation, LeadingTrailingTriviaPair> triviaPairs)
+ {
+ // if there are dup, duplicated one will be ignored.
+ var set = new HashSet<PreviousNextTokenPair>();
+ for (int i = (int)TriviaLocation.BeforeBeginningOfSpan; i <= (int)TriviaLocation.AfterEndOfSpan; i++)
+ {
+ var location = (TriviaLocation)i;
+ var key = tokenPairs[location];
+ if (set.Contains(key))
+ {
+ continue;
+ }
+
+ yield return Tuple.Create(key, triviaPairs[location]);
+ set.Add(key);
+ }
+ }
+
+ private Dictionary<SyntaxToken, LeadingTrailingTriviaPair> CreateTokenLeadingTrailingTriviaMap(
+ Dictionary<TriviaLocation, SyntaxToken> tokens)
+ {
+ var tuple = default(LeadingTrailingTriviaPair);
+ var map = new Dictionary<SyntaxToken, LeadingTrailingTriviaPair>();
+
+ tuple = map.GetOrAdd(tokens[TriviaLocation.BeforeBeginningOfSpan], _ => default(LeadingTrailingTriviaPair));
+ map[tokens[TriviaLocation.BeforeBeginningOfSpan]] = new LeadingTrailingTriviaPair
+ {
+ LeadingTrivia = tuple.LeadingTrivia,
+ TrailingTrivia = _triviaList[TriviaLocation.BeforeBeginningOfSpan]
+ };
+
+ tuple = map.GetOrAdd(tokens[TriviaLocation.AfterBeginningOfSpan], _ => default(LeadingTrailingTriviaPair));
+ map[tokens[TriviaLocation.AfterBeginningOfSpan]] = new LeadingTrailingTriviaPair
+ {
+ LeadingTrivia = _triviaList[TriviaLocation.AfterBeginningOfSpan],
+ TrailingTrivia = tuple.TrailingTrivia
+ };
+
+ tuple = map.GetOrAdd(tokens[TriviaLocation.BeforeEndOfSpan], _ => default(LeadingTrailingTriviaPair));
+ map[tokens[TriviaLocation.BeforeEndOfSpan]] = new LeadingTrailingTriviaPair
+ {
+ LeadingTrivia = tuple.LeadingTrivia,
+ TrailingTrivia = _triviaList[TriviaLocation.BeforeEndOfSpan]
+ };
+
+ tuple = map.GetOrAdd(tokens[TriviaLocation.AfterEndOfSpan], _ => default(LeadingTrailingTriviaPair));
+ map[tokens[TriviaLocation.AfterEndOfSpan]] = new LeadingTrailingTriviaPair
+ {
+ LeadingTrivia = _triviaList[TriviaLocation.AfterEndOfSpan],
+ TrailingTrivia = tuple.TrailingTrivia
+ };
+
+ return map;
+ }
+
+ private Dictionary<TriviaLocation, PreviousNextTokenPair> CreatePreviousNextTokenPairs(
+ Dictionary<TriviaLocation, SyntaxToken> tokens)
+ {
+ var tokenPairs = new Dictionary<TriviaLocation, PreviousNextTokenPair>();
+
+ tokenPairs[TriviaLocation.BeforeBeginningOfSpan] = new PreviousNextTokenPair
+ {
+ PreviousToken = tokens[TriviaLocation.BeforeBeginningOfSpan],
+ NextToken = tokens[TriviaLocation.BeforeBeginningOfSpan].GetNextToken(includeZeroWidth: true)
+ };
+
+ tokenPairs[TriviaLocation.AfterBeginningOfSpan] = new PreviousNextTokenPair
+ {
+ PreviousToken = tokens[TriviaLocation.AfterBeginningOfSpan].GetPreviousToken(includeZeroWidth: true),
+ NextToken = tokens[TriviaLocation.AfterBeginningOfSpan]
+ };
+
+ tokenPairs[TriviaLocation.BeforeEndOfSpan] = new PreviousNextTokenPair
+ {
+ PreviousToken = tokens[TriviaLocation.BeforeEndOfSpan],
+ NextToken = tokens[TriviaLocation.BeforeEndOfSpan].GetNextToken(includeZeroWidth: true)
+ };
+
+ tokenPairs[TriviaLocation.AfterEndOfSpan] = new PreviousNextTokenPair
+ {
+ PreviousToken = tokens[TriviaLocation.AfterEndOfSpan].GetPreviousToken(includeZeroWidth: true),
+ NextToken = tokens[TriviaLocation.AfterEndOfSpan]
+ };
+
+ return tokenPairs;
+ }
+
+ private IEnumerable<SyntaxTrivia> CreateTriviaListFromTo(
+ List<SyntaxTrivia> list,
+ int startIndex,
+ int endIndex)
+ {
+ if (startIndex > endIndex)
+ {
+ yield break;
+ }
+
+ for (int i = startIndex; i <= endIndex; i++)
+ {
+ yield return list[i];
+ }
+ }
+
+ private static SyntaxToken ResolveAnnotation(
+ SyntaxNode root,
+ TriviaLocation location,
+ SyntaxAnnotation annotation)
+ {
+ return root.GetAnnotatedNodesAndTokens(annotation).FirstOrDefault().AsToken();
+ }
+
+ private static IEnumerable<SyntaxTrivia> ResolveTrivia(
+ TriviaLocation location,
+ PreviousNextTokenPair tokenPair,
+ Dictionary<SyntaxToken, LeadingTrailingTriviaPair> triviaMap)
+ {
+ var previousTriviaPair = triviaMap.ContainsKey(tokenPair.PreviousToken) ? triviaMap[tokenPair.PreviousToken] : default(LeadingTrailingTriviaPair);
+ var nextTriviaPair = triviaMap.ContainsKey(tokenPair.NextToken) ? triviaMap[tokenPair.NextToken] : default(LeadingTrailingTriviaPair);
+
+ var trailingTrivia = previousTriviaPair.TrailingTrivia ?? SpecializedCollections.EmptyEnumerable<SyntaxTrivia>();
+ var leadingTrivia = nextTriviaPair.LeadingTrivia ?? SpecializedCollections.EmptyEnumerable<SyntaxTrivia>();
+
+ return tokenPair.PreviousToken.TrailingTrivia.Concat(trailingTrivia).Concat(leadingTrivia).Concat(tokenPair.NextToken.LeadingTrivia);
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/AbstractSyntaxTriviaService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/AbstractSyntaxTriviaService.cs
new file mode 100644
index 0000000000..61116b6aa8
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/AbstractSyntaxTriviaService.cs
@@ -0,0 +1,131 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.Text;
+using Roslyn.Utilities;
+using ICSharpCode.NRefactory6.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public abstract partial class AbstractSyntaxTriviaService : ISyntaxTriviaService
+ {
+ private const int TriviaLocationsCount = 4;
+
+ //private readonly ISyntaxFactsService _syntaxFactsService;
+ private readonly int _endOfLineKind;
+
+ protected AbstractSyntaxTriviaService(/*ISyntaxFactsService syntaxFactsService, */int endOfLineKind)
+ {
+// _syntaxFactsService = syntaxFactsService;
+ _endOfLineKind = endOfLineKind;
+ }
+
+ public ITriviaSavedResult SaveTriviaAroundSelection(SyntaxNode root, TextSpan textSpan)
+ {
+ var tokens = GetTokensAtEdges(root, textSpan);
+
+ // span must contain after and before spans at the both edges
+
+ var triviaList = GetTriviaAtEdges(tokens, textSpan);
+
+ var annotations = Enumerable.Range((int)TriviaLocation.BeforeBeginningOfSpan, TriviaLocationsCount)
+ .Cast<TriviaLocation>()
+ .ToDictionary(location => location, _ => new SyntaxAnnotation());
+
+ var map = CreateOldToNewTokensMap(tokens, annotations);
+ var rootWithAnnotation = ReplaceTokens(root, map.Keys, (o, n) => map[o]);
+
+ return CreateResult(rootWithAnnotation, annotations, triviaList);
+ }
+
+ private SyntaxNode ReplaceTokens(
+ SyntaxNode root,
+ IEnumerable<SyntaxToken> oldTokens,
+ Func<SyntaxToken, SyntaxToken, SyntaxToken> computeReplacementToken)
+ {
+ return root.ReplaceTokens(oldTokens, (o, n) => computeReplacementToken(o, n));
+ }
+
+ private ITriviaSavedResult CreateResult(
+ SyntaxNode root,
+ Dictionary<TriviaLocation, SyntaxAnnotation> annotations,
+ Dictionary<TriviaLocation, IEnumerable<SyntaxTrivia>> triviaList)
+ {
+ return new Result(root, _endOfLineKind, annotations, triviaList);
+ }
+
+ private Dictionary<SyntaxToken, SyntaxToken> CreateOldToNewTokensMap(
+ Dictionary<TriviaLocation, SyntaxToken> tokens,
+ Dictionary<TriviaLocation, SyntaxAnnotation> annotations)
+ {
+ var token = default(SyntaxToken);
+ var map = new Dictionary<SyntaxToken, SyntaxToken>();
+ var emptyList = SpecializedCollections.EmptyEnumerable<SyntaxTrivia>();
+
+ token = map.GetOrAdd(tokens[TriviaLocation.BeforeBeginningOfSpan], _ => tokens[TriviaLocation.BeforeBeginningOfSpan]);
+ map[tokens[TriviaLocation.BeforeBeginningOfSpan]] = token.WithTrailingTrivia(emptyList).WithAdditionalAnnotations(annotations[TriviaLocation.BeforeBeginningOfSpan]);
+
+ token = map.GetOrAdd(tokens[TriviaLocation.AfterBeginningOfSpan], _ => tokens[TriviaLocation.AfterBeginningOfSpan]);
+ map[tokens[TriviaLocation.AfterBeginningOfSpan]] = token.WithLeadingTrivia(emptyList).WithAdditionalAnnotations(annotations[TriviaLocation.AfterBeginningOfSpan]);
+
+ token = map.GetOrAdd(tokens[TriviaLocation.BeforeEndOfSpan], _ => tokens[TriviaLocation.BeforeEndOfSpan]);
+ map[tokens[TriviaLocation.BeforeEndOfSpan]] = token.WithTrailingTrivia(emptyList).WithAdditionalAnnotations(annotations[TriviaLocation.BeforeEndOfSpan]);
+
+ token = map.GetOrAdd(tokens[TriviaLocation.AfterEndOfSpan], _ => tokens[TriviaLocation.AfterEndOfSpan]);
+ map[tokens[TriviaLocation.AfterEndOfSpan]] = token.WithLeadingTrivia(emptyList).WithAdditionalAnnotations(annotations[TriviaLocation.AfterEndOfSpan]);
+
+ return map;
+ }
+
+ private Dictionary<TriviaLocation, IEnumerable<SyntaxTrivia>> GetTriviaAtEdges(Dictionary<TriviaLocation, SyntaxToken> tokens, TextSpan textSpan)
+ {
+ var triviaAtBeginning = SplitTrivia(tokens[TriviaLocation.BeforeBeginningOfSpan], tokens[TriviaLocation.AfterBeginningOfSpan], t => t.FullSpan.End <= textSpan.Start);
+ var triviaAtEnd = SplitTrivia(tokens[TriviaLocation.BeforeEndOfSpan], tokens[TriviaLocation.AfterEndOfSpan], t => t.FullSpan.Start < textSpan.End);
+
+ var triviaList = new Dictionary<TriviaLocation, IEnumerable<SyntaxTrivia>>();
+ triviaList[TriviaLocation.BeforeBeginningOfSpan] = triviaAtBeginning.Item1;
+ triviaList[TriviaLocation.AfterBeginningOfSpan] = triviaAtBeginning.Item2;
+
+ triviaList[TriviaLocation.BeforeEndOfSpan] = triviaAtEnd.Item1;
+ triviaList[TriviaLocation.AfterEndOfSpan] = triviaAtEnd.Item2;
+ return triviaList;
+ }
+
+ private Dictionary<TriviaLocation, SyntaxToken> GetTokensAtEdges(SyntaxNode root, TextSpan textSpan)
+ {
+ var tokens = new Dictionary<TriviaLocation, SyntaxToken>();
+ tokens[TriviaLocation.AfterBeginningOfSpan] = root.FindTokenOnRightOfPosition(textSpan.Start, includeSkipped: false);
+ tokens[TriviaLocation.BeforeBeginningOfSpan] = tokens[TriviaLocation.AfterBeginningOfSpan].GetPreviousToken(includeZeroWidth: true);
+ tokens[TriviaLocation.BeforeEndOfSpan] = root.FindTokenOnLeftOfPosition(textSpan.End, includeSkipped: false);
+ tokens[TriviaLocation.AfterEndOfSpan] = tokens[TriviaLocation.BeforeEndOfSpan].GetNextToken(includeZeroWidth: true);
+ return tokens;
+ }
+
+ private static Tuple<List<SyntaxTrivia>, List<SyntaxTrivia>> SplitTrivia(
+ SyntaxToken token1,
+ SyntaxToken token2,
+ Func<SyntaxTrivia, bool> conditionToLeftAtCallSite)
+ {
+ var triviaLeftAtCallSite = new List<SyntaxTrivia>();
+ var triviaMovedToDefinition = new List<SyntaxTrivia>();
+
+ foreach (var trivia in token1.TrailingTrivia.Concat(token2.LeadingTrivia))
+ {
+ if (conditionToLeftAtCallSite(trivia))
+ {
+ triviaLeftAtCallSite.Add(trivia);
+ }
+ else
+ {
+ triviaMovedToDefinition.Add(trivia);
+ }
+ }
+
+ return Tuple.Create(triviaLeftAtCallSite, triviaMovedToDefinition);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpExtractMethodService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpExtractMethodService.cs
new file mode 100644
index 0000000000..dbbbfd6639
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpExtractMethodService.cs
@@ -0,0 +1,27 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Composition;
+using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.CodeAnalysis.Options;
+using Microsoft.CodeAnalysis.Text;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public class CSharpExtractMethodService : AbstractExtractMethodService<CSharpSelectionValidator, CSharpMethodExtractor, CSharpSelectionResult>
+ {
+ [ImportingConstructor]
+ public CSharpExtractMethodService()
+ {
+ }
+
+ protected override CSharpSelectionValidator CreateSelectionValidator(SemanticDocument document, TextSpan textSpan, OptionSet options)
+ {
+ return new CSharpSelectionValidator(document, textSpan, options);
+ }
+
+ protected override CSharpMethodExtractor CreateMethodExtractor(CSharpSelectionResult selectionResult)
+ {
+ return new CSharpMethodExtractor(selectionResult);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.Analyzer.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.Analyzer.cs
new file mode 100644
index 0000000000..682014ea29
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.Analyzer.cs
@@ -0,0 +1,136 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.CSharp.Symbols;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public partial class CSharpMethodExtractor : MethodExtractor
+ {
+ private class CSharpAnalyzer : Analyzer
+ {
+ private static readonly HashSet<int> s_nonNoisySyntaxKindSet = new HashSet<int>(new int[] { (int)SyntaxKind.WhitespaceTrivia, (int)SyntaxKind.EndOfLineTrivia });
+
+ public static Task<AnalyzerResult> AnalyzeAsync(SelectionResult selectionResult, CancellationToken cancellationToken)
+ {
+ var analyzer = new CSharpAnalyzer(selectionResult, cancellationToken);
+ return analyzer.AnalyzeAsync();
+ }
+
+ public CSharpAnalyzer(SelectionResult selectionResult, CancellationToken cancellationToken) :
+ base(selectionResult, cancellationToken)
+ {
+ }
+
+ protected override VariableInfo CreateFromSymbol(
+ Compilation compilation,
+ ISymbol symbol,
+ ITypeSymbol type,
+ VariableStyle style,
+ bool variableDeclared)
+ {
+ return CreateFromSymbolCommon<LocalDeclarationStatementSyntax>(compilation, symbol, type, style, s_nonNoisySyntaxKindSet);
+ }
+
+ protected override int GetIndexOfVariableInfoToUseAsReturnValue(IList<VariableInfo> variableInfo)
+ {
+ var numberOfOutParameters = 0;
+ var numberOfRefParameters = 0;
+
+ int outSymbolIndex = -1;
+ int refSymbolIndex = -1;
+
+ for (int i = 0; i < variableInfo.Count; i++)
+ {
+ var variable = variableInfo[i];
+
+ // there should be no one set as return value yet
+ //Contract.ThrowIfTrue(variable.UseAsReturnValue);
+
+ if (!variable.CanBeUsedAsReturnValue)
+ {
+ continue;
+ }
+
+ // check modifier
+ if (variable.ParameterModifier == ParameterBehavior.Ref)
+ {
+ numberOfRefParameters++;
+ refSymbolIndex = i;
+ }
+ else if (variable.ParameterModifier == ParameterBehavior.Out)
+ {
+ numberOfOutParameters++;
+ outSymbolIndex = i;
+ }
+ }
+
+ // if there is only one "out" or "ref", that will be converted to return statement.
+ if (numberOfOutParameters == 1)
+ {
+ return outSymbolIndex;
+ }
+
+ if (numberOfRefParameters == 1)
+ {
+ return refSymbolIndex;
+ }
+
+ return -1;
+ }
+
+ protected override ITypeSymbol GetRangeVariableType(SemanticModel model, IRangeVariableSymbol symbol)
+ {
+ var info = model.GetSpeculativeTypeInfo(this.SelectionResult.FinalSpan.Start, SyntaxFactory.ParseName(symbol.Name), SpeculativeBindingOption.BindAsExpression);
+ if (info.Type.IsErrorType())
+ {
+ return null;
+ }
+
+ return info.Type == null || info.Type.SpecialType == Microsoft.CodeAnalysis.SpecialType.System_Object
+ ? info.Type
+ : info.ConvertedType;
+ }
+
+ protected override Tuple<SyntaxNode, SyntaxNode> GetFlowAnalysisNodeRange()
+ {
+ var csharpSelectionResult = this.SelectionResult as CSharpSelectionResult;
+
+ var first = csharpSelectionResult.GetFirstStatement();
+ var last = csharpSelectionResult.GetLastStatement();
+
+ // single statement case
+ if (first == last ||
+ first.Span.Contains(last.Span))
+ {
+ return new Tuple<SyntaxNode, SyntaxNode>(first, first);
+ }
+
+ // multiple statement case
+ var firstUnderContainer = csharpSelectionResult.GetFirstStatementUnderContainer();
+ var lastUnderContainer = csharpSelectionResult.GetLastStatementUnderContainer();
+ return new Tuple<SyntaxNode, SyntaxNode>(firstUnderContainer, lastUnderContainer);
+ }
+
+ protected override bool ContainsReturnStatementInSelectedCode(IEnumerable<SyntaxNode> jumpOutOfRegionStatements)
+ {
+ return jumpOutOfRegionStatements.Where(n => n is ReturnStatementSyntax).Any();
+ }
+
+ protected override bool ReadOnlyFieldAllowed()
+ {
+ var scope = this.SelectionResult.GetContainingScopeOf<ConstructorDeclarationSyntax>();
+ return scope == null;
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.CSharpCodeGenerator.CallSiteContainerRewriter.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.CSharpCodeGenerator.CallSiteContainerRewriter.cs
new file mode 100644
index 0000000000..d171ae5cec
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.CSharpCodeGenerator.CallSiteContainerRewriter.cs
@@ -0,0 +1,392 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Roslyn.Utilities;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public partial class CSharpMethodExtractor
+ {
+ private abstract partial class CSharpCodeGenerator
+ {
+ private class CallSiteContainerRewriter : CSharpSyntaxRewriter
+ {
+ private readonly SyntaxNode _outmostCallSiteContainer;
+ private readonly IEnumerable<SyntaxNode> _statementsOrFieldToInsert;
+ private readonly HashSet<SyntaxAnnotation> _variableToRemoveMap;
+ private readonly SyntaxNode _firstStatementOrFieldToReplace;
+ private readonly SyntaxNode _lastStatementOrFieldToReplace;
+
+ public CallSiteContainerRewriter(
+ SyntaxNode outmostCallSiteContainer,
+ HashSet<SyntaxAnnotation> variableToRemoveMap,
+ SyntaxNode firstStatementOrFieldToReplace,
+ SyntaxNode lastStatementOrFieldToReplace,
+ IEnumerable<SyntaxNode> statementsOrFieldToInsert)
+ {
+// Contract.ThrowIfNull(outmostCallSiteContainer);
+// Contract.ThrowIfNull(variableToRemoveMap);
+// Contract.ThrowIfNull(firstStatementOrFieldToReplace);
+// Contract.ThrowIfNull(lastStatementOrFieldToReplace);
+// Contract.ThrowIfNull(statementsOrFieldToInsert);
+// Contract.ThrowIfTrue(statementsOrFieldToInsert.IsEmpty());
+
+ _outmostCallSiteContainer = outmostCallSiteContainer;
+
+ _variableToRemoveMap = variableToRemoveMap;
+ _statementsOrFieldToInsert = statementsOrFieldToInsert;
+
+ _firstStatementOrFieldToReplace = firstStatementOrFieldToReplace;
+ _lastStatementOrFieldToReplace = lastStatementOrFieldToReplace;
+
+ //Contract.ThrowIfFalse(_firstStatementOrFieldToReplace.Parent == _lastStatementOrFieldToReplace.Parent);
+ }
+
+ public SyntaxNode Generate()
+ {
+ return Visit(_outmostCallSiteContainer);
+ }
+
+ private SyntaxNode ContainerOfStatementsOrFieldToReplace
+ {
+ get { return _firstStatementOrFieldToReplace.Parent; }
+ }
+
+ public override SyntaxNode VisitLocalDeclarationStatement(LocalDeclarationStatementSyntax node)
+ {
+ node = (LocalDeclarationStatementSyntax)base.VisitLocalDeclarationStatement(node);
+
+ var list = new List<VariableDeclaratorSyntax>();
+ var triviaList = new List<SyntaxTrivia>();
+
+ // go through each var decls in decl statement
+ foreach (var variable in node.Declaration.Variables)
+ {
+ if (_variableToRemoveMap.HasSyntaxAnnotation(variable))
+ {
+ // if it had initialization, it shouldn't reach here.
+ //Contract.ThrowIfFalse(variable.Initializer == null);
+
+ // we don't remove trivia around tokens we remove
+ triviaList.AddRange(variable.GetLeadingTrivia());
+ triviaList.AddRange(variable.GetTrailingTrivia());
+ continue;
+ }
+
+ if (triviaList.Count > 0)
+ {
+ list.Add(variable.WithPrependedLeadingTrivia(triviaList));
+ triviaList.Clear();
+ continue;
+ }
+
+ list.Add(variable);
+ }
+
+ if (list.Count == 0)
+ {
+ // nothing has survived. remove this from the list
+ if (triviaList.Count == 0)
+ {
+ return null;
+ }
+
+ // well, there are trivia associated with the node.
+ // we can't just delete the node since then, we will lose
+ // the trivia. unfortunately, it is not easy to attach the trivia
+ // to next token. for now, create an empty statement and associate the
+ // trivia to the statement
+
+ // TODO : think about a way to move the trivia to next token.
+ return SyntaxFactory.EmptyStatement(SyntaxFactory.Token(SyntaxFactory.TriviaList(triviaList), SyntaxKind.SemicolonToken, SyntaxTriviaList.Create(SyntaxFactory.ElasticMarker)));
+ }
+
+ if (list.Count == node.Declaration.Variables.Count)
+ {
+ // nothing has changed, return as it is
+ return node;
+ }
+
+ // TODO : fix how it manipulate trivia later
+
+ // if there is left over syntax trivia, it will be attached to leading trivia
+ // of semicolon
+ return
+ SyntaxFactory.LocalDeclarationStatement(
+ node.Modifiers,
+ SyntaxFactory.VariableDeclaration(
+ node.Declaration.Type,
+ SyntaxFactory.SeparatedList(list)),
+ node.SemicolonToken.WithPrependedLeadingTrivia(triviaList));
+ }
+
+ // for every kind of extract methods
+ public override SyntaxNode VisitBlock(BlockSyntax node)
+ {
+ if (node != this.ContainerOfStatementsOrFieldToReplace)
+ {
+ // make sure we visit nodes under the block
+ return base.VisitBlock(node);
+ }
+
+ return node.WithStatements(VisitList(ReplaceStatements(node.Statements)).ToSyntaxList());
+ }
+
+ public override SyntaxNode VisitSwitchSection(SwitchSectionSyntax node)
+ {
+ if (node != this.ContainerOfStatementsOrFieldToReplace)
+ {
+ // make sure we visit nodes under the switch section
+ return base.VisitSwitchSection(node);
+ }
+
+ return node.WithStatements(VisitList(ReplaceStatements(node.Statements)).ToSyntaxList());
+ }
+
+ // only for single statement or expression
+ public override SyntaxNode VisitLabeledStatement(LabeledStatementSyntax node)
+ {
+ if (node != this.ContainerOfStatementsOrFieldToReplace)
+ {
+ return base.VisitLabeledStatement(node);
+ }
+
+ return node.WithStatement(ReplaceStatementIfNeeded(node.Statement));
+ }
+
+ public override SyntaxNode VisitElseClause(ElseClauseSyntax node)
+ {
+ if (node != this.ContainerOfStatementsOrFieldToReplace)
+ {
+ return base.VisitElseClause(node);
+ }
+
+ return node.WithStatement(ReplaceStatementIfNeeded(node.Statement));
+ }
+
+ public override SyntaxNode VisitIfStatement(IfStatementSyntax node)
+ {
+ if (node != this.ContainerOfStatementsOrFieldToReplace)
+ {
+ return base.VisitIfStatement(node);
+ }
+
+ return node.WithCondition(VisitNode(node.Condition))
+ .WithStatement(ReplaceStatementIfNeeded(node.Statement))
+ .WithElse(VisitNode(node.Else));
+ }
+
+ public override SyntaxNode VisitLockStatement(LockStatementSyntax node)
+ {
+ if (node != this.ContainerOfStatementsOrFieldToReplace)
+ {
+ return base.VisitLockStatement(node);
+ }
+
+ return node.WithExpression(VisitNode(node.Expression))
+ .WithStatement(ReplaceStatementIfNeeded(node.Statement));
+ }
+
+ public override SyntaxNode VisitFixedStatement(FixedStatementSyntax node)
+ {
+ if (node != this.ContainerOfStatementsOrFieldToReplace)
+ {
+ return base.VisitFixedStatement(node);
+ }
+
+ return node.WithDeclaration(VisitNode(node.Declaration))
+ .WithStatement(ReplaceStatementIfNeeded(node.Statement));
+ }
+
+ public override SyntaxNode VisitUsingStatement(UsingStatementSyntax node)
+ {
+ if (node != this.ContainerOfStatementsOrFieldToReplace)
+ {
+ return base.VisitUsingStatement(node);
+ }
+
+ return node.WithDeclaration(VisitNode(node.Declaration))
+ .WithExpression(VisitNode(node.Expression))
+ .WithStatement(ReplaceStatementIfNeeded(node.Statement));
+ }
+
+ public override SyntaxNode VisitForEachStatement(ForEachStatementSyntax node)
+ {
+ if (node != this.ContainerOfStatementsOrFieldToReplace)
+ {
+ return base.VisitForEachStatement(node);
+ }
+
+ return node.WithExpression(VisitNode(node.Expression))
+ .WithStatement(ReplaceStatementIfNeeded(node.Statement));
+ }
+
+ public override SyntaxNode VisitForStatement(ForStatementSyntax node)
+ {
+ if (node != this.ContainerOfStatementsOrFieldToReplace)
+ {
+ return base.VisitForStatement(node);
+ }
+
+ return node.WithDeclaration(VisitNode(node.Declaration))
+ .WithInitializers(VisitList(node.Initializers))
+ .WithCondition(VisitNode(node.Condition))
+ .WithIncrementors(VisitList(node.Incrementors))
+ .WithStatement(ReplaceStatementIfNeeded(node.Statement));
+ }
+
+ public override SyntaxNode VisitDoStatement(DoStatementSyntax node)
+ {
+ if (node != this.ContainerOfStatementsOrFieldToReplace)
+ {
+ return base.VisitDoStatement(node);
+ }
+
+ return node.WithStatement(ReplaceStatementIfNeeded(node.Statement))
+ .WithCondition(VisitNode(node.Condition));
+ }
+
+ public override SyntaxNode VisitWhileStatement(WhileStatementSyntax node)
+ {
+ if (node != this.ContainerOfStatementsOrFieldToReplace)
+ {
+ return base.VisitWhileStatement(node);
+ }
+
+ return node.WithCondition(VisitNode(node.Condition))
+ .WithStatement(ReplaceStatementIfNeeded(node.Statement));
+ }
+
+ private TNode VisitNode<TNode>(TNode node) where TNode : SyntaxNode
+ {
+ return (TNode)Visit(node);
+ }
+
+ private StatementSyntax ReplaceStatementIfNeeded(StatementSyntax statement)
+ {
+ //Contract.ThrowIfNull(statement);
+
+ // if all three same
+ if ((statement != _firstStatementOrFieldToReplace) || (_firstStatementOrFieldToReplace != _lastStatementOrFieldToReplace))
+ {
+ return statement;
+ }
+
+ // replace one statement with another
+ if (_statementsOrFieldToInsert.Count() == 1)
+ {
+ return _statementsOrFieldToInsert.Cast<StatementSyntax>().Single();
+ }
+
+ // replace one statement with multiple statements (see bug # 6310)
+ return SyntaxFactory.Block(SyntaxFactory.List(_statementsOrFieldToInsert.Cast<StatementSyntax>()));
+ }
+
+ private SyntaxList<StatementSyntax> ReplaceStatements(SyntaxList<StatementSyntax> statements)
+ {
+ // okay, this visit contains the statement
+ var newStatements = new List<StatementSyntax>(statements);
+
+ var firstStatementIndex = newStatements.FindIndex(s => s == _firstStatementOrFieldToReplace);
+ //Contract.ThrowIfFalse(firstStatementIndex >= 0);
+
+ var lastStatementIndex = newStatements.FindIndex(s => s == _lastStatementOrFieldToReplace);
+ //Contract.ThrowIfFalse(lastStatementIndex >= 0);
+
+ //Contract.ThrowIfFalse(firstStatementIndex <= lastStatementIndex);
+
+ // remove statement that must be removed
+ newStatements.RemoveRange(firstStatementIndex, lastStatementIndex - firstStatementIndex + 1);
+
+ // add new statements to replace
+ newStatements.InsertRange(firstStatementIndex, _statementsOrFieldToInsert.Cast<StatementSyntax>());
+
+ return newStatements.ToSyntaxList();
+ }
+
+ private SyntaxList<MemberDeclarationSyntax> ReplaceMembers(SyntaxList<MemberDeclarationSyntax> members, bool global)
+ {
+ // okay, this visit contains the statement
+ var newMembers = new List<MemberDeclarationSyntax>(members);
+
+ var firstMemberIndex = newMembers.FindIndex(s => s == (global ? _firstStatementOrFieldToReplace.Parent : _firstStatementOrFieldToReplace));
+ //Contract.ThrowIfFalse(firstMemberIndex >= 0);
+
+ var lastMemberIndex = newMembers.FindIndex(s => s == (global ? _lastStatementOrFieldToReplace.Parent : _lastStatementOrFieldToReplace));
+ //Contract.ThrowIfFalse(lastMemberIndex >= 0);
+
+ //Contract.ThrowIfFalse(firstMemberIndex <= lastMemberIndex);
+
+ // remove statement that must be removed
+ newMembers.RemoveRange(firstMemberIndex, lastMemberIndex - firstMemberIndex + 1);
+
+ // add new statements to replace
+ newMembers.InsertRange(firstMemberIndex,
+ _statementsOrFieldToInsert.Select(s => global ? SyntaxFactory.GlobalStatement((StatementSyntax)s) : (MemberDeclarationSyntax)s));
+
+ return newMembers.ToSyntaxList();
+ }
+
+ public override SyntaxNode VisitGlobalStatement(GlobalStatementSyntax node)
+ {
+ if (node != this.ContainerOfStatementsOrFieldToReplace)
+ {
+ return base.VisitGlobalStatement(node);
+ }
+
+ return node.WithStatement(ReplaceStatementIfNeeded(node.Statement));
+ }
+
+ public override SyntaxNode VisitConstructorDeclaration(ConstructorDeclarationSyntax node)
+ {
+ if (node != this.ContainerOfStatementsOrFieldToReplace)
+ {
+ return base.VisitConstructorDeclaration(node);
+ }
+
+ //Contract.ThrowIfFalse(_firstStatementOrFieldToReplace == _lastStatementOrFieldToReplace);
+ return node.WithInitializer((ConstructorInitializerSyntax)_statementsOrFieldToInsert.Single());
+ }
+
+ public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
+ {
+ if (node != this.ContainerOfStatementsOrFieldToReplace)
+ {
+ return base.VisitClassDeclaration(node);
+ }
+
+ var newMembers = VisitList(ReplaceMembers(node.Members, global: false));
+ return node.WithMembers(newMembers);
+ }
+
+ public override SyntaxNode VisitStructDeclaration(StructDeclarationSyntax node)
+ {
+ if (node != this.ContainerOfStatementsOrFieldToReplace)
+ {
+ return base.VisitStructDeclaration(node);
+ }
+
+ var newMembers = VisitList(ReplaceMembers(node.Members, global: false));
+ return node.WithMembers(newMembers);
+ }
+
+ public override SyntaxNode VisitCompilationUnit(CompilationUnitSyntax node)
+ {
+ if (node != this.ContainerOfStatementsOrFieldToReplace.Parent)
+ {
+ // make sure we visit nodes under the block
+ return base.VisitCompilationUnit(node);
+ }
+
+ var newMembers = VisitList(ReplaceMembers(node.Members, global: true));
+ return node.WithMembers(newMembers);
+ }
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.CSharpCodeGenerator.ExpressionCodeGenerator.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.CSharpCodeGenerator.ExpressionCodeGenerator.cs
new file mode 100644
index 0000000000..0bbe81fccf
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.CSharpCodeGenerator.ExpressionCodeGenerator.cs
@@ -0,0 +1,242 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public partial class CSharpMethodExtractor
+ {
+ private partial class CSharpCodeGenerator
+ {
+ private class ExpressionCodeGenerator : CSharpCodeGenerator
+ {
+ public ExpressionCodeGenerator(
+ InsertionPoint insertionPoint,
+ SelectionResult selectionResult,
+ AnalyzerResult analyzerResult) :
+ base(insertionPoint, selectionResult, analyzerResult)
+ {
+ }
+
+ public static bool IsExtractMethodOnExpression(SelectionResult code)
+ {
+ return code.SelectionInExpression;
+ }
+
+ protected override SyntaxToken CreateMethodName()
+ {
+ var methodName = "NewMethod";
+ var containingScope = this.CSharpSelectionResult.GetContainingScope();
+
+ methodName = GetMethodNameBasedOnExpression(methodName, containingScope);
+
+ var semanticModel = this.SemanticDocument.SemanticModel;
+ var nameGenerator = new UniqueNameGenerator(semanticModel);
+ return SyntaxFactory.Identifier(nameGenerator.CreateUniqueMethodName(containingScope, methodName));
+ }
+
+ private static string GetMethodNameBasedOnExpression(string methodName, SyntaxNode expression)
+ {
+ if (expression.Parent != null &&
+ expression.Parent.Kind() == SyntaxKind.EqualsValueClause &&
+ expression.Parent.Parent != null &&
+ expression.Parent.Parent.Kind() == SyntaxKind.VariableDeclarator)
+ {
+ var name = ((VariableDeclaratorSyntax)expression.Parent.Parent).Identifier.ValueText;
+ return (name != null && name.Length > 0) ? MakeMethodName("Get", name) : methodName;
+ }
+
+ if (expression is MemberAccessExpressionSyntax)
+ {
+ expression = ((MemberAccessExpressionSyntax)expression).Name;
+ }
+
+ if (expression is NameSyntax)
+ {
+ SimpleNameSyntax unqualifiedName;
+
+ switch (expression.Kind())
+ {
+ case SyntaxKind.IdentifierName:
+ case SyntaxKind.GenericName:
+ unqualifiedName = (SimpleNameSyntax)expression;
+ break;
+ case SyntaxKind.QualifiedName:
+ unqualifiedName = ((QualifiedNameSyntax)expression).Right;
+ break;
+ case SyntaxKind.AliasQualifiedName:
+ unqualifiedName = ((AliasQualifiedNameSyntax)expression).Name;
+ break;
+ default:
+ throw new System.NotSupportedException("Unexpected name kind: " + expression.Kind().ToString());
+ }
+
+ var unqualifiedNameIdentifierValueText = unqualifiedName.Identifier.ValueText;
+ return (unqualifiedNameIdentifierValueText != null && unqualifiedNameIdentifierValueText.Length > 0) ? MakeMethodName("Get", unqualifiedNameIdentifierValueText) : methodName;
+ }
+
+ return methodName;
+ }
+
+ protected override IEnumerable<StatementSyntax> GetInitialStatementsForMethodDefinitions()
+ {
+ //Contract.ThrowIfFalse(IsExtractMethodOnExpression(this.CSharpSelectionResult));
+
+ ExpressionSyntax expression = null;
+
+ // special case for array initializer
+ var returnType = (ITypeSymbol)this.AnalyzerResult.ReturnType;
+ var containingScope = this.CSharpSelectionResult.GetContainingScope();
+
+ if (returnType.TypeKind == TypeKind.Array && containingScope is InitializerExpressionSyntax)
+ {
+ var typeSyntax = returnType.GenerateTypeSyntax();
+
+ expression = SyntaxFactory.ArrayCreationExpression(typeSyntax as ArrayTypeSyntax, containingScope as InitializerExpressionSyntax);
+ }
+ else
+ {
+ expression = containingScope as ExpressionSyntax;
+ }
+
+ if (this.AnalyzerResult.HasReturnType)
+ {
+ return SpecializedCollections.SingletonEnumerable<StatementSyntax>(
+ SyntaxFactory.ReturnStatement(
+ WrapInCheckedExpressionIfNeeded(expression)));
+ }
+ else
+ {
+ return SpecializedCollections.SingletonEnumerable<StatementSyntax>(
+ SyntaxFactory.ExpressionStatement(
+ WrapInCheckedExpressionIfNeeded(expression)));
+ }
+ }
+
+ private ExpressionSyntax WrapInCheckedExpressionIfNeeded(ExpressionSyntax expression)
+ {
+ var kind = this.CSharpSelectionResult.UnderCheckedExpressionContext();
+ if (kind == SyntaxKind.None)
+ {
+ return expression;
+ }
+
+ return SyntaxFactory.CheckedExpression(kind, expression);
+ }
+
+ protected override SyntaxNode GetOutermostCallSiteContainerToProcess(CancellationToken cancellationToken)
+ {
+ var callSiteContainer = GetCallSiteContainerFromOutermostMoveInVariable(cancellationToken);
+ if (callSiteContainer != null)
+ {
+ return callSiteContainer;
+ }
+ else
+ {
+ return GetCallSiteContainerFromExpression();
+ }
+ }
+
+ private SyntaxNode GetCallSiteContainerFromExpression()
+ {
+ var container = this.CSharpSelectionResult.GetInnermostStatementContainer();
+
+// Contract.ThrowIfNull(container);
+// Contract.ThrowIfFalse(container.IsStatementContainerNode() ||
+// container is TypeDeclarationSyntax ||
+// container is ConstructorDeclarationSyntax ||
+// container is CompilationUnitSyntax);
+
+ return container;
+ }
+
+ protected override SyntaxNode GetFirstStatementOrInitializerSelectedAtCallSite()
+ {
+ var scope = (SyntaxNode)this.CSharpSelectionResult.GetContainingScopeOf<StatementSyntax>();
+ if (scope == null)
+ {
+ scope = this.CSharpSelectionResult.GetContainingScopeOf<FieldDeclarationSyntax>();
+ }
+
+ if (scope == null)
+ {
+ scope = this.CSharpSelectionResult.GetContainingScopeOf<ConstructorInitializerSyntax>();
+ }
+
+ return scope;
+ }
+
+ protected override SyntaxNode GetLastStatementOrInitializerSelectedAtCallSite()
+ {
+ return GetFirstStatementOrInitializerSelectedAtCallSite();
+ }
+
+ protected override async Task<SyntaxNode> GetStatementOrInitializerContainingInvocationToExtractedMethodAsync(
+ SyntaxAnnotation callSiteAnnotation, CancellationToken cancellationToken)
+ {
+ var enclosingStatement = GetFirstStatementOrInitializerSelectedAtCallSite();
+ var callSignature = CreateCallSignature().WithAdditionalAnnotations(callSiteAnnotation);
+ var invocation = callSignature.IsKind(SyntaxKind.AwaitExpression) ? ((AwaitExpressionSyntax)callSignature).Expression : callSignature;
+
+ var sourceNode = this.CSharpSelectionResult.GetContainingScope();
+// Contract.ThrowIfTrue(
+// sourceNode.Parent is MemberAccessExpressionSyntax && ((MemberAccessExpressionSyntax)sourceNode.Parent).Name == sourceNode,
+// "invalid scope. given scope is not an expression");
+
+ // To lower the chances that replacing sourceNode with callSignature will break the user's
+ // code, we make the enclosing statement semantically explicit. This ends up being a little
+ // bit more work because we need to annotate the sourceNode so that we can get back to it
+ // after rewriting the enclosing statement.
+ var updatedDocument = this.SemanticDocument.Document;
+ var sourceNodeAnnotation = new SyntaxAnnotation();
+ var enclosingStatementAnnotation = new SyntaxAnnotation();
+ var newEnclosingStatement = enclosingStatement
+ .ReplaceNode(sourceNode, sourceNode.WithAdditionalAnnotations(sourceNodeAnnotation))
+ .WithAdditionalAnnotations(enclosingStatementAnnotation);
+
+ updatedDocument = await updatedDocument.ReplaceNodeAsync(enclosingStatement, newEnclosingStatement, cancellationToken).ConfigureAwait(false);
+
+ var updatedRoot = await updatedDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
+ newEnclosingStatement = updatedRoot.GetAnnotatedNodesAndTokens(enclosingStatementAnnotation).Single().AsNode();
+
+ // because of the complexifiction we cannot guarantee that there is only one annotation.
+ // however complexification of names is prepended, so the last annotation should be the original one.
+ sourceNode = updatedRoot.GetAnnotatedNodesAndTokens(sourceNodeAnnotation).Last().AsNode();
+
+ // we want to replace the old identifier with a invocation expression, but because of MakeExplicit we might have
+ // a member access now instead of the identifer. So more syntax fiddling is needed.
+ if (sourceNode.Parent.Kind() == SyntaxKind.SimpleMemberAccessExpression &&
+ ((ExpressionSyntax)sourceNode).IsRightSideOfDot())
+ {
+ var explicitMemberAccess = (MemberAccessExpressionSyntax)sourceNode.Parent;
+ var replacementMemberAccess = explicitMemberAccess.CopyAnnotationsTo(
+ SyntaxFactory.MemberAccessExpression(
+ sourceNode.Parent.Kind(),
+ explicitMemberAccess.Expression,
+ (SimpleNameSyntax)((InvocationExpressionSyntax)invocation).Expression));
+ var newInvocation = SyntaxFactory.InvocationExpression(
+ replacementMemberAccess,
+ ((InvocationExpressionSyntax)invocation).ArgumentList);
+
+ var newCallSignature = callSignature != invocation ?
+ callSignature.ReplaceNode(invocation, newInvocation) : invocation.CopyAnnotationsTo(newInvocation);
+
+ sourceNode = sourceNode.Parent;
+ callSignature = newCallSignature;
+ }
+
+ return newEnclosingStatement.ReplaceNode(sourceNode, callSignature);
+ }
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.CSharpCodeGenerator.MultipleStatementsCodeGenerator.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.CSharpCodeGenerator.MultipleStatementsCodeGenerator.cs
new file mode 100644
index 0000000000..205d488fc5
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.CSharpCodeGenerator.MultipleStatementsCodeGenerator.cs
@@ -0,0 +1,139 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public partial class CSharpMethodExtractor
+ {
+ private partial class CSharpCodeGenerator
+ {
+ public class MultipleStatementsCodeGenerator : CSharpCodeGenerator
+ {
+ public MultipleStatementsCodeGenerator(
+ InsertionPoint insertionPoint,
+ SelectionResult selectionResult,
+ AnalyzerResult analyzerResult) :
+ base(insertionPoint, selectionResult, analyzerResult)
+ {
+ }
+
+ public static bool IsExtractMethodOnMultipleStatements(SelectionResult code)
+ {
+ var result = (CSharpSelectionResult)code;
+ var first = result.GetFirstStatement();
+ var last = result.GetLastStatement();
+
+ if (first != last)
+ {
+ var firstUnderContainer = result.GetFirstStatementUnderContainer();
+ var lastUnderContainer = result.GetLastStatementUnderContainer();
+ //Contract.ThrowIfFalse(firstUnderContainer.Parent == lastUnderContainer.Parent);
+ return true;
+ }
+
+ return false;
+ }
+
+ protected override SyntaxToken CreateMethodName()
+ {
+ // change this to more smarter one.
+ var semanticModel = this.SemanticDocument.SemanticModel;
+ var nameGenerator = new UniqueNameGenerator(semanticModel);
+ var scope = this.CSharpSelectionResult.GetContainingScope();
+ return SyntaxFactory.Identifier(nameGenerator.CreateUniqueMethodName(scope, "NewMethod"));
+ }
+
+ protected override IEnumerable<StatementSyntax> GetInitialStatementsForMethodDefinitions()
+ {
+ var firstSeen = false;
+ var firstStatementUnderContainer = this.CSharpSelectionResult.GetFirstStatementUnderContainer();
+ var lastStatementUnderContainer = this.CSharpSelectionResult.GetLastStatementUnderContainer();
+
+ var list = new List<StatementSyntax>();
+ foreach (var statement in GetStatementsFromContainer(firstStatementUnderContainer.Parent))
+ {
+ // reset first seen
+ if (!firstSeen)
+ {
+ firstSeen = statement == firstStatementUnderContainer;
+ }
+
+ // continue until we see the first statement
+ if (!firstSeen)
+ {
+ continue;
+ }
+
+ list.Add(statement);
+
+ // exit if we see last statement
+ if (statement == lastStatementUnderContainer)
+ {
+ break;
+ }
+ }
+
+ return list;
+ }
+
+ protected override SyntaxNode GetOutermostCallSiteContainerToProcess(CancellationToken cancellationToken)
+ {
+ var callSiteContainer = GetCallSiteContainerFromOutermostMoveInVariable(cancellationToken);
+ if (callSiteContainer != null)
+ {
+ return callSiteContainer;
+ }
+ else
+ {
+ var firstStatement = this.CSharpSelectionResult.GetFirstStatementUnderContainer();
+ return firstStatement.Parent;
+ }
+ }
+
+ private SyntaxList<StatementSyntax> GetStatementsFromContainer(SyntaxNode node)
+ {
+// Contract.ThrowIfNull(node);
+// Contract.ThrowIfFalse(node.IsStatementContainerNode());
+
+ var blockNode = node as BlockSyntax;
+ if (blockNode != null)
+ {
+ return blockNode.Statements;
+ }
+
+ var switchSecionNode = node as SwitchSectionSyntax;
+ if (switchSecionNode != null)
+ {
+ return switchSecionNode.Statements;
+ }
+
+ return new SyntaxList<StatementSyntax> ();//Contract.FailWithReturn<SyntaxList<StatementSyntax>>("unknown statements container!");
+ }
+
+ protected override SyntaxNode GetFirstStatementOrInitializerSelectedAtCallSite()
+ {
+ return this.CSharpSelectionResult.GetFirstStatementUnderContainer();
+ }
+
+ protected override SyntaxNode GetLastStatementOrInitializerSelectedAtCallSite()
+ {
+ return this.CSharpSelectionResult.GetLastStatementUnderContainer();
+ }
+
+ protected override Task<SyntaxNode> GetStatementOrInitializerContainingInvocationToExtractedMethodAsync(
+ SyntaxAnnotation callSiteAnnotation, CancellationToken cancellationToken)
+ {
+ var statement = GetStatementContainingInvocationToExtractedMethodWorker();
+ return Task.FromResult<SyntaxNode>(statement.WithAdditionalAnnotations(callSiteAnnotation));
+ }
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.CSharpCodeGenerator.SingleStatementCodeGenerator.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.CSharpCodeGenerator.SingleStatementCodeGenerator.cs
new file mode 100644
index 0000000000..de7e6ca9d1
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.CSharpCodeGenerator.SingleStatementCodeGenerator.cs
@@ -0,0 +1,87 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public partial class CSharpMethodExtractor
+ {
+ private partial class CSharpCodeGenerator
+ {
+ public class SingleStatementCodeGenerator : CSharpCodeGenerator
+ {
+ public SingleStatementCodeGenerator(
+ InsertionPoint insertionPoint,
+ SelectionResult selectionResult,
+ AnalyzerResult analyzerResult) :
+ base(insertionPoint, selectionResult, analyzerResult)
+ {
+ }
+
+ public static bool IsExtractMethodOnSingleStatement(SelectionResult code)
+ {
+ var result = (CSharpSelectionResult)code;
+ var firstStatement = result.GetFirstStatement();
+ var lastStatement = result.GetLastStatement();
+
+ return firstStatement == lastStatement || firstStatement.Span.Contains(lastStatement.Span);
+ }
+
+ protected override SyntaxToken CreateMethodName()
+ {
+ // change this to more smarter one.
+ var semanticModel = this.SemanticDocument.SemanticModel;
+ var nameGenerator = new UniqueNameGenerator(semanticModel);
+ var scope = this.CSharpSelectionResult.GetContainingScope();
+ return SyntaxFactory.Identifier(nameGenerator.CreateUniqueMethodName(scope, "NewMethod"));
+ }
+
+ protected override IEnumerable<StatementSyntax> GetInitialStatementsForMethodDefinitions()
+ {
+ // Contract.ThrowIfFalse(IsExtractMethodOnSingleStatement(this.CSharpSelectionResult));
+
+ return SpecializedCollections.SingletonEnumerable<StatementSyntax>(this.CSharpSelectionResult.GetFirstStatement());
+ }
+
+ protected override SyntaxNode GetOutermostCallSiteContainerToProcess(CancellationToken cancellationToken)
+ {
+ var callSiteContainer = GetCallSiteContainerFromOutermostMoveInVariable(cancellationToken);
+ if (callSiteContainer != null)
+ {
+ return callSiteContainer;
+ }
+ else
+ {
+ var firstStatement = this.CSharpSelectionResult.GetFirstStatement();
+ return firstStatement.Parent;
+ }
+ }
+
+ protected override SyntaxNode GetFirstStatementOrInitializerSelectedAtCallSite()
+ {
+ return this.CSharpSelectionResult.GetFirstStatement();
+ }
+
+ protected override SyntaxNode GetLastStatementOrInitializerSelectedAtCallSite()
+ {
+ // it is a single statement case. either first statement is same as last statement or
+ // last statement belongs (embedded statement) to the first statement.
+ return this.CSharpSelectionResult.GetFirstStatement();
+ }
+
+ protected override Task<SyntaxNode> GetStatementOrInitializerContainingInvocationToExtractedMethodAsync(
+ SyntaxAnnotation callSiteAnnotation, CancellationToken cancellationToken)
+ {
+ var statement = GetStatementContainingInvocationToExtractedMethodWorker();
+ return Task.FromResult<SyntaxNode>(statement.WithAdditionalAnnotations(callSiteAnnotation));
+ }
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.CSharpCodeGenerator.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.CSharpCodeGenerator.cs
new file mode 100644
index 0000000000..32bab2eebb
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.CSharpCodeGenerator.cs
@@ -0,0 +1,583 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeGeneration;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Symbols;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Editing;
+using Microsoft.CodeAnalysis.Formatting;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Simplification;
+using Roslyn.Utilities;
+using ICSharpCode.NRefactory6.CSharp.CodeGeneration;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public partial class CSharpMethodExtractor
+ {
+ private abstract partial class CSharpCodeGenerator : CodeGenerator<StatementSyntax, ExpressionSyntax, SyntaxNode>
+ {
+ private SyntaxToken _methodName;
+
+ public static async Task<GeneratedCode> GenerateAsync(
+ InsertionPoint insertionPoint,
+ SelectionResult selectionResult,
+ AnalyzerResult analyzerResult,
+ CancellationToken cancellationToken)
+ {
+ var codeGenerator = Create(insertionPoint, selectionResult, analyzerResult);
+ return await codeGenerator.GenerateAsync(cancellationToken).ConfigureAwait(false);
+ }
+
+ private static CSharpCodeGenerator Create(
+ InsertionPoint insertionPoint,
+ SelectionResult selectionResult,
+ AnalyzerResult analyzerResult)
+ {
+ if (ExpressionCodeGenerator.IsExtractMethodOnExpression(selectionResult))
+ {
+ return new ExpressionCodeGenerator(insertionPoint, selectionResult, analyzerResult);
+ }
+
+ if (SingleStatementCodeGenerator.IsExtractMethodOnSingleStatement(selectionResult))
+ {
+ return new SingleStatementCodeGenerator(insertionPoint, selectionResult, analyzerResult);
+ }
+
+ if (MultipleStatementsCodeGenerator.IsExtractMethodOnMultipleStatements(selectionResult))
+ {
+ return new MultipleStatementsCodeGenerator(insertionPoint, selectionResult, analyzerResult);
+ }
+
+ return null;//Contract.FailWithReturn<CSharpCodeGenerator>("Unknown selection");
+ }
+
+ protected CSharpCodeGenerator(
+ InsertionPoint insertionPoint,
+ SelectionResult selectionResult,
+ AnalyzerResult analyzerResult) :
+ base(insertionPoint, selectionResult, analyzerResult)
+ {
+ //Contract.ThrowIfFalse(this.SemanticDocument == selectionResult.SemanticDocument);
+
+ var nameToken = (SyntaxToken)CreateMethodName();
+ _methodName = nameToken.WithAdditionalAnnotations(this.MethodNameAnnotation);
+ }
+
+ private CSharpSelectionResult CSharpSelectionResult
+ {
+ get { return (CSharpSelectionResult)this.SelectionResult; }
+ }
+
+ protected override SyntaxNode GetPreviousMember(SemanticDocument document)
+ {
+ var node = this.InsertionPoint.With(document).GetContext();
+ return (node.Parent is GlobalStatementSyntax) ? node.Parent : node;
+ }
+
+ protected override OperationStatus<IMethodSymbol> GenerateMethodDefinition(CancellationToken cancellationToken)
+ {
+ var result = CreateMethodBody(cancellationToken);
+
+ var methodSymbol = CodeGenerationSymbolFactory.CreateMethodSymbol(
+ attributes: SpecializedCollections.EmptyList<AttributeData>(),
+ accessibility: Accessibility.Private,
+ modifiers: CreateMethodModifiers(),
+ returnType: this.AnalyzerResult.ReturnType,
+ explicitInterfaceSymbol: null,
+ name: _methodName.ToString(),
+ typeParameters: CreateMethodTypeParameters(cancellationToken),
+ parameters: CreateMethodParameters(),
+ statements: result.Data);
+
+ return result.With(
+ this.MethodDefinitionAnnotation.AddAnnotationToSymbol(
+ Formatter.Annotation.AddAnnotationToSymbol(methodSymbol)));
+ }
+
+ protected override async Task<SyntaxNode> GenerateBodyForCallSiteContainerAsync(CancellationToken cancellationToken)
+ {
+ var container = this.GetOutermostCallSiteContainerToProcess(cancellationToken);
+ var variableMapToRemove = CreateVariableDeclarationToRemoveMap(
+ this.AnalyzerResult.GetVariablesToMoveIntoMethodDefinition(cancellationToken), cancellationToken);
+ var firstStatementToRemove = GetFirstStatementOrInitializerSelectedAtCallSite();
+ var lastStatementToRemove = GetLastStatementOrInitializerSelectedAtCallSite();
+
+ //Contract.ThrowIfFalse(firstStatementToRemove.Parent == lastStatementToRemove.Parent);
+
+ var statementsToInsert = await CreateStatementsOrInitializerToInsertAtCallSiteAsync(cancellationToken).ConfigureAwait(false);
+
+ var callSiteGenerator =
+ new CallSiteContainerRewriter(
+ container,
+ variableMapToRemove,
+ firstStatementToRemove,
+ lastStatementToRemove,
+ statementsToInsert);
+
+ return container.CopyAnnotationsTo(callSiteGenerator.Generate()).WithAdditionalAnnotations(Formatter.Annotation);
+ }
+
+ private async Task<IEnumerable<SyntaxNode>> CreateStatementsOrInitializerToInsertAtCallSiteAsync(CancellationToken cancellationToken)
+ {
+ var selectedNode = this.GetFirstStatementOrInitializerSelectedAtCallSite();
+
+ // field initializer and constructor initializer case
+ if (selectedNode is ConstructorInitializerSyntax ||
+ selectedNode is FieldDeclarationSyntax)
+ {
+ var statement = await GetStatementOrInitializerContainingInvocationToExtractedMethodAsync(this.CallSiteAnnotation, cancellationToken).ConfigureAwait(false);
+ return SpecializedCollections.SingletonEnumerable(statement);
+ }
+
+ // regular case
+ var semanticModel = this.SemanticDocument.SemanticModel;
+ var context = this.InsertionPoint.GetContext();
+ var postProcessor = new PostProcessor(semanticModel, context.SpanStart);
+ var statements = SpecializedCollections.EmptyEnumerable<StatementSyntax>();
+
+ statements = AddSplitOrMoveDeclarationOutStatementsToCallSite(statements, cancellationToken);
+ statements = postProcessor.MergeDeclarationStatements(statements);
+ statements = AddAssignmentStatementToCallSite(statements, cancellationToken);
+ statements = await AddInvocationAtCallSiteAsync(statements, cancellationToken).ConfigureAwait(false);
+ statements = AddReturnIfUnreachable(statements, cancellationToken);
+
+ return statements;
+ }
+
+ private SimpleNameSyntax CreateMethodNameForInvocation()
+ {
+ return this.AnalyzerResult.MethodTypeParametersInDeclaration.Count == 0
+ ? (SimpleNameSyntax)SyntaxFactory.IdentifierName(_methodName)
+ : SyntaxFactory.GenericName(_methodName, SyntaxFactory.TypeArgumentList(CreateMethodCallTypeVariables()));
+ }
+
+ private SeparatedSyntaxList<TypeSyntax> CreateMethodCallTypeVariables()
+ {
+ //Contract.ThrowIfTrue(this.AnalyzerResult.MethodTypeParametersInDeclaration.Count == 0);
+
+ // propagate any type variable used in extracted code
+ var typeVariables = new List<TypeSyntax>();
+ foreach (var methodTypeParameter in this.AnalyzerResult.MethodTypeParametersInDeclaration)
+ {
+ typeVariables.Add(SyntaxFactory.ParseTypeName(methodTypeParameter.Name));
+ }
+
+ return SyntaxFactory.SeparatedList(typeVariables);
+ }
+
+ protected SyntaxNode GetCallSiteContainerFromOutermostMoveInVariable(CancellationToken cancellationToken)
+ {
+ var outmostVariable = GetOutermostVariableToMoveIntoMethodDefinition(cancellationToken);
+ if (outmostVariable == null)
+ {
+ return null;
+ }
+
+ var idToken = outmostVariable.GetIdentifierTokenAtDeclaration(this.SemanticDocument);
+ var declStatement = idToken.GetAncestor<LocalDeclarationStatementSyntax>();
+// Contract.ThrowIfNull(declStatement);
+// Contract.ThrowIfFalse(declStatement.Parent.IsStatementContainerNode());
+
+ return declStatement.Parent;
+ }
+
+ private DeclarationModifiers CreateMethodModifiers()
+ {
+ var isUnsafe = this.CSharpSelectionResult.ShouldPutUnsafeModifier();
+ var isAsync = this.CSharpSelectionResult.ShouldPutAsyncModifier();
+ var result = DeclarationModifiers.None;
+ if (isUnsafe)
+ result = result.WithIsUnsafe (true);
+ if (isAsync)
+ result = result.WithAsync (true);
+ if (!this.AnalyzerResult.UseInstanceMember)
+ result = result.WithIsStatic (true);
+ return result;
+ }
+
+ private static SyntaxKind GetParameterRefSyntaxKind(ParameterBehavior parameterBehavior)
+ {
+ return parameterBehavior == ParameterBehavior.Ref ?
+ SyntaxKind.RefKeyword :
+ parameterBehavior == ParameterBehavior.Out ?
+ SyntaxKind.OutKeyword : SyntaxKind.None;
+ }
+
+ private OperationStatus<List<SyntaxNode>> CreateMethodBody(CancellationToken cancellationToken)
+ {
+ var statements = GetInitialStatementsForMethodDefinitions();
+
+ statements = SplitOrMoveDeclarationIntoMethodDefinition(statements, cancellationToken);
+ statements = MoveDeclarationOutFromMethodDefinition(statements, cancellationToken);
+ statements = AppendReturnStatementIfNeeded(statements);
+ statements = CleanupCode(statements);
+
+ // set output so that we can use it in negative preview
+ var wrapped = WrapInCheckStatementIfNeeded(statements);
+ return CheckActiveStatements(statements).With(wrapped.ToList<SyntaxNode>());
+ }
+
+ private IEnumerable<StatementSyntax> WrapInCheckStatementIfNeeded(IEnumerable<StatementSyntax> statements)
+ {
+ var kind = this.CSharpSelectionResult.UnderCheckedStatementContext();
+ if (kind == SyntaxKind.None)
+ {
+ return statements;
+ }
+
+ if (statements.Skip(1).Any())
+ {
+ return SpecializedCollections.SingletonEnumerable<StatementSyntax>(SyntaxFactory.CheckedStatement(kind, SyntaxFactory.Block(statements)));
+ }
+
+ var block = statements.Single() as BlockSyntax;
+ if (block != null)
+ {
+ return SpecializedCollections.SingletonEnumerable<StatementSyntax>(SyntaxFactory.CheckedStatement(kind, block));
+ }
+
+ return SpecializedCollections.SingletonEnumerable<StatementSyntax>(SyntaxFactory.CheckedStatement(kind, SyntaxFactory.Block(statements)));
+ }
+
+ private IEnumerable<StatementSyntax> CleanupCode(IEnumerable<StatementSyntax> statements)
+ {
+ var semanticModel = this.SemanticDocument.SemanticModel;
+ var context = this.InsertionPoint.GetContext();
+ var postProcessor = new PostProcessor(semanticModel, context.SpanStart);
+
+ statements = postProcessor.RemoveRedundantBlock(statements);
+ statements = postProcessor.RemoveDeclarationAssignmentPattern(statements);
+ statements = postProcessor.RemoveInitializedDeclarationAndReturnPattern(statements);
+
+ return statements;
+ }
+
+ private OperationStatus CheckActiveStatements(IEnumerable<StatementSyntax> statements)
+ {
+ var count = statements.Count();
+ if (count == 0)
+ {
+ return OperationStatus.NoActiveStatement;
+ }
+
+ if (count == 1)
+ {
+ var returnStatement = statements.Single() as ReturnStatementSyntax;
+ if (returnStatement != null && returnStatement.Expression == null)
+ {
+ return OperationStatus.NoActiveStatement;
+ }
+ }
+
+ foreach (var statement in statements)
+ {
+ var declStatement = statement as LocalDeclarationStatementSyntax;
+ if (declStatement == null)
+ {
+ // found one
+ return OperationStatus.Succeeded;
+ }
+
+ foreach (var variable in declStatement.Declaration.Variables)
+ {
+ if (variable.Initializer != null)
+ {
+ // found one
+ return OperationStatus.Succeeded;
+ }
+ }
+ }
+
+ return OperationStatus.NoActiveStatement;
+ }
+
+ private IEnumerable<StatementSyntax> MoveDeclarationOutFromMethodDefinition(
+ IEnumerable<StatementSyntax> statements, CancellationToken cancellationToken)
+ {
+ var variableToRemoveMap = CreateVariableDeclarationToRemoveMap(
+ this.AnalyzerResult.GetVariablesToMoveOutToCallSiteOrDelete(cancellationToken), cancellationToken);
+
+ foreach (var statement in statements)
+ {
+ var declarationStatement = statement as LocalDeclarationStatementSyntax;
+ if (declarationStatement == null)
+ {
+ // if given statement is not decl statement, do nothing.
+ yield return statement;
+ continue;
+ }
+
+ var expressionStatements = new List<StatementSyntax>();
+ var list = new List<VariableDeclaratorSyntax>();
+ var triviaList = new List<SyntaxTrivia>();
+
+ // When we modify the declaration to an initialization we have to preserve the leading trivia
+ var firstVariableToAttachTrivia = true;
+
+ // go through each var decls in decl statement, and create new assignment if
+ // variable is initialized at decl.
+ foreach (var variableDeclaration in declarationStatement.Declaration.Variables)
+ {
+ if (variableToRemoveMap.HasSyntaxAnnotation(variableDeclaration))
+ {
+ if (variableDeclaration.Initializer != null)
+ {
+ SyntaxToken identifier = ApplyTriviaFromDeclarationToAssignmentIdentifier(declarationStatement, firstVariableToAttachTrivia, variableDeclaration);
+
+ // move comments with the variable here
+ expressionStatements.Add(CreateAssignmentExpressionStatement(identifier, variableDeclaration.Initializer.Value));
+ }
+ else
+ {
+ // we don't remove trivia around tokens we remove
+ triviaList.AddRange(variableDeclaration.GetLeadingTrivia());
+ triviaList.AddRange(variableDeclaration.GetTrailingTrivia());
+ }
+
+ firstVariableToAttachTrivia = false;
+ continue;
+ }
+
+ // Prepend the trivia from the declarations without initialization to the next persisting variable declaration
+ if (triviaList.Count > 0)
+ {
+ list.Add(variableDeclaration.WithPrependedLeadingTrivia(triviaList));
+ triviaList.Clear();
+ firstVariableToAttachTrivia = false;
+ continue;
+ }
+
+ firstVariableToAttachTrivia = false;
+ list.Add(variableDeclaration);
+ }
+
+ if (list.Count == 0 && triviaList.Count > 0)
+ {
+ // well, there are trivia associated with the node.
+ // we can't just delete the node since then, we will lose
+ // the trivia. unfortunately, it is not easy to attach the trivia
+ // to next token. for now, create an empty statement and associate the
+ // trivia to the statement
+
+ // TODO : think about a way to trivia attached to next token
+ yield return SyntaxFactory.EmptyStatement(SyntaxFactory.Token(SyntaxFactory.TriviaList(triviaList), SyntaxKind.SemicolonToken, SyntaxTriviaList.Create(SyntaxFactory.ElasticMarker)));
+ triviaList.Clear();
+ }
+
+ // return survived var decls
+ if (list.Count > 0)
+ {
+ yield return
+ SyntaxFactory.LocalDeclarationStatement(
+ declarationStatement.Modifiers,
+ SyntaxFactory.VariableDeclaration(
+ declarationStatement.Declaration.Type,
+ SyntaxFactory.SeparatedList(list)),
+ declarationStatement.SemicolonToken.WithPrependedLeadingTrivia(triviaList));
+ triviaList.Clear();
+ }
+
+ // return any expression statement if there was any
+ foreach (var expressionStatement in expressionStatements)
+ {
+ yield return expressionStatement;
+ }
+ }
+ }
+
+ private static SyntaxToken ApplyTriviaFromDeclarationToAssignmentIdentifier(LocalDeclarationStatementSyntax declarationStatement, bool firstVariableToAttachTrivia, VariableDeclaratorSyntax variable)
+ {
+ var identifier = variable.Identifier;
+ var typeSyntax = declarationStatement.Declaration.Type;
+ if (firstVariableToAttachTrivia && typeSyntax != null)
+ {
+ var identifierLeadingTrivia = new SyntaxTriviaList();
+
+ if (typeSyntax.HasLeadingTrivia)
+ {
+ identifierLeadingTrivia = identifierLeadingTrivia.AddRange(typeSyntax.GetLeadingTrivia());
+ }
+
+ identifierLeadingTrivia = identifierLeadingTrivia.AddRange(identifier.LeadingTrivia);
+ identifier = identifier.WithLeadingTrivia(identifierLeadingTrivia);
+ }
+
+ return identifier;
+ }
+
+ private static SyntaxToken GetIdentifierTokenAndTrivia(SyntaxToken identifier, TypeSyntax typeSyntax)
+ {
+ if (typeSyntax != null)
+ {
+ var identifierLeadingTrivia = new SyntaxTriviaList();
+ var identifierTrailingTrivia = new SyntaxTriviaList();
+ if (typeSyntax.HasLeadingTrivia)
+ {
+ identifierLeadingTrivia = identifierLeadingTrivia.AddRange(typeSyntax.GetLeadingTrivia());
+ }
+
+ if (typeSyntax.HasTrailingTrivia)
+ {
+ identifierLeadingTrivia = identifierLeadingTrivia.AddRange(typeSyntax.GetTrailingTrivia());
+ }
+
+ identifierLeadingTrivia = identifierLeadingTrivia.AddRange(identifier.LeadingTrivia);
+ identifierTrailingTrivia = identifierTrailingTrivia.AddRange(identifier.TrailingTrivia);
+ identifier = identifier.WithLeadingTrivia(identifierLeadingTrivia)
+ .WithTrailingTrivia(identifierTrailingTrivia);
+ }
+
+ return identifier;
+ }
+
+ private IEnumerable<StatementSyntax> SplitOrMoveDeclarationIntoMethodDefinition(
+ IEnumerable<StatementSyntax> statements,
+ CancellationToken cancellationToken)
+ {
+ var semanticModel = this.SemanticDocument.SemanticModel;
+ var context = this.InsertionPoint.GetContext();
+ var postProcessor = new PostProcessor(semanticModel, context.SpanStart);
+
+ var declStatements = CreateDeclarationStatements(AnalyzerResult.GetVariablesToSplitOrMoveIntoMethodDefinition(cancellationToken), cancellationToken);
+ declStatements = postProcessor.MergeDeclarationStatements(declStatements);
+
+ return declStatements.Concat(statements);
+ }
+
+ private ExpressionSyntax CreateAssignmentExpression(SyntaxToken identifier, ExpressionSyntax rvalue)
+ {
+ return SyntaxFactory.AssignmentExpression(
+ SyntaxKind.SimpleAssignmentExpression,
+ SyntaxFactory.IdentifierName(identifier),
+ rvalue);
+ }
+
+ protected override bool LastStatementOrHasReturnStatementInReturnableConstruct()
+ {
+ var lastStatement = this.GetLastStatementOrInitializerSelectedAtCallSite();
+ var container = lastStatement.GetAncestorsOrThis<SyntaxNode>().FirstOrDefault(n => n.IsReturnableConstruct());
+ if (container == null)
+ {
+ // case such as field initializer
+ return false;
+ }
+
+ var blockBody = container.GetBlockBody();
+ if (blockBody == null)
+ {
+ // such as expression lambda. there is no statement
+ return false;
+ }
+
+ // check whether it is last statement except return statement
+ var statements = blockBody.Statements;
+ if (statements.Last() == lastStatement)
+ {
+ return true;
+ }
+
+ var index = statements.IndexOf((StatementSyntax)lastStatement);
+ return statements[index + 1].Kind() == SyntaxKind.ReturnStatement;
+ }
+
+ protected override SyntaxToken CreateIdentifier(string name)
+ {
+ return SyntaxFactory.Identifier(name);
+ }
+
+ protected override StatementSyntax CreateReturnStatement(string identifierName = null)
+ {
+ return string.IsNullOrEmpty(identifierName)
+ ? SyntaxFactory.ReturnStatement()
+ : SyntaxFactory.ReturnStatement(SyntaxFactory.IdentifierName(identifierName));
+ }
+
+ protected override ExpressionSyntax CreateCallSignature()
+ {
+ var methodName = CreateMethodNameForInvocation().WithAdditionalAnnotations(Simplifier.Annotation);
+
+ var arguments = new List<ArgumentSyntax>();
+ foreach (var argument in this.AnalyzerResult.MethodParameters)
+ {
+ var modifier = GetParameterRefSyntaxKind(argument.ParameterModifier);
+ var refOrOut = modifier == SyntaxKind.None ? default(SyntaxToken) : SyntaxFactory.Token(modifier);
+
+ arguments.Add(SyntaxFactory.Argument(SyntaxFactory.IdentifierName(argument.Name)).WithRefOrOutKeyword(refOrOut));
+ }
+
+ var invocation = SyntaxFactory.InvocationExpression(methodName,
+ SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(arguments)));
+
+ var shouldPutAsyncModifier = this.CSharpSelectionResult.ShouldPutAsyncModifier();
+ if (!shouldPutAsyncModifier)
+ {
+ return invocation;
+ }
+
+ return SyntaxFactory.AwaitExpression(invocation);
+ }
+
+ protected override StatementSyntax CreateAssignmentExpressionStatement(SyntaxToken identifier, ExpressionSyntax rvalue)
+ {
+ return SyntaxFactory.ExpressionStatement(CreateAssignmentExpression((SyntaxToken)identifier, rvalue));
+ }
+
+ protected override StatementSyntax CreateDeclarationStatement(
+ VariableInfo variable,
+ CancellationToken cancellationToken,
+ ExpressionSyntax initialValue = null)
+ {
+ var type = variable.GetVariableType(this.SemanticDocument);
+ var typeNode = type.GenerateTypeSyntax();
+
+ var equalsValueClause = initialValue == null ? null : SyntaxFactory.EqualsValueClause(value: initialValue);
+
+ return SyntaxFactory.LocalDeclarationStatement(
+ SyntaxFactory.VariableDeclaration(typeNode)
+ .AddVariables(SyntaxFactory.VariableDeclarator(SyntaxFactory.Identifier(variable.Name)).WithInitializer(equalsValueClause)));
+ }
+
+ protected override async Task<GeneratedCode> CreateGeneratedCodeAsync(OperationStatus status, SemanticDocument newDocument, CancellationToken cancellationToken)
+ {
+ if (status.Succeeded())
+ {
+ // in hybrid code cases such as extract method, formatter will have some difficulties on where it breaks lines in two.
+ // here, we explicitly insert newline at the end of "{" of auto generated method decl so that anchor knows how to find out
+ // indentation of inserted statements (from users code) with user code style preserved
+ var root = newDocument.Root;
+ var methodDefinition = root.GetAnnotatedNodes<MethodDeclarationSyntax>(this.MethodDefinitionAnnotation).First();
+
+ var newMethodDefinition =
+ methodDefinition.ReplaceToken(
+ methodDefinition.Body.OpenBraceToken,
+ methodDefinition.Body.OpenBraceToken.WithAppendedTrailingTrivia(
+ SpecializedCollections.SingletonEnumerable(SyntaxFactory.CarriageReturnLineFeed)));
+
+ newDocument = await newDocument.WithSyntaxRootAsync(root.ReplaceNode(methodDefinition, newMethodDefinition), cancellationToken).ConfigureAwait(false);
+ }
+
+ return await base.CreateGeneratedCodeAsync(status, newDocument, cancellationToken).ConfigureAwait(false);
+ }
+
+ protected StatementSyntax GetStatementContainingInvocationToExtractedMethodWorker()
+ {
+ var callSignature = CreateCallSignature();
+
+ if (this.AnalyzerResult.HasReturnType)
+ {
+ //Contract.ThrowIfTrue(this.AnalyzerResult.HasVariableToUseAsReturnValue);
+ return SyntaxFactory.ReturnStatement(callSignature);
+ }
+
+ return SyntaxFactory.ExpressionStatement(callSignature);
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.FormattingProvider.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.FormattingProvider.cs
new file mode 100644
index 0000000000..b0a2c77618
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.FormattingProvider.cs
@@ -0,0 +1,66 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using Microsoft.CodeAnalysis.Formatting.Rules;
+using Microsoft.CodeAnalysis.Options;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public partial class CSharpMethodExtractor : MethodExtractor
+ {
+// private class FormattingRule : AbstractFormattingRule
+// {
+// public FormattingRule()
+// {
+// }
+//
+// public override AdjustNewLinesOperation GetAdjustNewLinesOperation(SyntaxToken previousToken, SyntaxToken currentToken, OptionSet optionSet, NextOperation<AdjustNewLinesOperation> nextOperation)
+// {
+// // for extract method case, for a hybrid case, don't force rule, but preserve user style
+// var operation = base.GetAdjustNewLinesOperation(previousToken, currentToken, optionSet, nextOperation);
+// if (operation == null)
+// {
+// return null;
+// }
+//
+// if (operation.Option == AdjustNewLinesOption.ForceLinesIfOnSingleLine)
+// {
+// return FormattingOperations.CreateAdjustNewLinesOperation(operation.Line, AdjustNewLinesOption.PreserveLines);
+// }
+//
+// if (operation.Option != AdjustNewLinesOption.ForceLines)
+// {
+// return operation;
+// }
+//
+// if (previousToken.RawKind == (int)SyntaxKind.OpenBraceToken)
+// {
+// return operation;
+// }
+//
+// if (previousToken.BetweenFieldAndNonFieldMember(currentToken))
+// {
+// // make sure to have at least 2 line breaks between field and other members except field
+// return FormattingOperations.CreateAdjustNewLinesOperation(2, AdjustNewLinesOption.PreserveLines);
+// }
+//
+// if (previousToken.HasHybridTriviaBetween(currentToken))
+// {
+// return FormattingOperations.CreateAdjustNewLinesOperation(operation.Line, AdjustNewLinesOption.PreserveLines);
+// }
+//
+// return operation;
+// }
+//
+// public override void AddAnchorIndentationOperations(List<AnchorIndentationOperation> list, SyntaxNode node, OptionSet optionSet, NextAction<AnchorIndentationOperation> nextOperation)
+// {
+// if (node.IsKind(SyntaxKind.SimpleLambdaExpression) || node.IsKind(SyntaxKind.ParenthesizedLambdaExpression) || node.IsKind(SyntaxKind.AnonymousMethodExpression))
+// {
+// return;
+// }
+//
+// nextOperation.Invoke(list);
+// }
+// }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.PostProcessor.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.PostProcessor.cs
new file mode 100644
index 0000000000..0d77d21298
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.PostProcessor.cs
@@ -0,0 +1,314 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Symbols;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public partial class CSharpMethodExtractor
+ {
+ private class PostProcessor
+ {
+ private readonly SemanticModel _semanticModel;
+ private readonly int _contextPosition;
+
+ public PostProcessor(SemanticModel semanticModel, int contextPosition)
+ {
+ //Contract.ThrowIfNull(semanticModel);
+
+ _semanticModel = semanticModel;
+ _contextPosition = contextPosition;
+ }
+
+ public IEnumerable<StatementSyntax> RemoveRedundantBlock(IEnumerable<StatementSyntax> statements)
+ {
+ // it must have only one statement
+ if (statements.Count() != 1)
+ {
+ return statements;
+ }
+
+ // that statement must be a block
+ var block = statements.Single() as BlockSyntax;
+ if (block == null)
+ {
+ return statements;
+ }
+
+ // we have a block, remove them
+ return RemoveRedundantBlock(block);
+ }
+
+ private IEnumerable<StatementSyntax> RemoveRedundantBlock(BlockSyntax block)
+ {
+ // if block doesn't have any statement
+ if (block.Statements.Count == 0)
+ {
+ // either remove the block if it doesn't have any trivia, or return as it is if
+ // there are trivia attached to block
+ return (block.OpenBraceToken.GetAllTrivia().IsEmpty() && block.CloseBraceToken.GetAllTrivia().IsEmpty()) ?
+ SpecializedCollections.EmptyEnumerable<StatementSyntax>() : SpecializedCollections.SingletonEnumerable<StatementSyntax>(block);
+ }
+
+ // okay transfer asset attached to block to statements
+ var firstStatement = block.Statements.First();
+ var firstToken = firstStatement.GetFirstToken(includeZeroWidth: true);
+ var firstTokenWithAsset = block.OpenBraceToken.CopyAnnotationsTo(firstToken).WithPrependedLeadingTrivia(block.OpenBraceToken.GetAllTrivia());
+
+ var lastStatement = block.Statements.Last();
+ var lastToken = lastStatement.GetLastToken(includeZeroWidth: true);
+ var lastTokenWithAsset = block.CloseBraceToken.CopyAnnotationsTo(lastToken).WithAppendedTrailingTrivia(block.CloseBraceToken.GetAllTrivia());
+
+ // create new block with new tokens
+ block = block.ReplaceTokens(new[] { firstToken, lastToken }, (o, c) => (o == firstToken) ? firstTokenWithAsset : lastTokenWithAsset);
+
+ // return only statements without the wrapping block
+ return block.Statements;
+ }
+
+ public IEnumerable<StatementSyntax> MergeDeclarationStatements(IEnumerable<StatementSyntax> statements)
+ {
+ if (statements.FirstOrDefault() == null)
+ {
+ return statements;
+ }
+
+ return MergeDeclarationStatementsWorker(statements);
+ }
+
+ private IEnumerable<StatementSyntax> MergeDeclarationStatementsWorker(IEnumerable<StatementSyntax> statements)
+ {
+ var map = new Dictionary<ITypeSymbol, List<LocalDeclarationStatementSyntax>>();
+ foreach (var statement in statements)
+ {
+ if (!IsDeclarationMergable(statement))
+ {
+ foreach (var declStatement in GetMergedDeclarationStatements(map))
+ {
+ yield return declStatement;
+ }
+
+ yield return statement;
+ continue;
+ }
+
+ AppendDeclarationStatementToMap(statement as LocalDeclarationStatementSyntax, map);
+ }
+
+ // merge leftover
+ if (map.Count <= 0)
+ {
+ yield break;
+ }
+
+ foreach (var declStatement in GetMergedDeclarationStatements(map))
+ {
+ yield return declStatement;
+ }
+ }
+
+ private void AppendDeclarationStatementToMap(
+ LocalDeclarationStatementSyntax statement,
+ Dictionary<ITypeSymbol, List<LocalDeclarationStatementSyntax>> map)
+ {
+ // Contract.ThrowIfNull(statement);
+
+ var type = _semanticModel.GetSpeculativeTypeInfo(_contextPosition, statement.Declaration.Type, SpeculativeBindingOption.BindAsTypeOrNamespace).Type;
+ //Contract.ThrowIfNull(type);
+
+ map.GetOrAdd(type, _ => new List<LocalDeclarationStatementSyntax>()).Add(statement);
+ }
+
+ private IEnumerable<LocalDeclarationStatementSyntax> GetMergedDeclarationStatements(
+ Dictionary<ITypeSymbol, List<LocalDeclarationStatementSyntax>> map)
+ {
+ foreach (var keyValuePair in map)
+ {
+ //Contract.ThrowIfFalse(keyValuePair.Value.Count > 0);
+
+ // merge all variable decl for current type
+ var variables = new List<VariableDeclaratorSyntax>();
+ foreach (var statement in keyValuePair.Value)
+ {
+ foreach (var variable in statement.Declaration.Variables)
+ {
+ variables.Add(variable);
+ }
+ }
+
+ // and create one decl statement
+ // use type name from the first decl statement
+ yield return
+ SyntaxFactory.LocalDeclarationStatement(
+ SyntaxFactory.VariableDeclaration(keyValuePair.Value.First().Declaration.Type, SyntaxFactory.SeparatedList(variables)));
+ }
+
+ map.Clear();
+ }
+
+ private bool IsDeclarationMergable(StatementSyntax statement)
+ {
+ //Contract.ThrowIfNull(statement);
+
+ // to be mergable, statement must be
+ // 1. decl statement without any extra info
+ // 2. no initialization on any of its decls
+ // 3. no trivia except whitespace
+ // 4. type must be known
+
+ var declarationStatement = statement as LocalDeclarationStatementSyntax;
+ if (declarationStatement == null)
+ {
+ return false;
+ }
+
+ if (declarationStatement.Modifiers.Count > 0 ||
+ declarationStatement.IsConst ||
+ declarationStatement.IsMissing)
+ {
+ return false;
+ }
+
+ if (ContainsAnyInitialization(declarationStatement))
+ {
+ return false;
+ }
+
+ if (!ContainsOnlyWhitespaceTrivia(declarationStatement))
+ {
+ return false;
+ }
+
+ var semanticInfo = _semanticModel.GetSpeculativeTypeInfo(_contextPosition, declarationStatement.Declaration.Type, SpeculativeBindingOption.BindAsTypeOrNamespace).Type;
+ if (semanticInfo == null ||
+ semanticInfo.TypeKind == TypeKind.Error ||
+ semanticInfo.TypeKind == TypeKind.Unknown)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ private bool ContainsAnyInitialization(LocalDeclarationStatementSyntax statement)
+ {
+ foreach (var variable in statement.Declaration.Variables)
+ {
+ if (variable.Initializer != null)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static bool ContainsOnlyWhitespaceTrivia(StatementSyntax statement)
+ {
+ foreach (var token in statement.DescendantTokens())
+ {
+ foreach (var trivia in token.LeadingTrivia.Concat(token.TrailingTrivia))
+ {
+ if (trivia.Kind() != SyntaxKind.WhitespaceTrivia &&
+ trivia.Kind() != SyntaxKind.EndOfLineTrivia)
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public IEnumerable<StatementSyntax> RemoveInitializedDeclarationAndReturnPattern(IEnumerable<StatementSyntax> statements)
+ {
+ // if we have inline temp variable as service, we could just use that service here.
+ // since it is not a service right now, do very simple clean up
+ if (statements.ElementAtOrDefault(2) != null)
+ {
+ return statements;
+ }
+
+ var declaration = statements.ElementAtOrDefault(0) as LocalDeclarationStatementSyntax;
+ var returnStatement = statements.ElementAtOrDefault(1) as ReturnStatementSyntax;
+ if (declaration == null || returnStatement == null)
+ {
+ return statements;
+ }
+
+ if (declaration.Declaration == null ||
+ declaration.Declaration.Variables.Count != 1 ||
+ declaration.Declaration.Variables[0].Initializer == null ||
+ declaration.Declaration.Variables[0].Initializer.Value == null ||
+ declaration.Declaration.Variables[0].Initializer.Value is StackAllocArrayCreationExpressionSyntax ||
+ returnStatement.Expression == null)
+ {
+ return statements;
+ }
+
+ if (!ContainsOnlyWhitespaceTrivia(declaration) ||
+ !ContainsOnlyWhitespaceTrivia(returnStatement))
+ {
+ return statements;
+ }
+
+ var variableName = declaration.Declaration.Variables[0].Identifier.ToString();
+ if (returnStatement.Expression.ToString() != variableName)
+ {
+ return statements;
+ }
+
+ return SpecializedCollections.SingletonEnumerable<StatementSyntax>(SyntaxFactory.ReturnStatement(declaration.Declaration.Variables[0].Initializer.Value));
+ }
+
+ public IEnumerable<StatementSyntax> RemoveDeclarationAssignmentPattern(IEnumerable<StatementSyntax> statements)
+ {
+ // if we have inline temp variable as service, we could just use that service here.
+ // since it is not a service right now, do very simple clean up
+ var declaration = statements.ElementAtOrDefault(0) as LocalDeclarationStatementSyntax;
+ var assignment = statements.ElementAtOrDefault(1) as ExpressionStatementSyntax;
+ if (declaration == null || assignment == null)
+ {
+ return statements;
+ }
+
+ if (ContainsAnyInitialization(declaration) ||
+ declaration.Declaration == null ||
+ declaration.Declaration.Variables.Count != 1 ||
+ assignment.Expression == null ||
+ assignment.Expression.Kind() != SyntaxKind.SimpleAssignmentExpression)
+ {
+ return statements;
+ }
+
+ if (!ContainsOnlyWhitespaceTrivia(declaration) ||
+ !ContainsOnlyWhitespaceTrivia(assignment))
+ {
+ return statements;
+ }
+
+ var variableName = declaration.Declaration.Variables[0].Identifier.ToString();
+
+ var assignmentExpression = assignment.Expression as AssignmentExpressionSyntax;
+ if (assignmentExpression.Left == null ||
+ assignmentExpression.Right == null ||
+ assignmentExpression.Left.ToString() != variableName)
+ {
+ return statements;
+ }
+
+ var variable = declaration.Declaration.Variables[0].WithInitializer(SyntaxFactory.EqualsValueClause(assignmentExpression.Right));
+ return SpecializedCollections.SingletonEnumerable<StatementSyntax>(
+ declaration.WithDeclaration(
+ declaration.Declaration.WithVariables(
+ SyntaxFactory.SingletonSeparatedList(variable)))).Concat(statements.Skip(2));
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.TriviaResult.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.TriviaResult.cs
new file mode 100644
index 0000000000..05012b1e51
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.TriviaResult.cs
@@ -0,0 +1,161 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public partial class CSharpMethodExtractor
+ {
+ private class CSharpTriviaResult : TriviaResult
+ {
+ public static async Task<CSharpTriviaResult> ProcessAsync(SelectionResult selectionResult, CancellationToken cancellationToken)
+ {
+ var preservationService = new CSharpSyntaxTriviaService ();
+ var root = selectionResult.SemanticDocument.Root;
+ var result = preservationService.SaveTriviaAroundSelection(root, selectionResult.FinalSpan);
+ return new CSharpTriviaResult(
+ await selectionResult.SemanticDocument.WithSyntaxRootAsync(result.Root, cancellationToken).ConfigureAwait(false),
+ result);
+ }
+
+ private CSharpTriviaResult(SemanticDocument document, ITriviaSavedResult result) :
+ base(document, result, (int)SyntaxKind.EndOfLineTrivia, (int)SyntaxKind.WhitespaceTrivia)
+ {
+ }
+
+ protected override AnnotationResolver GetAnnotationResolver(SyntaxNode callsite, SyntaxNode method)
+ {
+ var methodDefinition = method as MethodDeclarationSyntax;
+ if (callsite == null || methodDefinition == null)
+ {
+ return null;
+ }
+
+ return (node, location, annotation) => AnnotationResolver(node, location, annotation, callsite, methodDefinition);
+ }
+
+ protected override TriviaResolver GetTriviaResolver(SyntaxNode method)
+ {
+ var methodDefinition = method as MethodDeclarationSyntax;
+ if (methodDefinition == null)
+ {
+ return null;
+ }
+
+ return (location, tokenPair, triviaMap) => TriviaResolver(location, tokenPair, triviaMap, methodDefinition);
+ }
+
+ private SyntaxToken AnnotationResolver(
+ SyntaxNode node,
+ TriviaLocation location,
+ SyntaxAnnotation annotation,
+ SyntaxNode callsite,
+ MethodDeclarationSyntax method)
+ {
+ var token = node.GetAnnotatedNodesAndTokens(annotation).FirstOrDefault().AsToken();
+ if (token.RawKind != 0)
+ {
+ return token;
+ }
+
+ switch (location)
+ {
+ case TriviaLocation.BeforeBeginningOfSpan:
+ return callsite.GetFirstToken(includeZeroWidth: true).GetPreviousToken(includeZeroWidth: true);
+ case TriviaLocation.AfterEndOfSpan:
+ return callsite.GetLastToken(includeZeroWidth: true).GetNextToken(includeZeroWidth: true);
+ case TriviaLocation.AfterBeginningOfSpan:
+ return method.Body.OpenBraceToken.GetNextToken(includeZeroWidth: true);
+ case TriviaLocation.BeforeEndOfSpan:
+ return method.Body.CloseBraceToken.GetPreviousToken(includeZeroWidth: true);
+ }
+
+ return token; //Contract.FailWithReturn<SyntaxToken>("can't happen");
+ }
+
+ private IEnumerable<SyntaxTrivia> TriviaResolver(
+ TriviaLocation location,
+ PreviousNextTokenPair tokenPair,
+ Dictionary<SyntaxToken, LeadingTrailingTriviaPair> triviaMap,
+ MethodDeclarationSyntax method)
+ {
+ // Resolve trivia at the edge of the selection. simple case is easy to deal with, but complex cases where
+ // elastic trivia and user trivia are mixed (hybrid case) and we want to preserve some part of user coding style
+ // but not others can be dealt with here.
+
+ // method has no statement in them. so basically two trivia list now pointing to same thing. "{" and "}"
+ if (tokenPair.PreviousToken == method.Body.OpenBraceToken &&
+ tokenPair.NextToken == method.Body.CloseBraceToken)
+ {
+ return (location == TriviaLocation.AfterBeginningOfSpan) ?
+ SpecializedCollections.SingletonEnumerable<SyntaxTrivia>(SyntaxFactory.ElasticMarker) :
+ SpecializedCollections.EmptyEnumerable<SyntaxTrivia>();
+ }
+
+ var previousTriviaPair = triviaMap.ContainsKey(tokenPair.PreviousToken) ? triviaMap[tokenPair.PreviousToken] : default(LeadingTrailingTriviaPair);
+ var nextTriviaPair = triviaMap.ContainsKey(tokenPair.NextToken) ? triviaMap[tokenPair.NextToken] : default(LeadingTrailingTriviaPair);
+
+ var trailingTrivia = previousTriviaPair.TrailingTrivia ?? SpecializedCollections.EmptyEnumerable<SyntaxTrivia>();
+ var leadingTrivia = nextTriviaPair.LeadingTrivia ?? SpecializedCollections.EmptyEnumerable<SyntaxTrivia>();
+
+ var list = trailingTrivia.Concat(leadingTrivia);
+
+ switch (location)
+ {
+ case TriviaLocation.BeforeBeginningOfSpan:
+ return FilterBeforeBeginningOfSpan(tokenPair, list);
+ case TriviaLocation.AfterEndOfSpan:
+ return FilterTriviaList(list.Concat(tokenPair.NextToken.LeadingTrivia));
+ case TriviaLocation.AfterBeginningOfSpan:
+ return FilterTriviaList(AppendTrailingTrivia(tokenPair).Concat(list).Concat(tokenPair.NextToken.LeadingTrivia));
+ case TriviaLocation.BeforeEndOfSpan:
+ return FilterTriviaList(tokenPair.PreviousToken.TrailingTrivia.Concat(list).Concat(tokenPair.NextToken.LeadingTrivia));
+ }
+
+ return null;//Contract.FailWithReturn<IEnumerable<SyntaxTrivia>>("Shouldn't reach here");
+ }
+
+ private IEnumerable<SyntaxTrivia> FilterBeforeBeginningOfSpan(PreviousNextTokenPair tokenPair, IEnumerable<SyntaxTrivia> list)
+ {
+ var allList = FilterTriviaList(tokenPair.PreviousToken.TrailingTrivia.Concat(list).Concat(AppendLeadingTrivia(tokenPair)));
+
+ if (tokenPair.PreviousToken.RawKind == (int)SyntaxKind.OpenBraceToken)
+ {
+ return RemoveBlankLines(allList);
+ }
+
+ return allList;
+ }
+
+ private IEnumerable<SyntaxTrivia> AppendLeadingTrivia(PreviousNextTokenPair tokenPair)
+ {
+ if (tokenPair.PreviousToken.RawKind == (int)SyntaxKind.OpenBraceToken ||
+ tokenPair.PreviousToken.RawKind == (int)SyntaxKind.SemicolonToken)
+ {
+ return tokenPair.NextToken.LeadingTrivia;
+ }
+
+ return SpecializedCollections.EmptyEnumerable<SyntaxTrivia>();
+ }
+
+ private IEnumerable<SyntaxTrivia> AppendTrailingTrivia(PreviousNextTokenPair tokenPair)
+ {
+ if (tokenPair.PreviousToken.RawKind == (int)SyntaxKind.OpenBraceToken ||
+ tokenPair.PreviousToken.RawKind == (int)SyntaxKind.SemicolonToken)
+ {
+ return tokenPair.PreviousToken.TrailingTrivia;
+ }
+
+ return SpecializedCollections.EmptyEnumerable<SyntaxTrivia>();
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.cs
new file mode 100644
index 0000000000..f4ff3c45c0
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpMethodExtractor.cs
@@ -0,0 +1,128 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Formatting;
+using Microsoft.CodeAnalysis.Formatting.Rules;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Simplification;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public partial class CSharpMethodExtractor : MethodExtractor
+ {
+ public CSharpMethodExtractor(CSharpSelectionResult result) :
+ base(result)
+ {
+ }
+
+ protected override Task<AnalyzerResult> AnalyzeAsync(SelectionResult selectionResult, CancellationToken cancellationToken)
+ {
+ return CSharpAnalyzer.AnalyzeAsync(selectionResult, cancellationToken);
+ }
+
+ protected override async Task<InsertionPoint> GetInsertionPointAsync(SemanticDocument document, int position, CancellationToken cancellationToken)
+ {
+ //Contract.ThrowIfFalse(position >= 0);
+
+ var root = await document.Document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
+ var basePosition = root.FindToken(position);
+
+ var memberNode = basePosition.GetAncestor<MemberDeclarationSyntax>();
+// Contract.ThrowIfNull(memberNode);
+// Contract.ThrowIfTrue(memberNode.Kind() == SyntaxKind.NamespaceDeclaration);
+
+ var globalStatement = memberNode as GlobalStatementSyntax;
+ if (globalStatement != null)
+ {
+ // check whether we are extracting whole global statement out
+ if (this.OriginalSelectionResult.FinalSpan.Contains(memberNode.Span))
+ {
+ return await InsertionPoint.CreateAsync(document, globalStatement.Parent, cancellationToken).ConfigureAwait(false);
+ }
+
+ return await InsertionPoint.CreateAsync(document, globalStatement.Statement, cancellationToken).ConfigureAwait(false);
+ }
+
+ return await InsertionPoint.CreateAsync(document, memberNode, cancellationToken).ConfigureAwait(false);
+ }
+
+ protected override async Task<TriviaResult> PreserveTriviaAsync(SelectionResult selectionResult, CancellationToken cancellationToken)
+ {
+ return await CSharpTriviaResult.ProcessAsync(selectionResult, cancellationToken).ConfigureAwait(false);
+ }
+
+ protected override async Task<SemanticDocument> ExpandAsync(SelectionResult selection, CancellationToken cancellationToken)
+ {
+ var lastExpression = selection.GetFirstTokenInSelection().GetCommonRoot(selection.GetLastTokenInSelection()).GetAncestors<ExpressionSyntax>().LastOrDefault();
+ if (lastExpression == null)
+ {
+ return selection.SemanticDocument;
+ }
+
+ var newExpression = await Simplifier.ExpandAsync(lastExpression, selection.SemanticDocument.Document, n => n != selection.GetContainingScope(), expandParameter: false, cancellationToken: cancellationToken).ConfigureAwait(false);
+ return await selection.SemanticDocument.WithSyntaxRootAsync(selection.SemanticDocument.Root.ReplaceNode(lastExpression, newExpression), cancellationToken).ConfigureAwait(false);
+ }
+
+ protected override Task<MethodExtractor.GeneratedCode> GenerateCodeAsync(InsertionPoint insertionPoint, SelectionResult selectionResult, AnalyzerResult analyzeResult, CancellationToken cancellationToken)
+ {
+ return CSharpCodeGenerator.GenerateAsync(insertionPoint, selectionResult, analyzeResult, cancellationToken);
+ }
+
+// protected override IEnumerable<IFormattingRule> GetFormattingRules(Document document)
+// {
+// return SpecializedCollections.SingletonEnumerable(new FormattingRule()).Concat(Formatter.GetDefaultFormattingRules(document));
+// }
+
+ protected override SyntaxToken GetMethodNameAtInvocation(IEnumerable<SyntaxNodeOrToken> methodNames)
+ {
+ return (SyntaxToken)methodNames.FirstOrDefault(t => !t.Parent.IsKind(SyntaxKind.MethodDeclaration));
+ }
+
+ protected override async Task<OperationStatus> CheckTypeAsync(
+ Document document,
+ SyntaxNode contextNode,
+ Location location,
+ ITypeSymbol type,
+ CancellationToken cancellationToken)
+ {
+ //Contract.ThrowIfNull(type);
+
+ // this happens when there is no return type
+ if (type.SpecialType == SpecialType.System_Void)
+ {
+ return OperationStatus.Succeeded;
+ }
+
+ if (type.TypeKind == TypeKind.Error ||
+ type.TypeKind == TypeKind.Unknown)
+ {
+ return OperationStatus.ErrorOrUnknownType;
+ }
+
+ // if it is type parameter, make sure we are getting same type parameter
+ var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
+
+ foreach (var typeParameter in TypeParameterCollector.Collect(type))
+ {
+ var typeName = SyntaxFactory.ParseTypeName(typeParameter.Name);
+ var currentType = semanticModel.GetSpeculativeTypeInfo(contextNode.SpanStart, typeName, SpeculativeBindingOption.BindAsTypeOrNamespace).Type;
+ if (currentType == null || !currentType.Equals(typeParameter))
+ {
+ return new OperationStatus(OperationStatusFlag.BestEffort,
+ string.Format("FeaturesResources.TypeParameterIsHiddenByAnother",
+ typeParameter.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
+ currentType == null ? string.Empty : currentType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)));
+ }
+ }
+
+ return OperationStatus.Succeeded;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSelectionResult.ExpressionResult.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSelectionResult.ExpressionResult.cs
new file mode 100644
index 0000000000..62917ffccc
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSelectionResult.ExpressionResult.cs
@@ -0,0 +1,140 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Linq;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Symbols;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Options;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Text;
+using Roslyn.Utilities;
+
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public partial class CSharpSelectionResult
+ {
+ private class ExpressionResult : CSharpSelectionResult
+ {
+ public ExpressionResult(
+ OperationStatus status,
+ TextSpan originalSpan,
+ TextSpan finalSpan,
+ OptionSet options,
+ bool selectionInExpression,
+ SemanticDocument document,
+ SyntaxAnnotation firstTokenAnnotation,
+ SyntaxAnnotation lastTokenAnnotation) :
+ base(status, originalSpan, finalSpan, options, selectionInExpression, document, firstTokenAnnotation, lastTokenAnnotation)
+ {
+ }
+
+ public override bool ContainingScopeHasAsyncKeyword()
+ {
+ return false;
+ }
+
+ public override SyntaxNode GetContainingScope()
+ {
+// Contract.ThrowIfNull(this.SemanticDocument);
+// Contract.ThrowIfFalse(this.SelectionInExpression);
+
+ var firstToken = this.GetFirstTokenInSelection();
+ var lastToken = this.GetLastTokenInSelection();
+ return firstToken.GetCommonRoot(lastToken).GetAncestorOrThis<ExpressionSyntax>();
+ }
+
+ public override ITypeSymbol GetContainingScopeType()
+ {
+ var node = this.GetContainingScope();
+ var model = this.SemanticDocument.SemanticModel;
+
+ if (!node.IsExpression())
+ {
+ // Contract.Fail("this shouldn't happen");
+ return null;
+ }
+
+ // special case for array initializer and explict cast
+ if (node.IsArrayInitializer())
+ {
+ var variableDeclExpression = node.GetAncestorOrThis<VariableDeclarationSyntax>();
+ if (variableDeclExpression != null)
+ {
+ return model.GetTypeInfo(variableDeclExpression.Type).Type;
+ }
+ }
+
+ if (node.IsExpressionInCast())
+ {
+ // bug # 12774 and # 4780
+ // if the expression is under cast, we use the heuristic below
+ // 1. if regular binding returns a meaningful type, we use it as it is
+ // 2. if it doesn't, even if the cast itself wasn't included in the selection, we will treat it
+ // as it was in the selection
+ var regularType = GetRegularExpressionType(model, node);
+ if (regularType != null && !regularType.IsObjectType())
+ {
+ return regularType;
+ }
+
+ var castExpression = node.Parent as CastExpressionSyntax;
+ if (castExpression != null)
+ {
+ return model.GetTypeInfo(castExpression.Type).Type;
+ }
+ }
+
+ return GetRegularExpressionType(model, node);
+ }
+
+ private static ITypeSymbol GetRegularExpressionType(SemanticModel semanticModel, SyntaxNode node)
+ {
+ // regular case. always use ConvertedType to get implicit conversion right.
+ var expression = node.GetUnparenthesizedExpression();
+
+ var info = semanticModel.GetTypeInfo(expression);
+ var conv = semanticModel.GetConversion(expression);
+
+ if (info.ConvertedType == null || info.ConvertedType.IsErrorType())
+ {
+ // there is no implicit conversion involved. no need to go further
+ return info.Type;
+ }
+
+ // always use converted type if method group
+ if ((!node.IsKind(SyntaxKind.ObjectCreationExpression) && semanticModel.GetMemberGroup(expression).Length > 0) ||
+ IsCoClassImplicitConversion(info, conv, semanticModel.Compilation.CoClassType()))
+ {
+ return info.ConvertedType;
+ }
+
+ // check implicit conversion
+ if (conv.IsImplicit && (conv.IsConstantExpression || conv.IsEnumeration))
+ {
+ return info.ConvertedType;
+ }
+
+ // always try to use type that is more specific than object type if possible.
+ return !info.Type.IsObjectType() ? info.Type : info.ConvertedType;
+ }
+ }
+
+ private static bool IsCoClassImplicitConversion(TypeInfo info, Conversion conversion, ISymbol coclassSymbol)
+ {
+ if (!conversion.IsImplicit ||
+ info.ConvertedType == null ||
+ info.ConvertedType.TypeKind != TypeKind.Interface)
+ {
+ return false;
+ }
+
+ // let's see whether this interface has coclass attribute
+ return info.ConvertedType.GetAttributes().Any(c => c.AttributeClass.Equals(coclassSymbol));
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSelectionResult.StatementResult.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSelectionResult.StatementResult.cs
new file mode 100644
index 0000000000..5d6565e056
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSelectionResult.StatementResult.cs
@@ -0,0 +1,88 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Linq;
+using Microsoft.CodeAnalysis.CSharp.Symbols;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Options;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Text;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public partial class CSharpSelectionResult
+ {
+ private class StatementResult : CSharpSelectionResult
+ {
+ public StatementResult(
+ OperationStatus status,
+ TextSpan originalSpan,
+ TextSpan finalSpan,
+ OptionSet options,
+ bool selectionInExpression,
+ SemanticDocument document,
+ SyntaxAnnotation firstTokenAnnotation,
+ SyntaxAnnotation lastTokenAnnotation) :
+ base(status, originalSpan, finalSpan, options, selectionInExpression, document, firstTokenAnnotation, lastTokenAnnotation)
+ {
+ }
+
+ public override bool ContainingScopeHasAsyncKeyword()
+ {
+ var node = this.GetContainingScope();
+ var semanticModel = this.SemanticDocument.SemanticModel;
+
+ return node.TypeSwitch(
+ (AccessorDeclarationSyntax access) => false,
+ (MethodDeclarationSyntax method) => method.Modifiers.Any(SyntaxKind.AsyncKeyword),
+ (ParenthesizedLambdaExpressionSyntax lambda) => lambda.AsyncKeyword.Kind() == SyntaxKind.AsyncKeyword,
+ (SimpleLambdaExpressionSyntax lambda) => lambda.AsyncKeyword.Kind() == SyntaxKind.AsyncKeyword,
+ (AnonymousMethodExpressionSyntax anonymous) => anonymous.AsyncKeyword.Kind() == SyntaxKind.AsyncKeyword);
+ }
+
+ public override SyntaxNode GetContainingScope()
+ {
+// Contract.ThrowIfNull(this.SemanticDocument);
+// Contract.ThrowIfTrue(this.SelectionInExpression);
+
+ // it contains statements
+ var firstToken = this.GetFirstTokenInSelection();
+ return firstToken.GetAncestors<SyntaxNode>().FirstOrDefault(n =>
+ {
+ return n is BaseMethodDeclarationSyntax ||
+ n is AccessorDeclarationSyntax ||
+ n is ParenthesizedLambdaExpressionSyntax ||
+ n is SimpleLambdaExpressionSyntax ||
+ n is AnonymousMethodExpressionSyntax ||
+ n is CompilationUnitSyntax;
+ });
+ }
+
+ public override ITypeSymbol GetContainingScopeType()
+ {
+ //Contract.ThrowIfTrue(this.SelectionInExpression);
+
+ var node = this.GetContainingScope();
+ var semanticModel = this.SemanticDocument.SemanticModel;
+
+ return node.TypeSwitch(
+ (AccessorDeclarationSyntax access) =>
+ {
+ // property case
+ if (access.Parent == null || access.Parent.Parent == null)
+ {
+ return null;
+ }
+
+ return ((IPropertySymbol)semanticModel.GetDeclaredSymbol(access.Parent.Parent)).Type;
+ },
+ (MethodDeclarationSyntax method) => ((IMethodSymbol)semanticModel.GetDeclaredSymbol(method)).ReturnType,
+ (ParenthesizedLambdaExpressionSyntax lambda) => semanticModel.GetLambdaOrAnonymousMethodReturnType(lambda),
+ (SimpleLambdaExpressionSyntax lambda) => semanticModel.GetLambdaOrAnonymousMethodReturnType(lambda),
+ (AnonymousMethodExpressionSyntax anonymous) => semanticModel.GetLambdaOrAnonymousMethodReturnType(anonymous));
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSelectionResult.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSelectionResult.cs
new file mode 100644
index 0000000000..80f343c09a
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSelectionResult.cs
@@ -0,0 +1,207 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Symbols;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Options;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Text;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public abstract partial class CSharpSelectionResult : SelectionResult
+ {
+ public static async Task<CSharpSelectionResult> CreateAsync(
+ OperationStatus status,
+ TextSpan originalSpan,
+ TextSpan finalSpan,
+ OptionSet options,
+ bool selectionInExpression,
+ SemanticDocument document,
+ SyntaxToken firstToken,
+ SyntaxToken lastToken,
+ CancellationToken cancellationToken)
+ {
+// Contract.ThrowIfNull(status);
+// Contract.ThrowIfNull(document);
+
+ var firstTokenAnnotation = new SyntaxAnnotation();
+ var lastTokenAnnotation = new SyntaxAnnotation();
+
+ var root = await document.Document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
+ var newDocument = await SemanticDocument.CreateAsync(document.Document.WithSyntaxRoot(root.AddAnnotations(
+ new[]
+ {
+ Tuple.Create<SyntaxToken, SyntaxAnnotation>(firstToken, firstTokenAnnotation),
+ Tuple.Create<SyntaxToken, SyntaxAnnotation>(lastToken, lastTokenAnnotation)
+ })), cancellationToken).ConfigureAwait(false);
+
+ if (selectionInExpression)
+ {
+ return new ExpressionResult(
+ status, originalSpan, finalSpan, options, selectionInExpression,
+ newDocument, firstTokenAnnotation, lastTokenAnnotation);
+ }
+ else
+ {
+ return new StatementResult(
+ status, originalSpan, finalSpan, options, selectionInExpression,
+ newDocument, firstTokenAnnotation, lastTokenAnnotation);
+ }
+ }
+
+ protected CSharpSelectionResult(
+ OperationStatus status,
+ TextSpan originalSpan,
+ TextSpan finalSpan,
+ OptionSet options,
+ bool selectionInExpression,
+ SemanticDocument document,
+ SyntaxAnnotation firstTokenAnnotation,
+ SyntaxAnnotation lastTokenAnnotation) :
+ base(status, originalSpan, finalSpan, options, selectionInExpression,
+ document, firstTokenAnnotation, lastTokenAnnotation)
+ {
+ }
+
+ protected override bool UnderAsyncAnonymousMethod(SyntaxToken token, SyntaxToken firstToken, SyntaxToken lastToken)
+ {
+ var current = token.Parent;
+ for (; current != null; current = current.Parent)
+ {
+ if (current is MemberDeclarationSyntax ||
+ current is SimpleLambdaExpressionSyntax ||
+ current is ParenthesizedLambdaExpressionSyntax ||
+ current is AnonymousMethodExpressionSyntax)
+ {
+ break;
+ }
+ }
+
+ if (current == null || current is MemberDeclarationSyntax)
+ {
+ return false;
+ }
+
+ // make sure the selection contains the lambda
+ return firstToken.SpanStart <= current.GetFirstToken().SpanStart &&
+ current.GetLastToken().Span.End <= lastToken.Span.End;
+ }
+
+ public StatementSyntax GetFirstStatement()
+ {
+ return GetFirstStatement<StatementSyntax>();
+ }
+
+ public StatementSyntax GetLastStatement()
+ {
+ return GetLastStatement<StatementSyntax>();
+ }
+
+ public StatementSyntax GetFirstStatementUnderContainer()
+ {
+ //Contract.ThrowIfTrue(this.SelectionInExpression);
+
+ var firstToken = this.GetFirstTokenInSelection();
+ var statement = firstToken.Parent.GetStatementUnderContainer();
+ //Contract.ThrowIfNull(statement);
+
+ return statement;
+ }
+
+ public StatementSyntax GetLastStatementUnderContainer()
+ {
+ //Contract.ThrowIfTrue(this.SelectionInExpression);
+
+ var lastToken = this.GetLastTokenInSelection();
+ var statement = lastToken.Parent.GetStatementUnderContainer();
+
+ //Contract.ThrowIfNull(statement);
+ var firstStatementUnderContainer = this.GetFirstStatementUnderContainer();
+ //Contract.ThrowIfFalse(statement.Parent == firstStatementUnderContainer.Parent);
+
+ return statement;
+ }
+
+ public SyntaxNode GetInnermostStatementContainer()
+ {
+ //Contract.ThrowIfFalse(this.SelectionInExpression);
+ var containingScope = this.GetContainingScope();
+ var statements = containingScope.GetAncestorsOrThis<StatementSyntax>();
+ StatementSyntax last = null;
+
+ foreach (var statement in statements)
+ {
+ if (statement.IsStatementContainerNode())
+ {
+ return statement;
+ }
+
+ last = statement;
+ }
+
+ // constructor initializer case
+ var constructorInitializer = this.GetContainingScopeOf<ConstructorInitializerSyntax>();
+ if (constructorInitializer != null)
+ {
+ return constructorInitializer.Parent;
+ }
+
+ // field initializer case
+ var field = this.GetContainingScopeOf<FieldDeclarationSyntax>();
+ if (field != null)
+ {
+ return field.Parent;
+ }
+
+// Contract.ThrowIfFalse(last.IsParentKind(SyntaxKind.GlobalStatement));
+// Contract.ThrowIfFalse(last.Parent.IsParentKind(SyntaxKind.CompilationUnit));
+ return last.Parent.Parent;
+ }
+
+ public bool ShouldPutUnsafeModifier()
+ {
+ var token = this.GetFirstTokenInSelection();
+ var ancestors = token.GetAncestors<SyntaxNode>();
+
+ // if enclosing type contains unsafe keyword, we don't need to put it again
+ if (ancestors.Where(a => SyntaxFacts.IsTypeDeclaration(a.Kind()))
+ .Cast<MemberDeclarationSyntax>()
+ .Any(m => m.GetModifiers().Any(SyntaxKind.UnsafeKeyword)))
+ {
+ return false;
+ }
+
+ return token.Parent.IsUnsafeContext();
+ }
+
+ public SyntaxKind UnderCheckedExpressionContext()
+ {
+ return UnderCheckedContext<CheckedExpressionSyntax>();
+ }
+
+ public SyntaxKind UnderCheckedStatementContext()
+ {
+ return UnderCheckedContext<CheckedStatementSyntax>();
+ }
+
+ private SyntaxKind UnderCheckedContext<T>() where T : SyntaxNode
+ {
+ var token = this.GetFirstTokenInSelection();
+ var contextNode = token.Parent.GetAncestor<T>();
+ if (contextNode == null)
+ {
+ return SyntaxKind.None;
+ }
+
+ return contextNode.Kind();
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSelectionValidator.Validator.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSelectionValidator.Validator.cs
new file mode 100644
index 0000000000..578998b402
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSelectionValidator.Validator.cs
@@ -0,0 +1,86 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public partial class CSharpSelectionValidator
+ {
+ public bool Check(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
+ {
+ return node.TypeSwitch(
+ (ExpressionSyntax expression) => CheckExpression(semanticModel, expression, cancellationToken),
+ (BlockSyntax block) => CheckBlock(semanticModel, block, cancellationToken),
+ (StatementSyntax statement) => CheckStatement(semanticModel, statement, cancellationToken),
+ (GlobalStatementSyntax globalStatement) => CheckGlobalStatement(semanticModel, globalStatement, cancellationToken));
+ }
+
+ private bool CheckGlobalStatement(SemanticModel semanticModel, GlobalStatementSyntax globalStatement, CancellationToken cancellationToken)
+ {
+ return true;
+ }
+
+ private bool CheckBlock(SemanticModel semanticModel, BlockSyntax block, CancellationToken cancellationToken)
+ {
+ // TODO(cyrusn): Is it intentional that fixed statement is not in this list?
+ if (block.Parent is BlockSyntax ||
+ block.Parent is DoStatementSyntax ||
+ block.Parent is ElseClauseSyntax ||
+ block.Parent is ForEachStatementSyntax ||
+ block.Parent is ForStatementSyntax ||
+ block.Parent is IfStatementSyntax ||
+ block.Parent is LockStatementSyntax ||
+ block.Parent is UsingStatementSyntax ||
+ block.Parent is WhileStatementSyntax)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ private bool CheckExpression(SemanticModel semanticModel, ExpressionSyntax expression, CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ // TODO(cyrusn): This is probably unnecessary. What we should be doing is binding
+ // the type of the expression and seeing if it contains an anonymous type.
+ if (expression is AnonymousObjectCreationExpressionSyntax)
+ {
+ return false;
+ }
+
+ return expression.CanReplaceWithRValue(semanticModel, cancellationToken);
+ }
+
+ private bool CheckStatement(SemanticModel semanticModel, StatementSyntax statement, CancellationToken cancellationToken)
+ {
+ if (statement is CheckedStatementSyntax ||
+ statement is DoStatementSyntax ||
+ statement is EmptyStatementSyntax ||
+ statement is ExpressionStatementSyntax ||
+ statement is FixedStatementSyntax ||
+ statement is ForEachStatementSyntax ||
+ statement is ForStatementSyntax ||
+ statement is IfStatementSyntax ||
+ statement is LocalDeclarationStatementSyntax ||
+ statement is LockStatementSyntax ||
+ statement is ReturnStatementSyntax ||
+ statement is SwitchStatementSyntax ||
+ statement is ThrowStatementSyntax ||
+ statement is TryStatementSyntax ||
+ statement is UnsafeStatementSyntax ||
+ statement is UsingStatementSyntax ||
+ statement is WhileStatementSyntax)
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSelectionValidator.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSelectionValidator.cs
new file mode 100644
index 0000000000..5d67b0e6eb
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSelectionValidator.cs
@@ -0,0 +1,485 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Symbols;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Options;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Text;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public partial class CSharpSelectionValidator : SelectionValidator
+ {
+ public CSharpSelectionValidator(
+ SemanticDocument document,
+ TextSpan textSpan,
+ OptionSet options) :
+ base(document, textSpan, options)
+ {
+ }
+
+ public override async Task<SelectionResult> GetValidSelectionAsync(CancellationToken cancellationToken)
+ {
+ if (!this.ContainsValidSelection)
+ {
+ return NullSelection;
+ }
+
+ var text = this.SemanticDocument.Text;
+ var root = this.SemanticDocument.Root;
+ var model = this.SemanticDocument.SemanticModel;
+
+ // go through pipe line and calculate information about the user selection
+ var selectionInfo = GetInitialSelectionInfo(root, text, cancellationToken);
+ selectionInfo = AssignInitialFinalTokens(selectionInfo, root, cancellationToken);
+ selectionInfo = AdjustFinalTokensBasedOnContext(selectionInfo, model, cancellationToken);
+ selectionInfo = AssignFinalSpan(selectionInfo, text, cancellationToken);
+ selectionInfo = ApplySpecialCases(selectionInfo, text, cancellationToken);
+ selectionInfo = CheckErrorCasesAndAppendDescriptions(selectionInfo, root, cancellationToken);
+
+ // there was a fatal error that we couldn't even do negative preview, return error result
+ if (selectionInfo.Status.FailedWithNoBestEffortSuggestion())
+ {
+ return new ErrorSelectionResult(selectionInfo.Status);
+ }
+
+ var controlFlowSpan = GetControlFlowSpan(selectionInfo);
+ if (!selectionInfo.SelectionInExpression)
+ {
+ var statementRange = GetStatementRangeContainedInSpan<StatementSyntax>(root, controlFlowSpan, cancellationToken);
+ if (statementRange == null)
+ {
+ selectionInfo = selectionInfo.WithStatus(s => s.With(OperationStatusFlag.None, "CSharpFeaturesResources.CantDetermineValidRangeOfStatements"));
+ return new ErrorSelectionResult(selectionInfo.Status);
+ }
+
+ var isFinalSpanSemanticallyValid = IsFinalSpanSemanticallyValidSpan(model, controlFlowSpan, statementRange, cancellationToken);
+ if (!isFinalSpanSemanticallyValid)
+ {
+ // check control flow only if we are extracting statement level, not expression
+ // level. you can not have goto that moves control out of scope in expression level
+ // (even in lambda)
+ selectionInfo = selectionInfo.WithStatus(s => s.With(OperationStatusFlag.BestEffort, "CSharpFeaturesResources.NotAllCodePathReturns"));
+ }
+ }
+
+ return await CSharpSelectionResult.CreateAsync(
+ selectionInfo.Status,
+ selectionInfo.OriginalSpan,
+ selectionInfo.FinalSpan,
+ this.Options,
+ selectionInfo.SelectionInExpression,
+ this.SemanticDocument,
+ selectionInfo.FirstTokenInFinalSpan,
+ selectionInfo.LastTokenInFinalSpan,
+ cancellationToken).ConfigureAwait(false);
+ }
+
+ private SelectionInfo ApplySpecialCases(SelectionInfo selectionInfo, SourceText text, CancellationToken cancellationToken)
+ {
+ if (selectionInfo.Status.FailedWithNoBestEffortSuggestion() || !selectionInfo.SelectionInExpression)
+ {
+ return selectionInfo;
+ }
+
+ var expressionNode = selectionInfo.FirstTokenInFinalSpan.GetCommonRoot(selectionInfo.LastTokenInFinalSpan);
+ if (!expressionNode.IsAnyAssignExpression())
+ {
+ return selectionInfo;
+ }
+
+ var assign = (AssignmentExpressionSyntax)expressionNode;
+
+ // make sure there is a visible token at right side expression
+ if (assign.Right.GetLastToken().Kind() == SyntaxKind.None)
+ {
+ return selectionInfo;
+ }
+
+ return AssignFinalSpan(selectionInfo.With(s => s.FirstTokenInFinalSpan = assign.Right.GetFirstToken(includeZeroWidth: true))
+ .With(s => s.LastTokenInFinalSpan = assign.Right.GetLastToken(includeZeroWidth: true)),
+ text, cancellationToken);
+ }
+
+ private TextSpan GetControlFlowSpan(SelectionInfo selectionInfo)
+ {
+ return TextSpan.FromBounds(selectionInfo.FirstTokenInFinalSpan.SpanStart, selectionInfo.LastTokenInFinalSpan.Span.End);
+ }
+
+ private SelectionInfo AdjustFinalTokensBasedOnContext(
+ SelectionInfo selectionInfo,
+ SemanticModel semanticModel,
+ CancellationToken cancellationToken)
+ {
+ if (selectionInfo.Status.FailedWithNoBestEffortSuggestion())
+ {
+ return selectionInfo;
+ }
+
+ // don't need to adjust anything if it is multi-statements case
+ if (!selectionInfo.SelectionInExpression && !selectionInfo.SelectionInSingleStatement)
+ {
+ return selectionInfo;
+ }
+
+ // get the node that covers the selection
+ var node = selectionInfo.FirstTokenInFinalSpan.GetCommonRoot(selectionInfo.LastTokenInFinalSpan);
+
+ var validNode = Check(semanticModel, node, cancellationToken);
+ if (validNode)
+ {
+ return selectionInfo;
+ }
+
+ var firstValidNode = node.GetAncestors<SyntaxNode>().FirstOrDefault(n => Check(semanticModel, n, cancellationToken));
+ if (firstValidNode == null)
+ {
+ // couldn't find any valid node
+ return selectionInfo.WithStatus(s => new OperationStatus(OperationStatusFlag.None, "CSharpFeaturesResources.SelectionDoesNotContainAValidNode"))
+ .With(s => s.FirstTokenInFinalSpan = default(SyntaxToken))
+ .With(s => s.LastTokenInFinalSpan = default(SyntaxToken));
+ }
+
+ firstValidNode = (firstValidNode.Parent is ExpressionStatementSyntax) ? firstValidNode.Parent : firstValidNode;
+
+ return selectionInfo.With(s => s.SelectionInExpression = firstValidNode is ExpressionSyntax)
+ .With(s => s.SelectionInSingleStatement = firstValidNode is StatementSyntax)
+ .With(s => s.FirstTokenInFinalSpan = firstValidNode.GetFirstToken(includeZeroWidth: true))
+ .With(s => s.LastTokenInFinalSpan = firstValidNode.GetLastToken(includeZeroWidth: true));
+ }
+
+ private SelectionInfo GetInitialSelectionInfo(SyntaxNode root, SourceText text, CancellationToken cancellationToken)
+ {
+ var adjustedSpan = GetAdjustedSpan(text, this.OriginalSpan);
+
+ var firstTokenInSelection = root.FindTokenOnRightOfPosition(adjustedSpan.Start, includeSkipped: false);
+ var lastTokenInSelection = root.FindTokenOnLeftOfPosition(adjustedSpan.End, includeSkipped: false);
+
+ if (firstTokenInSelection.Kind() == SyntaxKind.None || lastTokenInSelection.Kind() == SyntaxKind.None)
+ {
+ return new SelectionInfo { Status = new OperationStatus(OperationStatusFlag.None, "CSharpFeaturesResources.InvalidSelection"), OriginalSpan = adjustedSpan };
+ }
+
+ if (!adjustedSpan.Contains(firstTokenInSelection.Span) && !adjustedSpan.Contains(lastTokenInSelection.Span))
+ {
+ return new SelectionInfo
+ {
+ Status = new OperationStatus(OperationStatusFlag.None, "CSharpFeaturesResources.SelectionDoesNotContainAValidToken"),
+ OriginalSpan = adjustedSpan,
+ FirstTokenInOriginalSpan = firstTokenInSelection,
+ LastTokenInOriginalSpan = lastTokenInSelection
+ };
+ }
+
+ if (!firstTokenInSelection.UnderValidContext() || !lastTokenInSelection.UnderValidContext())
+ {
+ return new SelectionInfo
+ {
+ OriginalSpan = adjustedSpan,
+ FirstTokenInOriginalSpan = firstTokenInSelection,
+ LastTokenInOriginalSpan = lastTokenInSelection
+ };
+ }
+
+ var commonRoot = firstTokenInSelection.GetCommonRoot(lastTokenInSelection);
+ if (commonRoot == null)
+ {
+ return new SelectionInfo
+ {
+ Status = new OperationStatus(OperationStatusFlag.None, "CSharpFeaturesResources.NoCommonRootNodeForExtraction"),
+ OriginalSpan = adjustedSpan,
+ FirstTokenInOriginalSpan = firstTokenInSelection,
+ LastTokenInOriginalSpan = lastTokenInSelection
+ };
+ }
+
+ var selectionInExpression = commonRoot is ExpressionSyntax;
+ if (!selectionInExpression && !commonRoot.UnderValidContext())
+ {
+ return new SelectionInfo
+ {
+ Status = new OperationStatus(OperationStatusFlag.None, "CSharpFeaturesResources.NoValidSelectionToPerformExtraction"),
+ OriginalSpan = adjustedSpan,
+ FirstTokenInOriginalSpan = firstTokenInSelection,
+ LastTokenInOriginalSpan = lastTokenInSelection
+ };
+ }
+
+ return new SelectionInfo
+ {
+ Status = OperationStatus.Succeeded,
+ OriginalSpan = adjustedSpan,
+ CommonRootFromOriginalSpan = commonRoot,
+ SelectionInExpression = selectionInExpression,
+ FirstTokenInOriginalSpan = firstTokenInSelection,
+ LastTokenInOriginalSpan = lastTokenInSelection
+ };
+ }
+
+ private SelectionInfo CheckErrorCasesAndAppendDescriptions(SelectionInfo selectionInfo, SyntaxNode root, CancellationToken cancellationToken)
+ {
+ if (selectionInfo.Status.FailedWithNoBestEffortSuggestion())
+ {
+ return selectionInfo;
+ }
+
+ if (selectionInfo.FirstTokenInFinalSpan.IsMissing || selectionInfo.LastTokenInFinalSpan.IsMissing)
+ {
+ selectionInfo = selectionInfo.WithStatus(s => s.With(OperationStatusFlag.None, "CSharpFeaturesResources.ContainsInvalidSelection"));
+ }
+
+ // get the node that covers the selection
+ var commonNode = selectionInfo.FirstTokenInFinalSpan.GetCommonRoot(selectionInfo.LastTokenInFinalSpan);
+
+ if ((selectionInfo.SelectionInExpression || selectionInfo.SelectionInSingleStatement) && commonNode.HasDiagnostics())
+ {
+ selectionInfo = selectionInfo.WithStatus(s => s.With(OperationStatusFlag.None, "CSharpFeaturesResources.TheSelectionContainsSyntacticErrors"));
+ }
+
+ var tokens = root.DescendantTokens(selectionInfo.FinalSpan);
+ if (tokens.ContainPreprocessorCrossOver(selectionInfo.FinalSpan))
+ {
+ selectionInfo = selectionInfo.WithStatus(s => s.With(OperationStatusFlag.BestEffort, "CSharpFeaturesResources.SelectionCanNotCrossOverPreprocessorDirectives"));
+ }
+
+ // TODO : check whether this can be handled by control flow analysis engine
+ if (tokens.Any(t => t.Kind() == SyntaxKind.YieldKeyword))
+ {
+ selectionInfo = selectionInfo.WithStatus(s => s.With(OperationStatusFlag.BestEffort, "CSharpFeaturesResources.SelectionCanNotContainAYieldStatement"));
+ }
+
+ // TODO : check behavior of control flow analysis engine around exception and exception handling.
+ if (tokens.ContainArgumentlessThrowWithoutEnclosingCatch(selectionInfo.FinalSpan))
+ {
+ selectionInfo = selectionInfo.WithStatus(s => s.With(OperationStatusFlag.BestEffort, "CSharpFeaturesResources.SelectionCanNotContainThrowStatement"));
+ }
+
+ if (selectionInfo.SelectionInExpression && commonNode.PartOfConstantInitializerExpression())
+ {
+ selectionInfo = selectionInfo.WithStatus(s => s.With(OperationStatusFlag.None, "CSharpFeaturesResources.SelectionCanNotBePartOfConstInitializerExpr"));
+ }
+
+ if (commonNode.IsUnsafeContext())
+ {
+ selectionInfo = selectionInfo.WithStatus(s => s.With(s.Flag, "CSharpFeaturesResources.TheSelectedCodeIsInsideAnUnsafeContext"));
+ }
+
+ var selectionChanged = selectionInfo.FirstTokenInOriginalSpan != selectionInfo.FirstTokenInFinalSpan || selectionInfo.LastTokenInOriginalSpan != selectionInfo.LastTokenInFinalSpan;
+ if (selectionChanged)
+ {
+ selectionInfo = selectionInfo.WithStatus(s => s.MarkSuggestion());
+ }
+
+ return selectionInfo;
+ }
+
+ private SelectionInfo AssignInitialFinalTokens(SelectionInfo selectionInfo, SyntaxNode root, CancellationToken cancellationToken)
+ {
+ if (selectionInfo.Status.FailedWithNoBestEffortSuggestion())
+ {
+ return selectionInfo;
+ }
+
+ if (selectionInfo.SelectionInExpression)
+ {
+ // simple expression case
+ return selectionInfo.With(s => s.FirstTokenInFinalSpan = s.CommonRootFromOriginalSpan.GetFirstToken(includeZeroWidth: true))
+ .With(s => s.LastTokenInFinalSpan = s.CommonRootFromOriginalSpan.GetLastToken(includeZeroWidth: true));
+ }
+
+ var range = GetStatementRangeContainingSpan<StatementSyntax>(
+ root, TextSpan.FromBounds(selectionInfo.FirstTokenInOriginalSpan.SpanStart, selectionInfo.LastTokenInOriginalSpan.Span.End),
+ cancellationToken);
+
+ if (range == null)
+ {
+ return selectionInfo.WithStatus(s => s.With(OperationStatusFlag.None, "CSharpFeaturesResources.NoValidStatementRangeToExtractOut"));
+ }
+
+ var statement1 = (StatementSyntax)range.Item1;
+ var statement2 = (StatementSyntax)range.Item2;
+
+ if (statement1 == statement2)
+ {
+ // check one more time to see whether it is an expression case
+ var expression = selectionInfo.CommonRootFromOriginalSpan.GetAncestor<ExpressionSyntax>();
+ if (expression != null && statement1.Span.Contains(expression.Span))
+ {
+ return selectionInfo.With(s => s.SelectionInExpression = true)
+ .With(s => s.FirstTokenInFinalSpan = expression.GetFirstToken(includeZeroWidth: true))
+ .With(s => s.LastTokenInFinalSpan = expression.GetLastToken(includeZeroWidth: true));
+ }
+
+ // single statement case
+ return selectionInfo.With(s => s.SelectionInSingleStatement = true)
+ .With(s => s.FirstTokenInFinalSpan = statement1.GetFirstToken(includeZeroWidth: true))
+ .With(s => s.LastTokenInFinalSpan = statement1.GetLastToken(includeZeroWidth: true));
+ }
+
+ // move only statements inside of the block
+ return selectionInfo.With(s => s.FirstTokenInFinalSpan = statement1.GetFirstToken(includeZeroWidth: true))
+ .With(s => s.LastTokenInFinalSpan = statement2.GetLastToken(includeZeroWidth: true));
+ }
+
+ private SelectionInfo AssignFinalSpan(SelectionInfo selectionInfo, SourceText text, CancellationToken cancellationToken)
+ {
+ if (selectionInfo.Status.FailedWithNoBestEffortSuggestion())
+ {
+ return selectionInfo;
+ }
+
+ // set final span
+ var start = (selectionInfo.FirstTokenInOriginalSpan == selectionInfo.FirstTokenInFinalSpan) ?
+ Math.Min(selectionInfo.FirstTokenInOriginalSpan.SpanStart, selectionInfo.OriginalSpan.Start) :
+ selectionInfo.FirstTokenInFinalSpan.FullSpan.Start;
+
+ var end = (selectionInfo.LastTokenInOriginalSpan == selectionInfo.LastTokenInFinalSpan) ?
+ Math.Max(selectionInfo.LastTokenInOriginalSpan.Span.End, selectionInfo.OriginalSpan.End) :
+ selectionInfo.LastTokenInFinalSpan.FullSpan.End;
+
+ return selectionInfo.With(s => s.FinalSpan = GetAdjustedSpan(text, TextSpan.FromBounds(start, end)));
+ }
+
+ public override bool ContainsNonReturnExitPointsStatements(IEnumerable<SyntaxNode> jumpsOutOfRegion)
+ {
+ return jumpsOutOfRegion.Where(n => !(n is ReturnStatementSyntax)).Any();
+ }
+
+ public override IEnumerable<SyntaxNode> GetOuterReturnStatements(SyntaxNode commonRoot, IEnumerable<SyntaxNode> jumpsOutOfRegion)
+ {
+ var returnStatements = jumpsOutOfRegion.Where(s => s is ReturnStatementSyntax);
+
+ var container = commonRoot.GetAncestorsOrThis<SyntaxNode>().Where(a => a.IsReturnableConstruct()).FirstOrDefault();
+ if (container == null)
+ {
+ return SpecializedCollections.EmptyEnumerable<SyntaxNode>();
+ }
+
+ var returnableConstructPairs = returnStatements.Select(r => Tuple.Create(r, r.GetAncestors<SyntaxNode>().Where(a => a.IsReturnableConstruct()).FirstOrDefault()))
+ .Where(p => p.Item2 != null);
+
+ // now filter return statements to only include the one under outmost container
+ return returnableConstructPairs.Where(p => p.Item2 == container).Select(p => p.Item1);
+ }
+
+ public override bool IsFinalSpanSemanticallyValidSpan(
+ SyntaxNode root, TextSpan textSpan,
+ IEnumerable<SyntaxNode> returnStatements, CancellationToken cancellationToken)
+ {
+ // return statement shouldn't contain any return value
+ if (returnStatements.Cast<ReturnStatementSyntax>().Any(r => r.Expression != null))
+ {
+ return false;
+ }
+
+ var lastToken = (SyntaxToken)root.FindToken(textSpan.End);
+ if (lastToken.Kind() == SyntaxKind.None)
+ {
+ return false;
+ }
+
+ var container = lastToken.GetAncestors<SyntaxNode>().FirstOrDefault(n => n.IsReturnableConstruct());
+ if (container == null)
+ {
+ return false;
+ }
+
+ var body = container.GetBlockBody();
+ if (body == null)
+ {
+ return false;
+ }
+
+ // make sure that next token of the last token in the selection is the close braces of containing block
+ if (body.CloseBraceToken != lastToken.GetNextToken(includeZeroWidth: true))
+ {
+ return false;
+ }
+
+ // alright, for these construcuts, it must be okay to be extracted
+ switch (container.Kind())
+ {
+ case SyntaxKind.AnonymousMethodExpression:
+ case SyntaxKind.SimpleLambdaExpression:
+ case SyntaxKind.ParenthesizedLambdaExpression:
+ return true;
+ }
+
+ // now, only method is okay to be extracted out
+ var method = body.Parent as MethodDeclarationSyntax;
+ if (method == null)
+ {
+ return false;
+ }
+
+ // make sure this method doesn't have return type.
+ return method.ReturnType.TypeSwitch((PredefinedTypeSyntax p) => p.Keyword.Kind() == SyntaxKind.VoidKeyword);
+ }
+
+ private static TextSpan GetAdjustedSpan(SourceText text, TextSpan textSpan)
+ {
+ // beginning of a file
+ if (textSpan.IsEmpty || textSpan.End == 0)
+ {
+ return textSpan;
+ }
+
+ // if it is a start of new line, make it belong to previous line
+ var line = text.Lines.GetLineFromPosition(textSpan.End);
+ if (line.Start != textSpan.End)
+ {
+ return textSpan;
+ }
+
+ // get previous line
+ //Contract.ThrowIfFalse(line.LineNumber > 0);
+ var previousLine = text.Lines[line.LineNumber - 1];
+ return TextSpan.FromBounds(textSpan.Start, previousLine.End);
+ }
+
+ private class SelectionInfo
+ {
+ public OperationStatus Status { get; set; }
+
+ public TextSpan OriginalSpan { get; set; }
+ public TextSpan FinalSpan { get; set; }
+
+ public SyntaxNode CommonRootFromOriginalSpan { get; set; }
+
+ public SyntaxToken FirstTokenInOriginalSpan { get; set; }
+ public SyntaxToken LastTokenInOriginalSpan { get; set; }
+
+ public SyntaxToken FirstTokenInFinalSpan { get; set; }
+ public SyntaxToken LastTokenInFinalSpan { get; set; }
+
+ public bool SelectionInExpression { get; set; }
+ public bool SelectionInSingleStatement { get; set; }
+
+ public SelectionInfo WithStatus(Func<OperationStatus, OperationStatus> statusGetter)
+ {
+ return With(s => s.Status = statusGetter(s.Status));
+ }
+
+ public SelectionInfo With(Action<SelectionInfo> valueSetter)
+ {
+ var newInfo = this.Clone();
+ valueSetter(newInfo);
+ return newInfo;
+ }
+
+ public SelectionInfo Clone()
+ {
+ return (SelectionInfo)this.MemberwiseClone();
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSyntaxTriviaService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSyntaxTriviaService.cs
new file mode 100644
index 0000000000..1cabb19475
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSyntaxTriviaService.cs
@@ -0,0 +1,16 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.CodeAnalysis.Host;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public class CSharpSyntaxTriviaService : AbstractSyntaxTriviaService
+ {
+ public CSharpSyntaxTriviaService()
+ : base((int)SyntaxKind.EndOfLineTrivia)
+ {
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSyntaxTriviaServiceFactory.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSyntaxTriviaServiceFactory.cs
new file mode 100644
index 0000000000..d640d6066c
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/CSharpSyntaxTriviaServiceFactory.cs
@@ -0,0 +1,20 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Composition;
+using Microsoft.CodeAnalysis.Host;
+using Microsoft.CodeAnalysis.Host.Mef;
+
+using Microsoft.CodeAnalysis;
+
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+// [ExportLanguageServiceFactory(typeof(ISyntaxTriviaService), LanguageNames.CSharp), Shared]
+// public class CSharpSyntaxTriviaServiceFactory : ILanguageServiceFactory
+// {
+// public ILanguageService CreateLanguageService(HostLanguageServices provider)
+// {
+// return new CSharpSyntaxTriviaService(provider);
+// }
+// }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/Extensions.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/Extensions.cs
new file mode 100644
index 0000000000..61825c5c53
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/CSharp/Extensions.cs
@@ -0,0 +1,285 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Symbols;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Text;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ static partial class Extensions
+ {
+ public static ExpressionSyntax GetUnparenthesizedExpression(this SyntaxNode node)
+ {
+ var parenthesizedExpression = node as ParenthesizedExpressionSyntax;
+ if (parenthesizedExpression == null)
+ {
+ return node as ExpressionSyntax;
+ }
+
+ return GetUnparenthesizedExpression(parenthesizedExpression.Expression);
+ }
+
+ public static StatementSyntax GetStatementUnderContainer(this SyntaxNode node)
+ {
+ //Contract.ThrowIfNull(node);
+
+ while (node != null)
+ {
+ if (node.Parent != null &&
+ node.Parent.IsStatementContainerNode())
+ {
+ return node as StatementSyntax;
+ }
+
+ node = node.Parent;
+ }
+
+ return null;
+ }
+
+ public static StatementSyntax GetParentLabeledStatementIfPossible(this SyntaxNode node)
+ {
+ return (StatementSyntax)((node.Parent is LabeledStatementSyntax) ? node.Parent : node);
+ }
+
+ public static bool IsStatementContainerNode(this SyntaxNode node)
+ {
+ return node is BlockSyntax || node is SwitchSectionSyntax;
+ }
+
+ public static BlockSyntax GetBlockBody(this SyntaxNode node)
+ {
+ return node.TypeSwitch(
+ (BaseMethodDeclarationSyntax m) => m.Body,
+ (AccessorDeclarationSyntax a) => a.Body,
+ (SimpleLambdaExpressionSyntax s) => s.Body as BlockSyntax,
+ (ParenthesizedLambdaExpressionSyntax p) => p.Body as BlockSyntax,
+ (AnonymousMethodExpressionSyntax a) => a.Block);
+ }
+
+ public static bool UnderValidContext(this SyntaxNode node)
+ {
+ //Contract.ThrowIfNull(node);
+
+ Func<SyntaxNode, bool> predicate = n =>
+ {
+ if (n is BaseMethodDeclarationSyntax ||
+ n is AccessorDeclarationSyntax ||
+ n is BlockSyntax ||
+ n is GlobalStatementSyntax)
+ {
+ return true;
+ }
+
+ var constructorInitializer = n as ConstructorInitializerSyntax;
+ if (constructorInitializer != null)
+ {
+ return constructorInitializer.ContainsInArgument(node.Span);
+ }
+
+ return false;
+ };
+
+ if (!node.GetAncestorsOrThis<SyntaxNode>().Any(predicate))
+ {
+ return false;
+ }
+
+ if (node.FromScript() || node.GetAncestor<TypeDeclarationSyntax>() != null)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ public static bool UnderValidContext(this SyntaxToken token)
+ {
+ return token.GetAncestors<SyntaxNode>().Any(n => n.CheckTopLevel(token.Span));
+ }
+
+ public static bool PartOfConstantInitializerExpression(this SyntaxNode node)
+ {
+ return node.PartOfConstantInitializerExpression<FieldDeclarationSyntax>(n => n.Modifiers) ||
+ node.PartOfConstantInitializerExpression<LocalDeclarationStatementSyntax>(n => n.Modifiers);
+ }
+
+ private static bool PartOfConstantInitializerExpression<T>(this SyntaxNode node, Func<T, SyntaxTokenList> modifiersGetter) where T : SyntaxNode
+ {
+ var decl = node.GetAncestor<T>();
+ if (decl == null)
+ {
+ return false;
+ }
+
+ if (!modifiersGetter(decl).Any(t => t.Kind() == SyntaxKind.ConstKeyword))
+ {
+ return false;
+ }
+
+ // we are under decl with const modifier, check we are part of initializer expression
+ var equal = node.GetAncestor<EqualsValueClauseSyntax>();
+ if (equal == null)
+ {
+ return false;
+ }
+
+ return equal.Value != null && equal.Value.Span.Contains(node.Span);
+ }
+
+ public static bool ContainArgumentlessThrowWithoutEnclosingCatch(this IEnumerable<SyntaxToken> tokens, TextSpan textSpan)
+ {
+ foreach (var token in tokens)
+ {
+ if (token.Kind() != SyntaxKind.ThrowKeyword)
+ {
+ continue;
+ }
+
+ var throwStatement = token.Parent as ThrowStatementSyntax;
+ if (throwStatement == null || throwStatement.Expression != null)
+ {
+ continue;
+ }
+
+ var catchClause = token.GetAncestor<CatchClauseSyntax>();
+ if (catchClause == null || !textSpan.Contains(catchClause.Span))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static bool ContainPreprocessorCrossOver(this IEnumerable<SyntaxToken> tokens, TextSpan textSpan)
+ {
+ int activeRegions = 0;
+ int activeIfs = 0;
+
+ foreach (var trivia in tokens.GetAllTrivia())
+ {
+ if (!textSpan.Contains(trivia.Span))
+ {
+ continue;
+ }
+
+ switch (trivia.Kind())
+ {
+ case SyntaxKind.RegionDirectiveTrivia:
+ activeRegions++;
+ break;
+ case SyntaxKind.EndRegionDirectiveTrivia:
+ if (activeRegions <= 0)
+ {
+ return true;
+ }
+
+ activeRegions--;
+ break;
+ case SyntaxKind.IfDirectiveTrivia:
+ activeIfs++;
+ break;
+ case SyntaxKind.EndIfDirectiveTrivia:
+ if (activeIfs <= 0)
+ {
+ return true;
+ }
+
+ activeIfs--;
+ break;
+ case SyntaxKind.ElseDirectiveTrivia:
+ case SyntaxKind.ElifDirectiveTrivia:
+ if (activeIfs <= 0)
+ {
+ return true;
+ }
+
+ break;
+ }
+ }
+
+ return activeIfs != 0 || activeRegions != 0;
+ }
+
+ public static IEnumerable<SyntaxTrivia> GetAllTrivia(this IEnumerable<SyntaxToken> tokens)
+ {
+ foreach (var token in tokens)
+ {
+ foreach (var trivia in token.LeadingTrivia)
+ {
+ yield return trivia;
+ }
+
+ foreach (var trivia in token.TrailingTrivia)
+ {
+ yield return trivia;
+ }
+ }
+ }
+
+ public static bool HasSyntaxAnnotation(this HashSet<SyntaxAnnotation> set, SyntaxNode node)
+ {
+ return set.Any(a => node.GetAnnotatedNodesAndTokens(a).Any());
+ }
+
+ public static bool HasHybridTriviaBetween(this SyntaxToken token1, SyntaxToken token2)
+ {
+ if (token1.TrailingTrivia.Any(t => !t.IsElastic()))
+ {
+ return true;
+ }
+
+ if (token2.LeadingTrivia.Any(t => !t.IsElastic()))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ public static bool IsArrayInitializer(this SyntaxNode node)
+ {
+ return node is InitializerExpressionSyntax && node.Parent is EqualsValueClauseSyntax;
+ }
+
+ public static bool IsExpressionInCast(this SyntaxNode node)
+ {
+ return node is ExpressionSyntax && node.Parent is CastExpressionSyntax;
+ }
+
+ public static bool IsExpression(this SyntaxNode node)
+ {
+ return node is ExpressionSyntax;
+ }
+
+ public static bool IsErrorType(this ITypeSymbol type)
+ {
+ return type == null || type.Kind == SymbolKind.ErrorType;
+ }
+
+ public static bool IsObjectType(this ITypeSymbol type)
+ {
+ return type == null || type.SpecialType == SpecialType.System_Object;
+ }
+
+ public static bool BetweenFieldAndNonFieldMember(this SyntaxToken token1, SyntaxToken token2)
+ {
+ if (token1.RawKind != (int)SyntaxKind.SemicolonToken || !(token1.Parent is FieldDeclarationSyntax))
+ {
+ return false;
+ }
+
+ var field = token2.GetAncestor<FieldDeclarationSyntax>();
+ return field == null;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/Enums.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/Enums.cs
new file mode 100644
index 0000000000..36b85e5578
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/Enums.cs
@@ -0,0 +1,55 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public enum DeclarationBehavior
+ {
+ None,
+ Delete,
+ MoveIn,
+ MoveOut,
+ SplitIn,
+ SplitOut
+ }
+
+ public enum ReturnBehavior
+ {
+ None,
+ Initialization,
+ Assignment
+ }
+
+ public enum ParameterBehavior
+ {
+ None,
+ Input,
+ Out,
+ Ref
+ }
+
+ /// <summary>
+ /// status code for extract method operations
+ /// </summary>
+ [Flags]
+ public enum OperationStatusFlag
+ {
+ None = 0x0,
+
+ /// <summary>
+ /// operation has succeeded
+ /// </summary>
+ Succeeded = 0x1,
+
+ /// <summary>
+ /// operation has succeeded with a span that is different than original span
+ /// </summary>
+ Suggestion = 0x2,
+
+ /// <summary>
+ /// operation has failed but can provide some best effort result
+ /// </summary>
+ BestEffort = 0x4,
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/Extensions.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/Extensions.cs
new file mode 100644
index 0000000000..5a4f85d696
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/Extensions.cs
@@ -0,0 +1,121 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ static partial class Extensions
+ {
+ public static bool Succeeded(this OperationStatus status)
+ {
+ return status.Flag.Succeeded();
+ }
+
+ public static bool FailedWithNoBestEffortSuggestion(this OperationStatus status)
+ {
+ return status.Flag.Failed() && !status.Flag.HasBestEffort();
+ }
+
+ public static bool Failed(this OperationStatus status)
+ {
+ return status.Flag.Failed();
+ }
+
+ public static bool Succeeded(this OperationStatusFlag flag)
+ {
+ return (flag & OperationStatusFlag.Succeeded) != 0;
+ }
+
+ public static bool Failed(this OperationStatusFlag flag)
+ {
+ return !flag.Succeeded();
+ }
+
+ public static bool HasBestEffort(this OperationStatusFlag flag)
+ {
+ return (flag & OperationStatusFlag.BestEffort) != 0;
+ }
+
+ public static bool HasSuggestion(this OperationStatusFlag flag)
+ {
+ return (flag & OperationStatusFlag.Suggestion) != 0;
+ }
+
+ public static bool HasMask(this OperationStatusFlag flag, OperationStatusFlag mask)
+ {
+ return (flag & mask) != 0x0;
+ }
+
+ public static OperationStatusFlag RemoveFlag(this OperationStatusFlag baseFlag, OperationStatusFlag flagToRemove)
+ {
+ return baseFlag & ~flagToRemove;
+ }
+
+ public static ITypeSymbol GetLambdaOrAnonymousMethodReturnType(this SemanticModel binding, SyntaxNode node)
+ {
+ var info = binding.GetSymbolInfo(node);
+ if (info.Symbol == null)
+ {
+ return null;
+ }
+
+ var methodSymbol = info.Symbol as IMethodSymbol;
+ if (methodSymbol.MethodKind != MethodKind.AnonymousFunction)
+ {
+ return null;
+ }
+
+ return methodSymbol.ReturnType;
+ }
+
+ public static Task<SemanticDocument> WithSyntaxRootAsync(this SemanticDocument semanticDocument, SyntaxNode root, CancellationToken cancellationToken)
+ {
+ return SemanticDocument.CreateAsync(semanticDocument.Document.WithSyntaxRoot(root), cancellationToken);
+ }
+
+ /// <summary>
+ /// get tokens with given annotation in current document
+ /// </summary>
+ public static SyntaxToken GetTokenWithAnnotaton(this SemanticDocument document, SyntaxAnnotation annotation)
+ {
+ return document.Root.GetAnnotatedNodesAndTokens(annotation).Single().AsToken();
+ }
+
+ /// <summary>
+ /// resolve the given symbol against compilation this snapshot has
+ /// </summary>
+ public static T ResolveType<T>(this SemanticModel semanticModel, T symbol) where T : class, ITypeSymbol
+ {
+ return (T)symbol.GetSymbolKey().Resolve(semanticModel.Compilation).GetAnySymbol();
+ }
+
+ /// <summary>
+ /// check whether node contains error for itself but not from its child node
+ /// </summary>
+ public static bool HasDiagnostics(this SyntaxNode node)
+ {
+ var set = new HashSet<Diagnostic>(node.GetDiagnostics());
+
+ foreach (var child in node.ChildNodes())
+ {
+ set.ExceptWith(child.GetDiagnostics());
+ }
+
+ return set.Count > 0;
+ }
+
+ public static bool FromScript(this SyntaxNode node)
+ {
+ if (node.SyntaxTree == null)
+ {
+ return false;
+ }
+
+ return node.SyntaxTree.Options.Kind != SourceCodeKind.Regular;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ExtractMethodMatrix.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ExtractMethodMatrix.cs
new file mode 100644
index 0000000000..9762ca2cc2
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ExtractMethodMatrix.cs
@@ -0,0 +1,229 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using Roslyn.Utilities;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public class ExtractMethodMatrix
+ {
+ private static readonly Dictionary<Key, VariableStyle> s_matrix;
+
+ static ExtractMethodMatrix()
+ {
+ s_matrix = new Dictionary<Key, VariableStyle>();
+ BuildMatrix();
+ }
+
+ public static VariableStyle GetVariableStyle(
+ bool captured,
+ bool dataFlowIn,
+ bool dataFlowOut,
+ bool alwaysAssigned,
+ bool variableDeclared,
+ bool readInside,
+ bool writtenInside,
+ bool readOutside,
+ bool writtenOutside,
+ bool unsafeAddressTaken)
+ {
+#if false
+ // decide not to treat capture variable special
+ if (captured)
+ {
+ // if a variable is captured, it can only be passed as ref parameter.
+ return VariableStyle.OnlyAsRefParam;
+ }
+#endif
+ // bug # 12258, 12114
+ // use "out" if "&" is taken for the variable
+ if (unsafeAddressTaken)
+ {
+ return VariableStyle.Out;
+ }
+
+ var key = new Key(
+ dataFlowIn,
+ dataFlowOut,
+ alwaysAssigned,
+ variableDeclared,
+ readInside,
+ writtenInside,
+ readOutside,
+ writtenOutside);
+
+ // special cases
+ if (!s_matrix.ContainsKey(key))
+ {
+ // Interesting case. Due to things like constant analysis there can be regions that
+ // the compiler considers data not to flow in (because analysis proves that that
+ // path will never be taken). However, the variable can still be read/written inside
+ // the region. For purposes of extract method, we check for this case, and we
+ // pretend it's as if data flowed into the region.
+ if (!dataFlowIn && (readInside || writtenInside))
+ {
+ key = new Key(true, dataFlowOut, alwaysAssigned, variableDeclared, readInside, writtenInside, readOutside, writtenOutside);
+ }
+
+ // another interesting case (bug # 10875)
+ // basically, it can happen in malformed code where a variable is not properly assigned but used outside of the selection + unreachable code region
+ // for such cases, treat it like "MoveOut"
+ if (!dataFlowOut && !alwaysAssigned && variableDeclared && !writtenInside && readOutside)
+ {
+ key = new Key(dataFlowIn, /*dataFlowOut*/ true, alwaysAssigned, variableDeclared, readInside, writtenInside, readOutside, writtenOutside);
+ }
+ }
+
+ // Contract.ThrowIfFalse(s_matrix.ContainsKey(key));
+
+ return s_matrix[key];
+ }
+
+ private static void BuildMatrix()
+ {
+ // meaning of each boolean values (total of 69 different cases)
+ // data flowin/data flow out/always assigned/variable declared/ read inside/written inside/read outside/written outside
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: false, variableDeclared: false, readInside: false, writtenInside: true, readOutside: false, writtenOutside: false), VariableStyle.InputOnly);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: false, variableDeclared: false, readInside: false, writtenInside: true, readOutside: false, writtenOutside: true), VariableStyle.InputOnly);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: false, variableDeclared: false, readInside: false, writtenInside: true, readOutside: true, writtenOutside: false), VariableStyle.InputOnly);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: false, variableDeclared: false, readInside: false, writtenInside: true, readOutside: true, writtenOutside: true), VariableStyle.InputOnly);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: false, variableDeclared: false, readInside: true, writtenInside: true, readOutside: false, writtenOutside: false), VariableStyle.MoveIn);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: false, variableDeclared: false, readInside: true, writtenInside: true, readOutside: false, writtenOutside: true), VariableStyle.InputOnly);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: false, variableDeclared: false, readInside: true, writtenInside: true, readOutside: true, writtenOutside: false), VariableStyle.SplitIn);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: false, variableDeclared: false, readInside: true, writtenInside: true, readOutside: true, writtenOutside: true), VariableStyle.InputOnly);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: false, variableDeclared: true, readInside: false, writtenInside: false, readOutside: false, writtenOutside: true), VariableStyle.MoveOut);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: false, variableDeclared: true, readInside: false, writtenInside: false, readOutside: true, writtenOutside: true), VariableStyle.MoveOut);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: false, variableDeclared: true, readInside: false, writtenInside: true, readOutside: false, writtenOutside: false), VariableStyle.None);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: false, variableDeclared: true, readInside: false, writtenInside: true, readOutside: false, writtenOutside: true), VariableStyle.SplitOut);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: false, variableDeclared: true, readInside: false, writtenInside: true, readOutside: true, writtenOutside: true), VariableStyle.SplitOut);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: false, variableDeclared: true, readInside: true, writtenInside: false, readOutside: false, writtenOutside: false), VariableStyle.None);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: false, variableDeclared: true, readInside: true, writtenInside: false, readOutside: false, writtenOutside: true), VariableStyle.SplitOut);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: false, variableDeclared: true, readInside: true, writtenInside: false, readOutside: true, writtenOutside: true), VariableStyle.SplitOut);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: false, variableDeclared: true, readInside: true, writtenInside: true, readOutside: false, writtenOutside: false), VariableStyle.None);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: false, variableDeclared: true, readInside: true, writtenInside: true, readOutside: false, writtenOutside: true), VariableStyle.SplitOut);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: false, variableDeclared: true, readInside: true, writtenInside: true, readOutside: true, writtenOutside: true), VariableStyle.SplitOut);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: true, variableDeclared: false, readInside: false, writtenInside: true, readOutside: false, writtenOutside: false), VariableStyle.MoveIn);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: true, variableDeclared: false, readInside: false, writtenInside: true, readOutside: false, writtenOutside: true), VariableStyle.SplitIn);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: true, variableDeclared: false, readInside: false, writtenInside: true, readOutside: true, writtenOutside: false), VariableStyle.SplitIn);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: true, variableDeclared: false, readInside: false, writtenInside: true, readOutside: true, writtenOutside: true), VariableStyle.SplitIn);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: true, variableDeclared: false, readInside: true, writtenInside: true, readOutside: false, writtenOutside: false), VariableStyle.MoveIn);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: true, variableDeclared: false, readInside: true, writtenInside: true, readOutside: false, writtenOutside: true), VariableStyle.SplitIn);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: true, variableDeclared: false, readInside: true, writtenInside: true, readOutside: true, writtenOutside: false), VariableStyle.SplitIn);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: true, variableDeclared: false, readInside: true, writtenInside: true, readOutside: true, writtenOutside: true), VariableStyle.SplitIn);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: true, variableDeclared: true, readInside: false, writtenInside: true, readOutside: false, writtenOutside: false), VariableStyle.None);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: true, variableDeclared: true, readInside: false, writtenInside: true, readOutside: false, writtenOutside: true), VariableStyle.SplitOut);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: true, variableDeclared: true, readInside: false, writtenInside: true, readOutside: true, writtenOutside: true), VariableStyle.SplitOut);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: true, variableDeclared: true, readInside: true, writtenInside: true, readOutside: false, writtenOutside: false), VariableStyle.None);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: true, variableDeclared: true, readInside: true, writtenInside: true, readOutside: false, writtenOutside: true), VariableStyle.SplitOut);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: false, alwaysAssigned: true, variableDeclared: true, readInside: true, writtenInside: true, readOutside: true, writtenOutside: true), VariableStyle.SplitOut);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: true, alwaysAssigned: false, variableDeclared: false, readInside: false, writtenInside: true, readOutside: true, writtenOutside: false), VariableStyle.Ref);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: true, alwaysAssigned: false, variableDeclared: false, readInside: false, writtenInside: true, readOutside: true, writtenOutside: true), VariableStyle.Ref);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: true, alwaysAssigned: false, variableDeclared: false, readInside: true, writtenInside: true, readOutside: true, writtenOutside: false), VariableStyle.Ref);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: true, alwaysAssigned: false, variableDeclared: false, readInside: true, writtenInside: true, readOutside: true, writtenOutside: true), VariableStyle.Ref);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: true, alwaysAssigned: false, variableDeclared: true, readInside: false, writtenInside: false, readOutside: true, writtenOutside: false), VariableStyle.NotUsed);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: true, alwaysAssigned: false, variableDeclared: true, readInside: false, writtenInside: false, readOutside: true, writtenOutside: true), VariableStyle.NotUsed);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: true, alwaysAssigned: false, variableDeclared: true, readInside: false, writtenInside: true, readOutside: true, writtenOutside: false), VariableStyle.OutWithMoveOut);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: true, alwaysAssigned: false, variableDeclared: true, readInside: false, writtenInside: true, readOutside: true, writtenOutside: true), VariableStyle.OutWithMoveOut);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: true, alwaysAssigned: false, variableDeclared: true, readInside: true, writtenInside: false, readOutside: true, writtenOutside: false), VariableStyle.OutWithMoveOut);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: true, alwaysAssigned: false, variableDeclared: true, readInside: true, writtenInside: false, readOutside: true, writtenOutside: true), VariableStyle.OutWithMoveOut);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: true, alwaysAssigned: false, variableDeclared: true, readInside: true, writtenInside: true, readOutside: true, writtenOutside: false), VariableStyle.OutWithMoveOut);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: true, alwaysAssigned: false, variableDeclared: true, readInside: true, writtenInside: true, readOutside: true, writtenOutside: true), VariableStyle.OutWithMoveOut);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: true, alwaysAssigned: true, variableDeclared: false, readInside: false, writtenInside: true, readOutside: true, writtenOutside: false), VariableStyle.Out);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: true, alwaysAssigned: true, variableDeclared: false, readInside: false, writtenInside: true, readOutside: true, writtenOutside: true), VariableStyle.Out);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: true, alwaysAssigned: true, variableDeclared: false, readInside: true, writtenInside: true, readOutside: true, writtenOutside: false), VariableStyle.Out);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: true, alwaysAssigned: true, variableDeclared: false, readInside: true, writtenInside: true, readOutside: true, writtenOutside: true), VariableStyle.Out);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: true, alwaysAssigned: true, variableDeclared: true, readInside: false, writtenInside: true, readOutside: true, writtenOutside: false), VariableStyle.OutWithMoveOut);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: true, alwaysAssigned: true, variableDeclared: true, readInside: false, writtenInside: true, readOutside: true, writtenOutside: true), VariableStyle.OutWithMoveOut);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: true, alwaysAssigned: true, variableDeclared: true, readInside: true, writtenInside: true, readOutside: true, writtenOutside: false), VariableStyle.OutWithMoveOut);
+ s_matrix.Add(new Key(dataFlowIn: false, dataFlowOut: true, alwaysAssigned: true, variableDeclared: true, readInside: true, writtenInside: true, readOutside: true, writtenOutside: true), VariableStyle.OutWithMoveOut);
+ s_matrix.Add(new Key(dataFlowIn: true, dataFlowOut: false, alwaysAssigned: false, variableDeclared: false, readInside: true, writtenInside: false, readOutside: false, writtenOutside: false), VariableStyle.InputOnly);
+ s_matrix.Add(new Key(dataFlowIn: true, dataFlowOut: false, alwaysAssigned: false, variableDeclared: false, readInside: true, writtenInside: false, readOutside: false, writtenOutside: true), VariableStyle.InputOnly);
+ s_matrix.Add(new Key(dataFlowIn: true, dataFlowOut: false, alwaysAssigned: false, variableDeclared: false, readInside: true, writtenInside: false, readOutside: true, writtenOutside: false), VariableStyle.InputOnly);
+ s_matrix.Add(new Key(dataFlowIn: true, dataFlowOut: false, alwaysAssigned: false, variableDeclared: false, readInside: true, writtenInside: false, readOutside: true, writtenOutside: true), VariableStyle.InputOnly);
+ s_matrix.Add(new Key(dataFlowIn: true, dataFlowOut: false, alwaysAssigned: false, variableDeclared: false, readInside: true, writtenInside: true, readOutside: false, writtenOutside: false), VariableStyle.InputOnly);
+ s_matrix.Add(new Key(dataFlowIn: true, dataFlowOut: false, alwaysAssigned: false, variableDeclared: false, readInside: true, writtenInside: true, readOutside: false, writtenOutside: true), VariableStyle.InputOnly);
+ s_matrix.Add(new Key(dataFlowIn: true, dataFlowOut: false, alwaysAssigned: false, variableDeclared: false, readInside: true, writtenInside: true, readOutside: true, writtenOutside: false), VariableStyle.InputOnly);
+ s_matrix.Add(new Key(dataFlowIn: true, dataFlowOut: false, alwaysAssigned: false, variableDeclared: false, readInside: true, writtenInside: true, readOutside: true, writtenOutside: true), VariableStyle.InputOnly);
+ s_matrix.Add(new Key(dataFlowIn: true, dataFlowOut: false, alwaysAssigned: true, variableDeclared: false, readInside: true, writtenInside: true, readOutside: false, writtenOutside: false), VariableStyle.InputOnly);
+ s_matrix.Add(new Key(dataFlowIn: true, dataFlowOut: false, alwaysAssigned: true, variableDeclared: false, readInside: true, writtenInside: true, readOutside: false, writtenOutside: true), VariableStyle.InputOnly);
+ s_matrix.Add(new Key(dataFlowIn: true, dataFlowOut: false, alwaysAssigned: true, variableDeclared: false, readInside: true, writtenInside: true, readOutside: true, writtenOutside: false), VariableStyle.InputOnly);
+ s_matrix.Add(new Key(dataFlowIn: true, dataFlowOut: false, alwaysAssigned: true, variableDeclared: false, readInside: true, writtenInside: true, readOutside: true, writtenOutside: true), VariableStyle.InputOnly);
+ s_matrix.Add(new Key(dataFlowIn: true, dataFlowOut: true, alwaysAssigned: false, variableDeclared: false, readInside: true, writtenInside: true, readOutside: true, writtenOutside: false), VariableStyle.OutWithErrorInput);
+ s_matrix.Add(new Key(dataFlowIn: true, dataFlowOut: true, alwaysAssigned: false, variableDeclared: false, readInside: true, writtenInside: true, readOutside: true, writtenOutside: true), VariableStyle.Ref);
+ s_matrix.Add(new Key(dataFlowIn: true, dataFlowOut: true, alwaysAssigned: true, variableDeclared: false, readInside: true, writtenInside: true, readOutside: true, writtenOutside: false), VariableStyle.OutWithErrorInput);
+ s_matrix.Add(new Key(dataFlowIn: true, dataFlowOut: true, alwaysAssigned: true, variableDeclared: false, readInside: true, writtenInside: true, readOutside: true, writtenOutside: true), VariableStyle.Ref);
+ }
+
+ private struct Key : IEquatable<Key>
+ {
+ public bool DataFlowIn { get; }
+ public bool DataFlowOut { get; }
+ public bool AlwaysAssigned { get; }
+ public bool VariableDeclared { get; }
+ public bool ReadInside { get; }
+ public bool WrittenInside { get; }
+ public bool ReadOutside { get; }
+ public bool WrittenOutside { get; }
+
+ public Key(
+ bool dataFlowIn,
+ bool dataFlowOut,
+ bool alwaysAssigned,
+ bool variableDeclared,
+ bool readInside,
+ bool writtenInside,
+ bool readOutside,
+ bool writtenOutside) :
+ this()
+ {
+ this.DataFlowIn = dataFlowIn;
+ this.DataFlowOut = dataFlowOut;
+ this.AlwaysAssigned = alwaysAssigned;
+ this.VariableDeclared = variableDeclared;
+ this.ReadInside = readInside;
+ this.WrittenInside = writtenInside;
+ this.ReadOutside = readOutside;
+ this.WrittenOutside = writtenOutside;
+ }
+
+ public bool Equals(Key key)
+ {
+ return this.DataFlowIn == key.DataFlowIn &&
+ this.DataFlowOut == key.DataFlowOut &&
+ this.AlwaysAssigned == key.AlwaysAssigned &&
+ this.VariableDeclared == key.VariableDeclared &&
+ this.ReadInside == key.ReadInside &&
+ this.WrittenInside == key.WrittenInside &&
+ this.ReadOutside == key.ReadOutside &&
+ this.WrittenOutside == key.WrittenOutside;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj is Key)
+ {
+ return Equals((Key)obj);
+ }
+
+ return false;
+ }
+
+ public override int GetHashCode()
+ {
+ var hashCode = 0;
+
+ hashCode = this.DataFlowIn ? 1 << 7 | hashCode : hashCode;
+ hashCode = this.DataFlowOut ? 1 << 6 | hashCode : hashCode;
+ hashCode = this.AlwaysAssigned ? 1 << 5 | hashCode : hashCode;
+ hashCode = this.VariableDeclared ? 1 << 4 | hashCode : hashCode;
+ hashCode = this.ReadInside ? 1 << 3 | hashCode : hashCode;
+ hashCode = this.WrittenInside ? 1 << 2 | hashCode : hashCode;
+ hashCode = this.ReadOutside ? 1 << 1 | hashCode : hashCode;
+ hashCode = this.WrittenOutside ? 1 << 0 | hashCode : hashCode;
+
+ return hashCode;
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ExtractMethodOptions.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ExtractMethodOptions.cs
new file mode 100644
index 0000000000..dccabba2d9
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ExtractMethodOptions.cs
@@ -0,0 +1,17 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.CodeAnalysis.Options;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public static class ExtractMethodOptions
+ {
+ public const string FeatureName = "ExtractMethod";
+
+ public static readonly PerLanguageOption<bool> AllowBestEffort = new PerLanguageOption<bool>(FeatureName, "Allow Best Effort", defaultValue: false);
+
+ public static readonly PerLanguageOption<bool> DontPutOutOrRefOnStruct = new PerLanguageOption<bool>(FeatureName, "Don't Put Out Or Ref On Strcut", defaultValue: true);
+
+ public static readonly PerLanguageOption<bool> AllowMovingDeclaration = new PerLanguageOption<bool>(FeatureName, "Allow Moving Declaration", defaultValue: false);
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ExtractMethodResult.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ExtractMethodResult.cs
new file mode 100644
index 0000000000..7aaf0fad80
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ExtractMethodResult.cs
@@ -0,0 +1,66 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using Roslyn.Utilities;
+using ICSharpCode.NRefactory6.CSharp;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public class ExtractMethodResult
+ {
+ /// <summary>
+ /// True if the extract method operation succeeded.
+ /// </summary>
+ public bool Succeeded { get; }
+
+ /// <summary>
+ /// True if the extract method operation is possible if the original span is adjusted.
+ /// </summary>
+ public bool SucceededWithSuggestion { get; }
+
+ /// <summary>
+ /// The transformed document that was produced as a result of the extract method operation.
+ /// </summary>
+ public Document Document { get; }
+
+ /// <summary>
+ /// The reasons why the extract method operation did not succeed.
+ /// </summary>
+ public IEnumerable<string> Reasons { get; }
+
+ /// <summary>
+ /// the generated method node that contains the extracted code.
+ /// </summary>
+ public SyntaxNode MethodDeclarationNode { get; }
+
+ /// <summary>
+ /// The name token for the invocation node that replaces the extracted code.
+ /// </summary>
+ public SyntaxToken InvocationNameToken { get; }
+
+ public ExtractMethodResult(
+ OperationStatusFlag status,
+ IEnumerable<string> reasons,
+ Document document,
+ SyntaxToken invocationNameToken,
+ SyntaxNode methodDeclarationNode)
+ {
+ this.Status = status;
+
+ this.Succeeded = status.Succeeded() && !status.HasSuggestion();
+ this.SucceededWithSuggestion = status.Succeeded() && status.HasSuggestion();
+
+ this.Reasons = (reasons ?? SpecializedCollections.EmptyEnumerable<string>()).ToReadOnlyCollection();
+
+ this.Document = document;
+ this.InvocationNameToken = invocationNameToken;
+ this.MethodDeclarationNode = methodDeclarationNode;
+ }
+
+ /// <summary>
+ /// public status of result. more fine grained reason why it is failed.
+ /// </summary>
+ public OperationStatusFlag Status { get; }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ExtractMethodService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ExtractMethodService.cs
new file mode 100644
index 0000000000..3ac1794b1c
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ExtractMethodService.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.Options;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public static class ExtractMethodService
+ {
+ readonly static CSharpExtractMethodService service = new CSharpExtractMethodService ();
+
+ public static Task<ExtractMethodResult> ExtractMethodAsync(Document document, TextSpan textSpan, OptionSet options = null, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ return service.ExtractMethodAsync(document, textSpan, options, cancellationToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/FailedExtractMethodResult.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/FailedExtractMethodResult.cs
new file mode 100644
index 0000000000..62391a1b66
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/FailedExtractMethodResult.cs
@@ -0,0 +1,13 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public class FailedExtractMethodResult : ExtractMethodResult
+ {
+ public FailedExtractMethodResult(OperationStatus status)
+ : base(status.Flag, status.Reasons, null, default(SyntaxToken), default(SyntaxNode))
+ {
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/IExtractMethodService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/IExtractMethodService.cs
new file mode 100644
index 0000000000..e5c2aa786c
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/IExtractMethodService.cs
@@ -0,0 +1,16 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.Host;
+using Microsoft.CodeAnalysis.Options;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public interface IExtractMethodService : ILanguageService
+ {
+ Task<ExtractMethodResult> ExtractMethodAsync(Document document, TextSpan textSpan, OptionSet options = null, CancellationToken cancellationToken = default(CancellationToken));
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ISyntaxTriviaService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ISyntaxTriviaService.cs
new file mode 100644
index 0000000000..f75f66ce20
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ISyntaxTriviaService.cs
@@ -0,0 +1,66 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using Microsoft.CodeAnalysis.Host;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public enum TriviaLocation
+ {
+ BeforeBeginningOfSpan = 0,
+ AfterBeginningOfSpan,
+ BeforeEndOfSpan,
+ AfterEndOfSpan
+ }
+
+ public struct PreviousNextTokenPair
+ {
+ public SyntaxToken PreviousToken { get; set; }
+ public SyntaxToken NextToken { get; set; }
+ }
+
+ public struct LeadingTrailingTriviaPair
+ {
+ public IEnumerable<SyntaxTrivia> LeadingTrivia { get; set; }
+ public IEnumerable<SyntaxTrivia> TrailingTrivia { get; set; }
+ }
+
+ public delegate SyntaxToken AnnotationResolver(SyntaxNode root, TriviaLocation location, SyntaxAnnotation annotation);
+ public delegate IEnumerable<SyntaxTrivia> TriviaResolver(TriviaLocation location, PreviousNextTokenPair tokenPair, Dictionary<SyntaxToken, LeadingTrailingTriviaPair> triviaMap);
+
+ /// <summary>
+ /// contains information to restore trivia later on to the annotated tree
+ /// </summary>
+ public interface ITriviaSavedResult
+ {
+ /// <summary>
+ /// root node of the annotated tree.
+ /// </summary>
+ SyntaxNode Root { get; }
+
+ /// <summary>
+ /// restore saved trivia to given tree
+ /// </summary>
+ /// <param name="root">root node to the annotated tree</param>
+ /// <param name="annotationResolver">it provides a custom way of resolving annotations to retrieve right tokens to attach trivia</param>
+ /// <param name="triviaResolver">it provides a custom way of creating trivia list between two tokens</param>
+ /// <returns>root node to a trivia restored tree</returns>
+ SyntaxNode RestoreTrivia(SyntaxNode root, AnnotationResolver annotationResolver = null, TriviaResolver triviaResolver = null);
+ }
+
+ /// <summary>
+ /// syntax trivia related services
+ /// </summary>
+ public interface ISyntaxTriviaService : ILanguageService
+ {
+ /// <summary>
+ /// save trivia around span and let user restore trivia later
+ /// </summary>
+ /// <param name="root">root node of a tree</param>
+ /// <param name="textSpan">selection whose trivia around its edges will be saved</param>
+ /// <returns>object that holds onto enough information to restore trivia later</returns>
+ ITriviaSavedResult SaveTriviaAroundSelection(SyntaxNode root, TextSpan textSpan);
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/InsertionPoint.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/InsertionPoint.cs
new file mode 100644
index 0000000000..d0d74659fd
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/InsertionPoint.cs
@@ -0,0 +1,64 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public class InsertionPoint
+ {
+ private readonly SyntaxAnnotation _annotation;
+ private readonly Lazy<SyntaxNode> _context;
+
+ public static async Task<InsertionPoint> CreateAsync(SemanticDocument document, SyntaxNode node, CancellationToken cancellationToken)
+ {
+ var root = document.Root;
+ var annotation = new SyntaxAnnotation();
+ var newRoot = root.AddAnnotations(SpecializedCollections.SingletonEnumerable(Tuple.Create(node, annotation)));
+ return new InsertionPoint(await document.WithSyntaxRootAsync(newRoot, cancellationToken).ConfigureAwait(false), annotation);
+ }
+
+ private InsertionPoint(SemanticDocument document, SyntaxAnnotation annotation)
+ {
+ //Contract.ThrowIfNull(document);
+ //Contract.ThrowIfNull(annotation);
+
+ this.SemanticDocument = document;
+ _annotation = annotation;
+ _context = CreateLazyContextNode();
+ }
+
+ public SemanticDocument SemanticDocument { get; }
+
+ public SyntaxNode GetRoot()
+ {
+ return this.SemanticDocument.Root;
+ }
+
+ public SyntaxNode GetContext()
+ {
+ return _context.Value;
+ }
+
+ public InsertionPoint With(SemanticDocument document)
+ {
+ return new InsertionPoint(document, _annotation);
+ }
+
+ private Lazy<SyntaxNode> CreateLazyContextNode()
+ {
+ return new Lazy<SyntaxNode>(ComputeContextNode, isThreadSafe: true);
+ }
+
+ private SyntaxNode ComputeContextNode()
+ {
+ var root = this.SemanticDocument.Root;
+ return root.GetAnnotatedNodesAndTokens(_annotation).Single().AsNode();
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.Analyzer.SymbolMapBuilder.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.Analyzer.SymbolMapBuilder.cs
new file mode 100644
index 0000000000..82ce82f155
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.Analyzer.SymbolMapBuilder.cs
@@ -0,0 +1,79 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Text;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public abstract partial class MethodExtractor
+ {
+ protected abstract partial class Analyzer
+ {
+ private class SymbolMapBuilder : SyntaxWalker
+ {
+ private readonly SemanticModel _semanticModel;
+ //private readonly ISyntaxFactsService _service;
+ private readonly TextSpan _span;
+ private readonly Dictionary<ISymbol, List<SyntaxToken>> _symbolMap;
+ private readonly CancellationToken _cancellationToken;
+
+ public static Dictionary<ISymbol, List<SyntaxToken>> Build(
+ // ISyntaxFactsService service,
+ SemanticModel semanticModel,
+ SyntaxNode root,
+ TextSpan span,
+ CancellationToken cancellationToken)
+ {
+ //Contract.ThrowIfNull(semanticModel);
+// Contract.ThrowIfNull(service);
+ //Contract.ThrowIfNull(root);
+
+ var builder = new SymbolMapBuilder(/*service, */semanticModel, span, cancellationToken);
+ builder.Visit(root);
+
+ return builder._symbolMap;
+ }
+
+ private SymbolMapBuilder(
+ // ISyntaxFactsService service,
+ SemanticModel semanticModel,
+ TextSpan span,
+ CancellationToken cancellationToken)
+ : base(SyntaxWalkerDepth.Token)
+ {
+ _semanticModel = semanticModel;
+ // _service = service;
+ _span = span;
+ _symbolMap = new Dictionary<ISymbol, List<SyntaxToken>>();
+ _cancellationToken = cancellationToken;
+ }
+
+ protected override void VisitToken(SyntaxToken token)
+ {
+ if (token.IsMissing ||
+ token.Width() <= 0 ||
+ !token.IsIdentifier() ||
+ !_span.Contains(token.Span) ||
+ token.Parent.IsNamedParameter())
+ {
+ return;
+ }
+
+ var symbolInfo = _semanticModel.GetSymbolInfo(token, _cancellationToken);
+ foreach (var sym in symbolInfo.GetAllSymbols())
+ {
+ // add binding result to map
+ var list = _symbolMap.GetOrAdd(sym, _ => new List<SyntaxToken>());
+ list.Add(token);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.Analyzer.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.Analyzer.cs
new file mode 100644
index 0000000000..300773b942
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.Analyzer.cs
@@ -0,0 +1,957 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public abstract partial class MethodExtractor
+ {
+ protected abstract partial class Analyzer
+ {
+ private readonly SemanticDocument _semanticDocument;
+
+ protected readonly CancellationToken CancellationToken;
+ protected readonly SelectionResult SelectionResult;
+
+ protected Analyzer(SelectionResult selectionResult, CancellationToken cancellationToken)
+ {
+ //Contract.ThrowIfNull(selectionResult);
+
+ this.SelectionResult = selectionResult;
+ _semanticDocument = selectionResult.SemanticDocument;
+ this.CancellationToken = cancellationToken;
+ }
+
+ /// <summary>
+ /// convert text span to node range for the flow analysis API
+ /// </summary>
+ protected abstract Tuple<SyntaxNode, SyntaxNode> GetFlowAnalysisNodeRange();
+
+ /// <summary>
+ /// check whether selection contains return statement or not
+ /// </summary>
+ protected abstract bool ContainsReturnStatementInSelectedCode(IEnumerable<SyntaxNode> jumpOutOfRegionStatements);
+
+ /// <summary>
+ /// create VariableInfo type
+ /// </summary>
+ protected abstract VariableInfo CreateFromSymbol(Compilation compilation, ISymbol symbol, ITypeSymbol type, VariableStyle variableStyle, bool variableDeclared);
+
+ /// <summary>
+ /// among variables that will be used as parameters at the extracted method, check whether one of the parameter can be used as return
+ /// </summary>
+ protected abstract int GetIndexOfVariableInfoToUseAsReturnValue(IList<VariableInfo> variableInfo);
+
+ /// <summary>
+ /// get type of the range variable symbol
+ /// </summary>
+ protected abstract ITypeSymbol GetRangeVariableType(SemanticModel model, IRangeVariableSymbol symbol);
+
+ /// <summary>
+ /// check whether the selection is at the placed where read-only field is allowed to be extracted out
+ /// </summary>
+ /// <returns></returns>
+ protected abstract bool ReadOnlyFieldAllowed();
+
+ public async Task<AnalyzerResult> AnalyzeAsync()
+ {
+ // do data flow analysis
+ var model = _semanticDocument.SemanticModel;
+ var dataFlowAnalysisData = GetDataFlowAnalysisData(model);
+
+ // build symbol map for the identifiers used inside of the selection
+ var symbolMap = GetSymbolMap(model);
+
+ // gather initial local or parameter variable info
+ var variableInfoMap = GenerateVariableInfoMap(model, dataFlowAnalysisData, symbolMap);
+
+ // check whether instance member is used inside of the selection
+ var instanceMemberIsUsed = IsInstanceMemberUsedInSelectedCode(dataFlowAnalysisData);
+
+ // check whether end of selection is reachable
+ var endOfSelectionReachable = IsEndOfSelectionReachable(model);
+
+ // collects various variable informations
+ // extracted code contains return value
+ var isInExpressionOrHasReturnStatement = IsInExpressionOrHasReturnStatement(model);
+ var signatureTuple = GetSignatureInformation(model, dataFlowAnalysisData, variableInfoMap, isInExpressionOrHasReturnStatement);
+
+ var parameters = signatureTuple.Item1;
+ var returnType = signatureTuple.Item2;
+ var variableToUseAsReturnValue = signatureTuple.Item3;
+ var unsafeAddressTakenUsed = signatureTuple.Item4;
+
+ var returnTypeTuple = AdjustReturnType(model, returnType);
+
+ returnType = returnTypeTuple.Item1;
+ bool returnTypeHasAnonymousType = returnTypeTuple.Item2;
+ bool awaitTaskReturn = returnTypeTuple.Item3;
+
+ // create new document
+ var newDocument = await CreateDocumentWithAnnotationsAsync(_semanticDocument, parameters, CancellationToken).ConfigureAwait(false);
+
+ // collect method type variable used in selected code
+ var sortedMap = new SortedDictionary<int, ITypeParameterSymbol>();
+ var typeParametersInConstraintList = GetMethodTypeParametersInConstraintList(model, variableInfoMap, symbolMap, sortedMap);
+ var typeParametersInDeclaration = GetMethodTypeParametersInDeclaration(returnType, sortedMap);
+
+ // check various error cases
+ var operationStatus = GetOperationStatus(model, symbolMap, parameters, unsafeAddressTakenUsed, returnTypeHasAnonymousType);
+
+ return new AnalyzerResult(
+ newDocument,
+ typeParametersInDeclaration, typeParametersInConstraintList,
+ parameters, variableToUseAsReturnValue, returnType, awaitTaskReturn,
+ instanceMemberIsUsed, endOfSelectionReachable, operationStatus);
+ }
+
+ private Tuple<ITypeSymbol, bool, bool> AdjustReturnType(SemanticModel model, ITypeSymbol returnType)
+ {
+ // check whether return type contains anonymous type and if it does, fix it up by making it object
+ var returnTypeHasAnonymousType = returnType.ContainsAnonymousType();
+ returnType = returnTypeHasAnonymousType ? returnType.RemoveAnonymousTypes(model.Compilation) : returnType;
+
+ // if selection contains await which is not under async lambda or anonymous delegate,
+ // change return type to be wrapped in Task
+ var shouldPutAsyncModifier = this.SelectionResult.ShouldPutAsyncModifier();
+ if (shouldPutAsyncModifier)
+ {
+ bool awaitTaskReturn;
+ WrapReturnTypeInTask(model, ref returnType, out awaitTaskReturn);
+
+ return Tuple.Create(returnType, returnTypeHasAnonymousType, awaitTaskReturn);
+ }
+
+ // unwrap task if needed
+ UnwrapTaskIfNeeded(model, ref returnType);
+ return Tuple.Create(returnType, returnTypeHasAnonymousType, false);
+ }
+
+ private void UnwrapTaskIfNeeded(SemanticModel model, ref ITypeSymbol returnType)
+ {
+ // nothing to unwrap
+ if (!this.SelectionResult.ContainingScopeHasAsyncKeyword() ||
+ !this.ContainsReturnStatementInSelectedCode(model))
+ {
+ return;
+ }
+
+ var originalDefinition = returnType.OriginalDefinition;
+
+ // see whether it needs to be unwrapped
+ var taskType = model.Compilation.TaskType();
+ if (originalDefinition.Equals(taskType))
+ {
+ returnType = model.Compilation.GetSpecialType(SpecialType.System_Void);
+ return;
+ }
+
+ var genericTaskType = model.Compilation.TaskOfTType();
+ if (originalDefinition.Equals(genericTaskType))
+ {
+ returnType = ((INamedTypeSymbol)returnType).TypeArguments[0];
+ return;
+ }
+
+ // nothing to unwrap
+ return;
+ }
+
+ private void WrapReturnTypeInTask(SemanticModel model, ref ITypeSymbol returnType, out bool awaitTaskReturn)
+ {
+ awaitTaskReturn = false;
+
+ var genericTaskType = model.Compilation.TaskOfTType();
+ var taskType = model.Compilation.TaskType();
+
+ if (returnType.Equals(model.Compilation.GetSpecialType(SpecialType.System_Void)))
+ {
+ // convert void to Task type
+ awaitTaskReturn = true;
+ returnType = taskType;
+ return;
+ }
+
+ if (this.SelectionResult.SelectionInExpression)
+ {
+ returnType = genericTaskType.Construct(returnType);
+ return;
+ }
+
+ if (ContainsReturnStatementInSelectedCode(model))
+ {
+ // check whether we will use return type as it is or not.
+ awaitTaskReturn = returnType.Equals(taskType);
+ return;
+ }
+
+ // okay, wrap the return type in Task<T>
+ returnType = genericTaskType.Construct(returnType);
+ }
+
+ private Tuple<IList<VariableInfo>, ITypeSymbol, VariableInfo, bool> GetSignatureInformation(
+ SemanticModel model,
+ DataFlowAnalysis dataFlowAnalysisData,
+ IDictionary<ISymbol, VariableInfo> variableInfoMap,
+ bool isInExpressionOrHasReturnStatement)
+ {
+ if (isInExpressionOrHasReturnStatement)
+ {
+ // check whether current selection contains return statement
+ var parameters = GetMethodParameters(variableInfoMap.Values);
+ var returnType = this.SelectionResult.GetContainingScopeType();
+ if (returnType == null)
+ {
+ returnType = model.Compilation.GetSpecialType(SpecialType.System_Object);
+ }
+
+ var unsafeAddressTakenUsed = ContainsVariableUnsafeAddressTaken(dataFlowAnalysisData, variableInfoMap.Keys);
+ return Tuple.Create(parameters, returnType, default(VariableInfo), unsafeAddressTakenUsed);
+ }
+ else
+ {
+ // no return statement
+ var parameters = MarkVariableInfoToUseAsReturnValueIfPossible(GetMethodParameters(variableInfoMap.Values));
+ var variableToUseAsReturnValue = parameters.FirstOrDefault(v => v.UseAsReturnValue);
+ var returnType = default(ITypeSymbol);
+ if (variableToUseAsReturnValue != null)
+ {
+ returnType = variableToUseAsReturnValue.GetVariableType(_semanticDocument);
+ }
+ else
+ {
+ returnType = model.Compilation.GetSpecialType(SpecialType.System_Void);
+ }
+
+ var unsafeAddressTakenUsed = ContainsVariableUnsafeAddressTaken(dataFlowAnalysisData, variableInfoMap.Keys);
+ return Tuple.Create(parameters, returnType, variableToUseAsReturnValue, unsafeAddressTakenUsed);
+ }
+ }
+
+ private bool IsInExpressionOrHasReturnStatement(SemanticModel model)
+ {
+ var isInExpressionOrHasReturnStatement = this.SelectionResult.SelectionInExpression;
+ if (!isInExpressionOrHasReturnStatement)
+ {
+ var containsReturnStatement = ContainsReturnStatementInSelectedCode(model);
+ isInExpressionOrHasReturnStatement |= containsReturnStatement;
+ }
+
+ return isInExpressionOrHasReturnStatement;
+ }
+
+ private OperationStatus GetOperationStatus(
+ SemanticModel model, Dictionary<ISymbol, List<SyntaxToken>> symbolMap, IList<VariableInfo> parameters,
+ bool unsafeAddressTakenUsed, bool returnTypeHasAnonymousType)
+ {
+ var readonlyFieldStatus = CheckReadOnlyFields(model, symbolMap);
+
+ var namesWithAnonymousTypes = parameters.Where(v => v.OriginalTypeHadAnonymousTypeOrDelegate).Select(v => v.Name ?? string.Empty);
+ if (returnTypeHasAnonymousType)
+ {
+ namesWithAnonymousTypes = namesWithAnonymousTypes.Concat("return type");
+ }
+
+ var anonymousTypeStatus = namesWithAnonymousTypes.Any() ?
+ new OperationStatus(OperationStatusFlag.BestEffort, string.Format("FeaturesResources.ContainsAnonymousType", string.Join(", ", namesWithAnonymousTypes))) :
+ OperationStatus.Succeeded;
+
+ var unsafeAddressStatus = unsafeAddressTakenUsed ? OperationStatus.UnsafeAddressTaken : OperationStatus.Succeeded;
+
+ var asyncRefOutParameterStatue = CheckAsyncMethodRefOutParameters(parameters);
+
+ return readonlyFieldStatus.With(anonymousTypeStatus).With(unsafeAddressStatus).With(asyncRefOutParameterStatue);
+ }
+
+ private OperationStatus CheckAsyncMethodRefOutParameters(IList<VariableInfo> parameters)
+ {
+ if (this.SelectionResult.ShouldPutAsyncModifier())
+ {
+ var names = parameters.Where(v => !v.UseAsReturnValue && (v.ParameterModifier == ParameterBehavior.Out || v.ParameterModifier == ParameterBehavior.Ref))
+ .Select(p => p.Name ?? string.Empty);
+
+ if (names.Any())
+ {
+ return new OperationStatus(OperationStatusFlag.BestEffort, string.Format("FeaturesResources.AsyncMethodWithRefOutParameters", string.Join(", ", names)));
+ }
+ }
+
+ return OperationStatus.Succeeded;
+ }
+
+ private Task<SemanticDocument> CreateDocumentWithAnnotationsAsync(SemanticDocument document, IList<VariableInfo> variables, CancellationToken cancellationToken)
+ {
+ var annotations = new List<Tuple<SyntaxToken, SyntaxAnnotation>>(variables.Count);
+ variables.Do(v => v.AddIdentifierTokenAnnotationPair(annotations, cancellationToken));
+
+ if (annotations.Count == 0)
+ {
+ return Task.FromResult(document);
+ }
+
+ return document.WithSyntaxRootAsync(document.Root.AddAnnotations(annotations), cancellationToken);
+ }
+
+ private Dictionary<ISymbol, List<SyntaxToken>> GetSymbolMap(SemanticModel model)
+ {
+ var context = this.SelectionResult.GetContainingScope();
+ var symbolMap = SymbolMapBuilder.Build(model, context, this.SelectionResult.FinalSpan, CancellationToken);
+
+ return symbolMap;
+ }
+
+ private bool ContainsVariableUnsafeAddressTaken(DataFlowAnalysis dataFlowAnalysisData, IEnumerable<ISymbol> symbols)
+ {
+ // check whether the selection contains "&" over a symbol exist
+ var map = new HashSet<ISymbol>(dataFlowAnalysisData.UnsafeAddressTaken);
+ return symbols.Any(s => map.Contains(s));
+ }
+
+ private DataFlowAnalysis GetDataFlowAnalysisData(SemanticModel model)
+ {
+ if (this.SelectionResult.SelectionInExpression)
+ {
+ return model.AnalyzeDataFlow(this.SelectionResult.GetContainingScope());
+ }
+
+ var pair = GetFlowAnalysisNodeRange();
+ return model.AnalyzeDataFlow(pair.Item1, pair.Item2);
+ }
+
+ private bool IsEndOfSelectionReachable(SemanticModel model)
+ {
+ if (this.SelectionResult.SelectionInExpression)
+ {
+ return true;
+ }
+
+ var pair = GetFlowAnalysisNodeRange();
+ var analysis = model.AnalyzeControlFlow(pair.Item1, pair.Item2);
+ return analysis.EndPointIsReachable;
+ }
+
+ private IList<VariableInfo> MarkVariableInfoToUseAsReturnValueIfPossible(IList<VariableInfo> variableInfo)
+ {
+ var variableToUseAsReturnValueIndex = GetIndexOfVariableInfoToUseAsReturnValue(variableInfo);
+ if (variableToUseAsReturnValueIndex >= 0)
+ {
+ variableInfo[variableToUseAsReturnValueIndex] = VariableInfo.CreateReturnValue(variableInfo[variableToUseAsReturnValueIndex]);
+ }
+
+ return variableInfo;
+ }
+
+ private IList<VariableInfo> GetMethodParameters(ICollection<VariableInfo> variableInfo)
+ {
+ var list = new List<VariableInfo>(variableInfo);
+
+ list.Sort(VariableInfo.Compare);
+
+ return list;
+ }
+
+ private IDictionary<ISymbol, VariableInfo> GenerateVariableInfoMap(
+ SemanticModel model, DataFlowAnalysis dataFlowAnalysisData, Dictionary<ISymbol, List<SyntaxToken>> symbolMap)
+ {
+// Contract.ThrowIfNull(model);
+// Contract.ThrowIfNull(dataFlowAnalysisData);
+
+ var variableInfoMap = new Dictionary<ISymbol, VariableInfo>();
+
+ // create map of each data
+ var capturedMap = new HashSet<ISymbol>(dataFlowAnalysisData.Captured);
+ var dataFlowInMap = new HashSet<ISymbol>(dataFlowAnalysisData.DataFlowsIn);
+ var dataFlowOutMap = new HashSet<ISymbol>(dataFlowAnalysisData.DataFlowsOut);
+ var alwaysAssignedMap = new HashSet<ISymbol>(dataFlowAnalysisData.AlwaysAssigned);
+ var variableDeclaredMap = new HashSet<ISymbol>(dataFlowAnalysisData.VariablesDeclared);
+ var readInsideMap = new HashSet<ISymbol>(dataFlowAnalysisData.ReadInside);
+ var writtenInsideMap = new HashSet<ISymbol>(dataFlowAnalysisData.WrittenInside);
+ var readOutsideMap = new HashSet<ISymbol>(dataFlowAnalysisData.ReadOutside);
+ var writtenOutsideMap = new HashSet<ISymbol>(dataFlowAnalysisData.WrittenOutside);
+ var unsafeAddressTakenMap = new HashSet<ISymbol>(dataFlowAnalysisData.UnsafeAddressTaken);
+
+ // gather all meaningful symbols for the span.
+ var candidates = new HashSet<ISymbol>(readInsideMap);
+ candidates.UnionWith(writtenInsideMap);
+ candidates.UnionWith(variableDeclaredMap);
+
+ foreach (var symbol in candidates)
+ {
+ if (IsThisParameter(symbol) ||
+ IsInteractiveSynthesizedParameter(symbol))
+ {
+ continue;
+ }
+
+ var captured = capturedMap.Contains(symbol);
+ var dataFlowIn = dataFlowInMap.Contains(symbol);
+ var dataFlowOut = dataFlowOutMap.Contains(symbol);
+ var alwaysAssigned = alwaysAssignedMap.Contains(symbol);
+ var variableDeclared = variableDeclaredMap.Contains(symbol);
+ var readInside = readInsideMap.Contains(symbol);
+ var writtenInside = writtenInsideMap.Contains(symbol);
+ var readOutside = readOutsideMap.Contains(symbol);
+ var writtenOutside = writtenOutsideMap.Contains(symbol);
+ var unsafeAddressTaken = unsafeAddressTakenMap.Contains(symbol);
+
+ // if it is static local, make sure it is not defined inside
+ if (symbol.IsStatic)
+ {
+ dataFlowIn = dataFlowIn && !variableDeclared;
+ }
+
+ // make sure readoutside is true when dataflowout is true (bug #3790)
+ // when a variable is only used inside of loop, a situation where dataflowout == true and readOutside == false
+ // can happen. but for extract method's point of view, this is not an information that would affect output.
+ // so, here we adjust flags to follow predefined assumption.
+ readOutside = readOutside || dataFlowOut;
+
+ // make sure data flow out is true when declared inside/written inside/read outside/not written outside are true (bug #6277)
+ dataFlowOut = dataFlowOut || (variableDeclared && writtenInside && readOutside && !writtenOutside);
+
+ // variable that is declared inside but never referenced outside. just ignore it and move to next one.
+ if (variableDeclared && !dataFlowOut && !readOutside && !writtenOutside)
+ {
+ continue;
+ }
+
+ // parameter defined inside of the selection (such as lambda parameter) will be ignored (bug # 10964)
+ if (symbol is IParameterSymbol && variableDeclared)
+ {
+ continue;
+ }
+
+ var type = GetSymbolType(model, symbol);
+ if (type == null)
+ {
+ continue;
+ }
+
+ var variableStyle = GetVariableStyle(symbolMap, symbol, model, type,
+ captured, dataFlowIn, dataFlowOut, alwaysAssigned, variableDeclared,
+ readInside, writtenInside, readOutside, writtenOutside, unsafeAddressTaken);
+
+ AddVariableToMap(variableInfoMap, symbol, CreateFromSymbol(model.Compilation, symbol, type, variableStyle, variableDeclared));
+ }
+
+ return variableInfoMap;
+ }
+
+ private void AddVariableToMap(IDictionary<ISymbol, VariableInfo> variableInfoMap, ISymbol localOrParameter, VariableInfo variableInfo)
+ {
+ variableInfoMap.Add(localOrParameter, variableInfo);
+ }
+
+ private VariableStyle GetVariableStyle(
+ Dictionary<ISymbol, List<SyntaxToken>> symbolMap,
+ ISymbol symbol,
+ SemanticModel model,
+ ITypeSymbol type,
+ bool captured,
+ bool dataFlowIn,
+ bool dataFlowOut,
+ bool alwaysAssigned,
+ bool variableDeclared,
+ bool readInside,
+ bool writtenInside,
+ bool readOutside,
+ bool writtenOutside,
+ bool unsafeAddressTaken)
+ {
+// Contract.ThrowIfNull(model);
+// Contract.ThrowIfNull(type);
+
+ var style = ExtractMethodMatrix.GetVariableStyle(captured, dataFlowIn, dataFlowOut, alwaysAssigned, variableDeclared,
+ readInside, writtenInside, readOutside, writtenOutside, unsafeAddressTaken);
+
+ if (SelectionContainsOnlyIdentifierWithSameType(type))
+ {
+ return style;
+ }
+
+ if (UserDefinedValueType(model.Compilation, type) && !this.SelectionResult.DontPutOutOrRefOnStruct)
+ {
+ return AlwaysReturn(style);
+ }
+
+ // for captured variable, never try to move the decl into extracted method
+ if (captured && (style == VariableStyle.MoveIn))
+ {
+ return VariableStyle.Out;
+ }
+
+ // check special value type cases
+ if (type.IsValueType && !IsWrittenInsideForFrameworkValueType(symbolMap, model, symbol, writtenInside))
+ {
+ return style;
+ }
+
+ // don't blindly always return. make sure there is a write inside of the selection
+ if (this.SelectionResult.AllowMovingDeclaration || !writtenInside)
+ {
+ return style;
+ }
+
+ return AlwaysReturn(style);
+ }
+
+ private bool IsWrittenInsideForFrameworkValueType(
+ Dictionary<ISymbol, List<SyntaxToken>> symbolMap, SemanticModel model, ISymbol symbol, bool writtenInside)
+ {
+ List<SyntaxToken> tokens;
+ if (!symbolMap.TryGetValue(symbol, out tokens))
+ {
+ return writtenInside;
+ }
+
+ // this relies on the fact that our IsWrittenTo only cares about syntax to figure out whether
+ // something is written to or not. but not semantic.
+ // we probably need to move the API to syntaxFact service not semanticFact.
+ //
+ // if one wants to get result that also considers semantic, he should use data control flow analysis API.
+ return tokens.Any(t => t.Parent is ExpressionSyntax && ((ExpressionSyntax)t.Parent).IsWrittenTo());
+ }
+
+ private bool SelectionContainsOnlyIdentifierWithSameType(ITypeSymbol type)
+ {
+ if (!this.SelectionResult.SelectionInExpression)
+ {
+ return false;
+ }
+
+ var firstToken = this.SelectionResult.GetFirstTokenInSelection();
+ var lastToken = this.SelectionResult.GetLastTokenInSelection();
+
+ if (!firstToken.Equals(lastToken))
+ {
+ return false;
+ }
+
+ return type.Equals(this.SelectionResult.GetContainingScopeType());
+ }
+
+ private bool UserDefinedValueType(Compilation compilation, ITypeSymbol type)
+ {
+ if (!type.IsValueType || type.IsPointerType() || type.IsEnumType())
+ {
+ return false;
+ }
+
+ return type.OriginalDefinition.SpecialType == SpecialType.None && !WellKnownFrameworkValueType(compilation, type);
+ }
+
+ private bool WellKnownFrameworkValueType(Compilation compilation, ITypeSymbol type)
+ {
+ if (!type.IsValueType)
+ {
+ return false;
+ }
+
+ var cancellationTokenType = compilation.GetTypeByMetadataName("System.Threading.CancellationToken");
+ if (cancellationTokenType != null && cancellationTokenType.Equals(type))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ private ITypeSymbol GetSymbolType(SemanticModel model, ISymbol symbol)
+ {
+ var local = symbol as ILocalSymbol;
+ if (local != null)
+ {
+ return local.Type;
+ }
+
+ var parameter = symbol as IParameterSymbol;
+ if (parameter != null)
+ {
+ return parameter.Type;
+ }
+
+ var rangeVariable = symbol as IRangeVariableSymbol;
+ if (rangeVariable != null)
+ {
+ return GetRangeVariableType(model, rangeVariable);
+ }
+
+ return null;
+ }
+
+ protected VariableStyle AlwaysReturn(VariableStyle style)
+ {
+ if (style == VariableStyle.InputOnly)
+ {
+ return VariableStyle.Ref;
+ }
+
+ if (style == VariableStyle.MoveIn)
+ {
+ return VariableStyle.Out;
+ }
+
+ if (style == VariableStyle.SplitIn)
+ {
+ return VariableStyle.Out;
+ }
+
+ if (style == VariableStyle.SplitOut)
+ {
+ return VariableStyle.OutWithMoveOut;
+ }
+
+ return style;
+ }
+
+ private bool IsParameterUsedOutside(ISymbol localOrParameter)
+ {
+ var parameter = localOrParameter as IParameterSymbol;
+ if (parameter == null)
+ {
+ return false;
+ }
+
+ return parameter.RefKind != RefKind.None;
+ }
+
+ private bool IsParameterAssigned(ISymbol localOrParameter)
+ {
+ // hack for now.
+ var parameter = localOrParameter as IParameterSymbol;
+ if (parameter == null)
+ {
+ return false;
+ }
+
+ return parameter.RefKind != RefKind.Out;
+ }
+
+ private bool IsThisParameter(ISymbol localOrParameter)
+ {
+ var parameter = localOrParameter as IParameterSymbol;
+ if (parameter == null)
+ {
+ return false;
+ }
+
+ return parameter.IsThis;
+ }
+
+ private bool IsInteractiveSynthesizedParameter(ISymbol localOrParameter)
+ {
+ var parameter = localOrParameter as IParameterSymbol;
+ if (parameter == null)
+ {
+ return false;
+ }
+
+ return parameter.IsImplicitlyDeclared &&
+ parameter.ContainingAssembly.IsInteractive &&
+ parameter.ContainingSymbol != null &&
+ parameter.ContainingSymbol.ContainingType != null &&
+ parameter.ContainingSymbol.ContainingType.IsScriptClass;
+ }
+
+ private bool ContainsReturnStatementInSelectedCode(SemanticModel model)
+ {
+ //Contract.ThrowIfTrue(this.SelectionResult.SelectionInExpression);
+
+ var pair = GetFlowAnalysisNodeRange();
+ var controlFlowAnalysisData = model.AnalyzeControlFlow(pair.Item1, pair.Item2);
+
+ return ContainsReturnStatementInSelectedCode(controlFlowAnalysisData.ExitPoints);
+ }
+
+ private void AddTypeParametersToMap(IEnumerable<ITypeParameterSymbol> typeParameters, IDictionary<int, ITypeParameterSymbol> sortedMap)
+ {
+ foreach (var typeParameter in typeParameters)
+ {
+ AddTypeParameterToMap(typeParameter, sortedMap);
+ }
+ }
+
+ private void AddTypeParameterToMap(ITypeParameterSymbol typeParameter, IDictionary<int, ITypeParameterSymbol> sortedMap)
+ {
+ if (typeParameter == null ||
+ typeParameter.DeclaringMethod == null ||
+ sortedMap.ContainsKey(typeParameter.Ordinal))
+ {
+ return;
+ }
+
+ sortedMap[typeParameter.Ordinal] = typeParameter;
+ }
+
+ private void AppendMethodTypeVariableFromDataFlowAnalysis(
+ SemanticModel model,
+ IDictionary<ISymbol, VariableInfo> variableInfoMap,
+ IDictionary<int, ITypeParameterSymbol> sortedMap)
+ {
+ foreach (var symbol in variableInfoMap.Keys)
+ {
+ var parameter = symbol as IParameterSymbol;
+ if (parameter != null)
+ {
+ AddTypeParametersToMap(TypeParameterCollector.Collect(parameter.Type), sortedMap);
+ continue;
+ }
+
+ var local = symbol as ILocalSymbol;
+ if (local != null)
+ {
+ AddTypeParametersToMap(TypeParameterCollector.Collect(local.Type), sortedMap);
+ continue;
+ }
+
+ var rangeVariable = symbol as IRangeVariableSymbol;
+ if (rangeVariable != null)
+ {
+ var type = GetRangeVariableType(model, rangeVariable);
+ AddTypeParametersToMap(TypeParameterCollector.Collect(type), sortedMap);
+ continue;
+ }
+
+ //Contract.Fail(FeaturesResources.UnknownSymbolKind);
+ }
+ }
+
+ private void AppendMethodTypeParameterFromConstraint(SortedDictionary<int, ITypeParameterSymbol> sortedMap)
+ {
+ var typeParametersInConstraint = new List<ITypeParameterSymbol>();
+
+ // collect all type parameter appears in constraint
+ foreach (var typeParameter in sortedMap.Values)
+ {
+ var constraintTypes = typeParameter.ConstraintTypes;
+ if (constraintTypes.IsDefaultOrEmpty)
+ {
+ continue;
+ }
+
+ foreach (var type in constraintTypes)
+ {
+ // constraint itself is type parameter
+ typeParametersInConstraint.AddRange(TypeParameterCollector.Collect(type));
+ }
+ }
+
+ // pick up only valid type parameter and add them to the map
+ foreach (var typeParameter in typeParametersInConstraint)
+ {
+ AddTypeParameterToMap(typeParameter, sortedMap);
+ }
+ }
+
+ private void AppendMethodTypeParameterUsedDirectly(IDictionary<ISymbol, List<SyntaxToken>> symbolMap, IDictionary<int, ITypeParameterSymbol> sortedMap)
+ {
+ foreach (var pair in symbolMap.Where(p => p.Key.Kind == SymbolKind.TypeParameter))
+ {
+ var typeParameter = pair.Key as ITypeParameterSymbol;
+ if (typeParameter.DeclaringMethod == null ||
+ sortedMap.ContainsKey(typeParameter.Ordinal))
+ {
+ continue;
+ }
+
+ sortedMap[typeParameter.Ordinal] = typeParameter;
+ }
+ }
+
+ private IEnumerable<ITypeParameterSymbol> GetMethodTypeParametersInConstraintList(
+ SemanticModel model,
+ IDictionary<ISymbol, VariableInfo> variableInfoMap,
+ IDictionary<ISymbol, List<SyntaxToken>> symbolMap,
+ SortedDictionary<int, ITypeParameterSymbol> sortedMap)
+ {
+ // find starting points
+ AppendMethodTypeVariableFromDataFlowAnalysis(model, variableInfoMap, sortedMap);
+ AppendMethodTypeParameterUsedDirectly(symbolMap, sortedMap);
+
+ // recursively dive into constraints to find all constraints needed
+ AppendTypeParametersInConstraintsUsedByConstructedTypeWithItsOwnConstraints(sortedMap);
+
+ return sortedMap.Values.ToList();
+ }
+
+ private void AppendTypeParametersInConstraintsUsedByConstructedTypeWithItsOwnConstraints(SortedDictionary<int, ITypeParameterSymbol> sortedMap)
+ {
+ var visited = new HashSet<ITypeSymbol>();
+ var candidates = SpecializedCollections.EmptyEnumerable<ITypeParameterSymbol>();
+
+ // collect all type parameter appears in constraint
+ foreach (var typeParameter in sortedMap.Values)
+ {
+ var constraintTypes = typeParameter.ConstraintTypes;
+ if (constraintTypes.IsDefaultOrEmpty)
+ {
+ continue;
+ }
+
+ foreach (var type in constraintTypes)
+ {
+ candidates = candidates.Concat(AppendTypeParametersInConstraintsUsedByConstructedTypeWithItsOwnConstraints(type, visited));
+ }
+ }
+
+ // pick up only valid type parameter and add them to the map
+ foreach (var typeParameter in candidates)
+ {
+ AddTypeParameterToMap(typeParameter, sortedMap);
+ }
+ }
+
+ private IEnumerable<ITypeParameterSymbol> AppendTypeParametersInConstraintsUsedByConstructedTypeWithItsOwnConstraints(
+ ITypeSymbol type, HashSet<ITypeSymbol> visited)
+ {
+ if (visited.Contains(type))
+ {
+ return SpecializedCollections.EmptyEnumerable<ITypeParameterSymbol>();
+ }
+
+ visited.Add(type);
+
+ if (type.OriginalDefinition.Equals(type))
+ {
+ return SpecializedCollections.EmptyEnumerable<ITypeParameterSymbol>();
+ }
+
+ var constructedType = type as INamedTypeSymbol;
+ if (constructedType == null)
+ {
+ return SpecializedCollections.EmptyEnumerable<ITypeParameterSymbol>();
+ }
+
+ var parameters = constructedType.GetAllTypeParameters().ToList();
+ var arguments = constructedType.GetAllTypeArguments().ToList();
+
+ //Contract.ThrowIfFalse(parameters.Count == arguments.Count);
+
+ var typeParameters = new List<ITypeParameterSymbol>();
+ for (int i = 0; i < parameters.Count; i++)
+ {
+ var parameter = parameters[i];
+
+ var argument = arguments[i] as ITypeParameterSymbol;
+ if (argument != null)
+ {
+ // no constraint, nothing to do
+ if (!parameter.HasConstructorConstraint &&
+ !parameter.HasReferenceTypeConstraint &&
+ !parameter.HasValueTypeConstraint &&
+ parameter.ConstraintTypes.IsDefaultOrEmpty)
+ {
+ continue;
+ }
+
+ typeParameters.Add(argument);
+ continue;
+ }
+
+ var candidate = arguments[i] as INamedTypeSymbol;
+ if (candidate == null)
+ {
+ continue;
+ }
+
+ typeParameters.AddRange(AppendTypeParametersInConstraintsUsedByConstructedTypeWithItsOwnConstraints(candidate, visited));
+ }
+
+ return typeParameters;
+ }
+
+ private IEnumerable<ITypeParameterSymbol> GetMethodTypeParametersInDeclaration(ITypeSymbol returnType, SortedDictionary<int, ITypeParameterSymbol> sortedMap)
+ {
+ // add return type to the map
+ AddTypeParametersToMap(TypeParameterCollector.Collect(returnType), sortedMap);
+
+ AppendMethodTypeParameterFromConstraint(sortedMap);
+
+ return sortedMap.Values.ToList();
+ }
+
+ private OperationStatus CheckReadOnlyFields(SemanticModel semanticModel, Dictionary<ISymbol, List<SyntaxToken>> symbolMap)
+ {
+ if (ReadOnlyFieldAllowed())
+ {
+ return OperationStatus.Succeeded;
+ }
+
+ List<string> names = null;
+
+ foreach (var pair in symbolMap.Where(p => p.Key.Kind == SymbolKind.Field))
+ {
+ var field = (IFieldSymbol)pair.Key;
+ if (!field.IsReadOnly)
+ {
+ continue;
+ }
+
+ var tokens = pair.Value;
+ if (tokens.All(t => !((ExpressionSyntax)t.Parent).IsWrittenTo()))
+ {
+ continue;
+ }
+
+ names = names ?? new List<string>();
+ names.Add(field.Name ?? string.Empty);
+ }
+
+ if (names != null)
+ {
+ return new OperationStatus(OperationStatusFlag.BestEffort, string.Format("FeaturesResources.AssingingToReadonlyFields", string.Join(", ", names)));
+ }
+
+ return OperationStatus.Succeeded;
+ }
+
+ private bool IsInstanceMemberUsedInSelectedCode(DataFlowAnalysis dataFlowAnalysisData)
+ {
+ //Contract.ThrowIfNull(dataFlowAnalysisData);
+
+ // "this" can be used as a lvalue in a struct, check WrittenInside as well
+ return dataFlowAnalysisData.ReadInside.Any(s => IsThisParameter(s)) ||
+ dataFlowAnalysisData.WrittenInside.Any(s => IsThisParameter(s));
+ }
+
+ protected VariableInfo CreateFromSymbolCommon<T>(
+ Compilation compilation,
+ ISymbol symbol,
+ ITypeSymbol type,
+ VariableStyle style,
+ HashSet<int> nonNoisySyntaxKindSet) where T : SyntaxNode
+ {
+ var local = symbol as ILocalSymbol;
+ if (local != null)
+ {
+ return new VariableInfo(
+ new LocalVariableSymbol<T>(compilation, local, type, nonNoisySyntaxKindSet),
+ style);
+ }
+
+ var parameter = symbol as IParameterSymbol;
+ if (parameter != null)
+ {
+ return new VariableInfo(new ParameterVariableSymbol(compilation, parameter, type), style);
+ }
+
+ var rangeVariable = symbol as IRangeVariableSymbol;
+ if (rangeVariable != null)
+ {
+ return new VariableInfo(new QueryVariableSymbol(compilation, rangeVariable, type), style);
+ }
+
+ return null;//Contract.FailWithReturn<VariableInfo>(FeaturesResources.Unknown);
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.AnalyzerResult.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.AnalyzerResult.cs
new file mode 100644
index 0000000000..12cee3b59a
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.AnalyzerResult.cs
@@ -0,0 +1,176 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Threading;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public abstract partial class MethodExtractor
+ {
+ protected class AnalyzerResult
+ {
+ private readonly IList<ITypeParameterSymbol> _typeParametersInDeclaration;
+ private readonly IList<ITypeParameterSymbol> _typeParametersInConstraintList;
+ private readonly IList<VariableInfo> _variables;
+ private readonly VariableInfo _variableToUseAsReturnValue;
+
+ public AnalyzerResult(
+ SemanticDocument document,
+ IEnumerable<ITypeParameterSymbol> typeParametersInDeclaration,
+ IEnumerable<ITypeParameterSymbol> typeParametersInConstraintList,
+ IList<VariableInfo> variables,
+ VariableInfo variableToUseAsReturnValue,
+ ITypeSymbol returnType,
+ bool awaitTaskReturn,
+ bool instanceMemberIsUsed,
+ bool endOfSelectionReachable,
+ OperationStatus status)
+ {
+ var semanticModel = document.SemanticModel;
+
+ this.UseInstanceMember = instanceMemberIsUsed;
+ this.EndOfSelectionReachable = endOfSelectionReachable;
+ this.AwaitTaskReturn = awaitTaskReturn;
+ this.SemanticDocument = document;
+ _typeParametersInDeclaration = typeParametersInDeclaration.Select(s => semanticModel.ResolveType(s)).ToList();
+ _typeParametersInConstraintList = typeParametersInConstraintList.Select(s => semanticModel.ResolveType(s)).ToList();
+ _variables = variables;
+ this.ReturnType = semanticModel.ResolveType(returnType);
+ _variableToUseAsReturnValue = variableToUseAsReturnValue;
+ this.Status = status;
+ }
+
+ public AnalyzerResult With(SemanticDocument document)
+ {
+ if (this.SemanticDocument == document)
+ {
+ return this;
+ }
+
+ return new AnalyzerResult(
+ document,
+ _typeParametersInDeclaration,
+ _typeParametersInConstraintList,
+ _variables,
+ _variableToUseAsReturnValue,
+ this.ReturnType,
+ this.AwaitTaskReturn,
+ this.UseInstanceMember,
+ this.EndOfSelectionReachable,
+ this.Status);
+ }
+
+ /// <summary>
+ /// used to determine whether static can be used
+ /// </summary>
+ public bool UseInstanceMember { get; }
+
+ /// <summary>
+ /// used to determine whether "return" statement needs to be inserted
+ /// </summary>
+ public bool EndOfSelectionReachable { get; }
+
+ /// <summary>
+ /// document this result is based on
+ /// </summary>
+ public SemanticDocument SemanticDocument { get; }
+
+ /// <summary>
+ /// flag to show whether task return type is due to await
+ /// </summary>
+ public bool AwaitTaskReturn { get; }
+
+ /// <summary>
+ /// return type
+ /// </summary>
+ public ITypeSymbol ReturnType { get; }
+
+ /// <summary>
+ /// analyzer result operation status
+ /// </summary>
+ public OperationStatus Status { get; }
+
+ public ReadOnlyCollection<ITypeParameterSymbol> MethodTypeParametersInDeclaration
+ {
+ get
+ {
+ return new ReadOnlyCollection<ITypeParameterSymbol>(_typeParametersInDeclaration);
+ }
+ }
+
+ public ReadOnlyCollection<ITypeParameterSymbol> MethodTypeParametersInConstraintList
+ {
+ get
+ {
+ return new ReadOnlyCollection<ITypeParameterSymbol>(_typeParametersInConstraintList);
+ }
+ }
+
+ public bool HasVariableToUseAsReturnValue
+ {
+ get
+ {
+ return _variableToUseAsReturnValue != null;
+ }
+ }
+
+ public VariableInfo VariableToUseAsReturnValue
+ {
+ get
+ {
+ //Contract.ThrowIfNull(_variableToUseAsReturnValue);
+ return _variableToUseAsReturnValue;
+ }
+ }
+
+ public bool HasReturnType
+ {
+ get
+ {
+ return this.ReturnType.SpecialType != SpecialType.System_Void && !this.AwaitTaskReturn;
+ }
+ }
+
+ public IEnumerable<VariableInfo> MethodParameters
+ {
+ get
+ {
+ return _variables.Where(v => v.UseAsParameter);
+ }
+ }
+
+ public IEnumerable<VariableInfo> GetVariablesToSplitOrMoveIntoMethodDefinition(CancellationToken cancellationToken)
+ {
+ return _variables
+ .Where(v => v.GetDeclarationBehavior(cancellationToken) == DeclarationBehavior.SplitIn ||
+ v.GetDeclarationBehavior(cancellationToken) == DeclarationBehavior.MoveIn);
+ }
+
+ public IEnumerable<VariableInfo> GetVariablesToMoveIntoMethodDefinition(CancellationToken cancellationToken)
+ {
+ return _variables.Where(v => v.GetDeclarationBehavior(cancellationToken) == DeclarationBehavior.MoveIn);
+ }
+
+ public IEnumerable<VariableInfo> GetVariablesToMoveOutToCallSite(CancellationToken cancellationToken)
+ {
+ return _variables.Where(v => v.GetDeclarationBehavior(cancellationToken) == DeclarationBehavior.MoveOut);
+ }
+
+ public IEnumerable<VariableInfo> GetVariablesToMoveOutToCallSiteOrDelete(CancellationToken cancellationToken)
+ {
+ return _variables.Where(v => v.GetDeclarationBehavior(cancellationToken) == DeclarationBehavior.MoveOut ||
+ v.GetDeclarationBehavior(cancellationToken) == DeclarationBehavior.Delete);
+ }
+
+ public IEnumerable<VariableInfo> GetVariablesToSplitOrMoveOutToCallSite(CancellationToken cancellationToken)
+ {
+ return _variables.Where(v => v.GetDeclarationBehavior(cancellationToken) == DeclarationBehavior.SplitOut ||
+ v.GetDeclarationBehavior(cancellationToken) == DeclarationBehavior.MoveOut);
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.CodeGenerator.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.CodeGenerator.cs
new file mode 100644
index 0000000000..87d59c70a8
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.CodeGenerator.cs
@@ -0,0 +1,316 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. 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.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeGeneration;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Simplification;
+using Roslyn.Utilities;
+using ICSharpCode.NRefactory6.CSharp.CodeGeneration;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public abstract partial class MethodExtractor
+ {
+ protected abstract partial class CodeGenerator<TStatement, TExpression, TNodeUnderContainer>
+ where TStatement : SyntaxNode
+ where TExpression : SyntaxNode
+ where TNodeUnderContainer : SyntaxNode
+ {
+ protected readonly SyntaxAnnotation MethodNameAnnotation;
+ protected readonly SyntaxAnnotation MethodDefinitionAnnotation;
+ protected readonly SyntaxAnnotation CallSiteAnnotation;
+
+ protected readonly InsertionPoint InsertionPoint;
+ protected readonly SemanticDocument SemanticDocument;
+ protected readonly SelectionResult SelectionResult;
+ protected readonly AnalyzerResult AnalyzerResult;
+
+ protected CodeGenerator(InsertionPoint insertionPoint, SelectionResult selectionResult, AnalyzerResult analyzerResult)
+ {
+ //Contract.ThrowIfFalse(insertionPoint.SemanticDocument == analyzerResult.SemanticDocument);
+
+ this.InsertionPoint = insertionPoint;
+ this.SemanticDocument = insertionPoint.SemanticDocument;
+
+ this.SelectionResult = selectionResult;
+ this.AnalyzerResult = analyzerResult;
+
+ this.MethodNameAnnotation = new SyntaxAnnotation();
+ this.CallSiteAnnotation = new SyntaxAnnotation();
+ this.MethodDefinitionAnnotation = new SyntaxAnnotation();
+ }
+
+ #region method to be implemented in sub classes
+
+ protected abstract SyntaxNode GetOutermostCallSiteContainerToProcess(CancellationToken cancellationToken);
+ protected abstract Task<SyntaxNode> GenerateBodyForCallSiteContainerAsync(CancellationToken cancellationToken);
+ protected abstract SyntaxNode GetPreviousMember(SemanticDocument document);
+ protected abstract OperationStatus<IMethodSymbol> GenerateMethodDefinition(CancellationToken cancellationToken);
+
+ protected abstract SyntaxToken CreateIdentifier(string name);
+ protected abstract SyntaxToken CreateMethodName();
+ protected abstract bool LastStatementOrHasReturnStatementInReturnableConstruct();
+
+ protected abstract TNodeUnderContainer GetFirstStatementOrInitializerSelectedAtCallSite();
+ protected abstract TNodeUnderContainer GetLastStatementOrInitializerSelectedAtCallSite();
+ protected abstract Task<TNodeUnderContainer> GetStatementOrInitializerContainingInvocationToExtractedMethodAsync(SyntaxAnnotation callsiteAnnotation, CancellationToken cancellationToken);
+
+ protected abstract TExpression CreateCallSignature();
+ protected abstract TStatement CreateDeclarationStatement(VariableInfo variable, CancellationToken cancellationToken, TExpression initialValue = null);
+ protected abstract TStatement CreateAssignmentExpressionStatement(SyntaxToken identifier, TExpression rvalue);
+ protected abstract TStatement CreateReturnStatement(string identifierName = null);
+
+ protected abstract IEnumerable<TStatement> GetInitialStatementsForMethodDefinitions();
+ #endregion
+
+ public async Task<GeneratedCode> GenerateAsync(CancellationToken cancellationToken)
+ {
+ var root = this.SemanticDocument.Root;
+
+ // should I check venus hidden position check here as well?
+ root = root.ReplaceNode(this.GetOutermostCallSiteContainerToProcess(cancellationToken), await this.GenerateBodyForCallSiteContainerAsync(cancellationToken).ConfigureAwait(false));
+ var callSiteDocument = await this.SemanticDocument.WithSyntaxRootAsync(root, cancellationToken).ConfigureAwait(false);
+
+ var newCallSiteRoot = callSiteDocument.Root;
+ var previousMemberNode = GetPreviousMember(callSiteDocument);
+
+ // it is possible in a script file case where there is no previous member. in that case, insert new text into top level script
+ var destination = (previousMemberNode.Parent == null) ? previousMemberNode : previousMemberNode.Parent;
+
+ var codeGenerationService = new CSharpCodeGenerationService (this.SemanticDocument.Document.Project.Solution.Workspace.Services.GetLanguageServices (LanguageNames.CSharp));
+ var result = this.GenerateMethodDefinition(cancellationToken);
+ var newContainer = codeGenerationService.AddMethod(
+ destination, result.Data,
+ new CodeGenerationOptions(afterThisLocation: previousMemberNode.GetLocation(), generateDefaultAccessibility: false, generateMethodBodies: true),
+ cancellationToken);
+
+ var newDocument = callSiteDocument.Document.WithSyntaxRoot(newCallSiteRoot.ReplaceNode(destination, newContainer));
+ newDocument = await Simplifier.ReduceAsync(newDocument, Simplifier.Annotation, null, cancellationToken).ConfigureAwait(false);
+
+ var finalDocument = await SemanticDocument.CreateAsync(newDocument, cancellationToken).ConfigureAwait(false);
+ var finalRoot = finalDocument.Root;
+
+ var methodDefinition = finalRoot.GetAnnotatedNodesAndTokens(this.MethodDefinitionAnnotation).FirstOrDefault();
+ if (!methodDefinition.IsNode || methodDefinition.AsNode() == null)
+ {
+ return await CreateGeneratedCodeAsync(
+ result.Status.With(OperationStatus.FailedWithUnknownReason), finalDocument, cancellationToken).ConfigureAwait(false);
+ }
+
+ if (methodDefinition.SyntaxTree.IsHiddenPosition(methodDefinition.AsNode().SpanStart, cancellationToken) ||
+ methodDefinition.SyntaxTree.IsHiddenPosition(methodDefinition.AsNode().Span.End, cancellationToken))
+ {
+ return await CreateGeneratedCodeAsync(
+ result.Status.With(OperationStatus.OverlapsHiddenPosition), finalDocument, cancellationToken).ConfigureAwait(false);
+ }
+
+ return await CreateGeneratedCodeAsync(result.Status, finalDocument, cancellationToken).ConfigureAwait(false);
+ }
+
+ protected virtual Task<GeneratedCode> CreateGeneratedCodeAsync(OperationStatus status, SemanticDocument newDocument, CancellationToken cancellationToken)
+ {
+ return Task.FromResult(new GeneratedCode(
+ status,
+ newDocument,
+ this.MethodNameAnnotation,
+ this.CallSiteAnnotation,
+ this.MethodDefinitionAnnotation));
+ }
+
+ protected VariableInfo GetOutermostVariableToMoveIntoMethodDefinition(CancellationToken cancellationToken)
+ {
+ var variables = new List<VariableInfo>(this.AnalyzerResult.GetVariablesToMoveIntoMethodDefinition(cancellationToken));
+ if (variables.Count <= 0)
+ {
+ return null;
+ }
+
+ variables.Sort(VariableInfo.Compare);
+ return variables[0];
+ }
+
+ protected IEnumerable<TStatement> AddReturnIfUnreachable(
+ IEnumerable<TStatement> statements, CancellationToken cancellationToken)
+ {
+ if (this.AnalyzerResult.EndOfSelectionReachable)
+ {
+ return statements;
+ }
+
+ var type = this.SelectionResult.GetContainingScopeType();
+ if (type != null && type.SpecialType != SpecialType.System_Void)
+ {
+ return statements;
+ }
+
+ // no return type + end of selection not reachable
+ if (LastStatementOrHasReturnStatementInReturnableConstruct())
+ {
+ return statements;
+ }
+
+ return statements.Concat(CreateReturnStatement());
+ }
+
+ protected async Task<IEnumerable<TStatement>> AddInvocationAtCallSiteAsync(
+ IEnumerable<TStatement> statements, CancellationToken cancellationToken)
+ {
+ if (this.AnalyzerResult.HasVariableToUseAsReturnValue)
+ {
+ return statements;
+ }
+
+ //Contract.ThrowIfTrue(this.AnalyzerResult.GetVariablesToSplitOrMoveOutToCallSite(cancellationToken).Any(v => v.UseAsReturnValue));
+
+ // add invocation expression
+ return statements.Concat(
+ (TStatement)(SyntaxNode)await GetStatementOrInitializerContainingInvocationToExtractedMethodAsync(this.CallSiteAnnotation, cancellationToken).ConfigureAwait(false));
+ }
+
+ protected IEnumerable<TStatement> AddAssignmentStatementToCallSite(
+ IEnumerable<TStatement> statements,
+ CancellationToken cancellationToken)
+ {
+ if (!this.AnalyzerResult.HasVariableToUseAsReturnValue)
+ {
+ return statements;
+ }
+
+ var variable = this.AnalyzerResult.VariableToUseAsReturnValue;
+ if (variable.ReturnBehavior == ReturnBehavior.Initialization)
+ {
+ // there must be one decl behavior when there is "return value and initialize" variable
+ //Contract.ThrowIfFalse(this.AnalyzerResult.GetVariablesToSplitOrMoveOutToCallSite(cancellationToken).Single(v => v.ReturnBehavior == ReturnBehavior.Initialization) != null);
+
+ return statements.Concat(
+ CreateDeclarationStatement(variable, cancellationToken, CreateCallSignature()).WithAdditionalAnnotations(this.CallSiteAnnotation));
+ }
+
+ //Contract.ThrowIfFalse(variable.ReturnBehavior == ReturnBehavior.Assignment);
+ return statements.Concat(
+ CreateAssignmentExpressionStatement(CreateIdentifier(variable.Name), CreateCallSignature()).WithAdditionalAnnotations(this.CallSiteAnnotation));
+ }
+
+ protected IEnumerable<TStatement> CreateDeclarationStatements(IEnumerable<VariableInfo> variables, CancellationToken cancellationToken)
+ {
+ var list = new List<TStatement>();
+
+ foreach (var variable in variables)
+ {
+ list.Add(CreateDeclarationStatement(variable, cancellationToken));
+ }
+
+ return list;
+ }
+
+ protected IEnumerable<TStatement> AddSplitOrMoveDeclarationOutStatementsToCallSite(IEnumerable<TStatement> statements, CancellationToken cancellationToken)
+ {
+ var list = new List<TStatement>();
+
+ foreach (var variable in this.AnalyzerResult.GetVariablesToSplitOrMoveOutToCallSite(cancellationToken))
+ {
+ if (variable.UseAsReturnValue)
+ {
+ continue;
+ }
+
+ list.Add(CreateDeclarationStatement(variable, cancellationToken));
+ }
+
+ return list;
+ }
+
+ protected IEnumerable<TStatement> AppendReturnStatementIfNeeded(IEnumerable<TStatement> statements)
+ {
+ if (!this.AnalyzerResult.HasVariableToUseAsReturnValue)
+ {
+ return statements;
+ }
+
+ var variableToUseAsReturnValue = this.AnalyzerResult.VariableToUseAsReturnValue;
+
+ //Contract.ThrowIfFalse(variableToUseAsReturnValue.ReturnBehavior == ReturnBehavior.Assignment ||
+ // variableToUseAsReturnValue.ReturnBehavior == ReturnBehavior.Initialization);
+
+ return statements.Concat(CreateReturnStatement(this.AnalyzerResult.VariableToUseAsReturnValue.Name));
+ }
+
+ protected HashSet<SyntaxAnnotation> CreateVariableDeclarationToRemoveMap(
+ IEnumerable<VariableInfo> variables, CancellationToken cancellationToken)
+ {
+ var annotations = new List<Tuple<SyntaxToken, SyntaxAnnotation>>();
+
+ foreach (var variable in variables)
+ {
+// Contract.ThrowIfFalse(variable.GetDeclarationBehavior(cancellationToken) == DeclarationBehavior.MoveOut ||
+// variable.GetDeclarationBehavior(cancellationToken) == DeclarationBehavior.MoveIn ||
+// variable.GetDeclarationBehavior(cancellationToken) == DeclarationBehavior.Delete);
+
+ variable.AddIdentifierTokenAnnotationPair(annotations, cancellationToken);
+ }
+
+ return new HashSet<SyntaxAnnotation>(annotations.Select(t => t.Item2));
+ }
+
+ protected IList<ITypeParameterSymbol> CreateMethodTypeParameters(CancellationToken cancellationToken)
+ {
+ if (this.AnalyzerResult.MethodTypeParametersInDeclaration.Count == 0)
+ {
+ return SpecializedCollections.EmptyList<ITypeParameterSymbol>();
+ }
+
+ var set = new HashSet<ITypeParameterSymbol>(this.AnalyzerResult.MethodTypeParametersInConstraintList);
+
+ var typeParameters = new List<ITypeParameterSymbol>();
+ foreach (var parameter in this.AnalyzerResult.MethodTypeParametersInDeclaration)
+ {
+ if (parameter != null && set.Contains(parameter))
+ {
+ typeParameters.Add(parameter);
+ continue;
+ }
+
+ typeParameters.Add(CodeGenerationSymbolFactory.CreateTypeParameter(
+ parameter.GetAttributes(), parameter.Variance, parameter.Name, ImmutableArray.Create<ITypeSymbol>(),
+ parameter.HasConstructorConstraint, parameter.HasReferenceTypeConstraint, parameter.HasValueTypeConstraint, parameter.Ordinal));
+ }
+
+ return typeParameters;
+ }
+
+ protected IList<IParameterSymbol> CreateMethodParameters()
+ {
+ var parameters = new List<IParameterSymbol>();
+
+ foreach (var parameter in this.AnalyzerResult.MethodParameters)
+ {
+ var refKind = GetRefKind(parameter.ParameterModifier);
+ var type = parameter.GetVariableType(this.SemanticDocument);
+
+ parameters.Add(
+ CodeGenerationSymbolFactory.CreateParameterSymbol(
+ attributes: SpecializedCollections.EmptyList<AttributeData>(),
+ refKind: refKind,
+ isParams: false,
+ type: type,
+ name: parameter.Name));
+ }
+
+ return parameters;
+ }
+
+ private static RefKind GetRefKind(ParameterBehavior parameterBehavior)
+ {
+ return parameterBehavior == ParameterBehavior.Ref ? RefKind.Ref :
+ parameterBehavior == ParameterBehavior.Out ? RefKind.Out : RefKind.None;
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.GeneratedCode.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.GeneratedCode.cs
new file mode 100644
index 0000000000..2948cb05e1
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.GeneratedCode.cs
@@ -0,0 +1,39 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public abstract partial class MethodExtractor
+ {
+ public class GeneratedCode
+ {
+ public GeneratedCode(
+ OperationStatus status,
+ SemanticDocument document,
+ SyntaxAnnotation methodNameAnnotation,
+ SyntaxAnnotation callsiteAnnotation,
+ SyntaxAnnotation methodDefinitionAnnotation)
+ {
+ //Contract.ThrowIfNull(document);
+ //Contract.ThrowIfNull(methodNameAnnotation);
+ //Contract.ThrowIfNull(callsiteAnnotation);
+ //Contract.ThrowIfNull(methodDefinitionAnnotation);
+
+ this.Status = status;
+ this.SemanticDocument = document;
+ this.MethodNameAnnotation = methodNameAnnotation;
+ this.CallSiteAnnotation = callsiteAnnotation;
+ this.MethodDefinitionAnnotation = methodDefinitionAnnotation;
+ }
+
+ public OperationStatus Status { get; }
+ public SemanticDocument SemanticDocument { get; }
+
+ public SyntaxAnnotation MethodNameAnnotation { get; }
+ public SyntaxAnnotation CallSiteAnnotation { get; }
+ public SyntaxAnnotation MethodDefinitionAnnotation { get; }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.TriviaResult.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.TriviaResult.cs
new file mode 100644
index 0000000000..6182d45d7c
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.TriviaResult.cs
@@ -0,0 +1,181 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public abstract partial class MethodExtractor
+ {
+ protected abstract class TriviaResult
+ {
+ private readonly int _endOfLineKind;
+ private readonly int _whitespaceKind;
+
+ private readonly ITriviaSavedResult _result;
+
+ public TriviaResult(SemanticDocument document, ITriviaSavedResult result, int endOfLineKind, int whitespaceKind)
+ {
+ this.SemanticDocument = document;
+
+ _result = result;
+ _endOfLineKind = endOfLineKind;
+ _whitespaceKind = whitespaceKind;
+ }
+
+ protected abstract AnnotationResolver GetAnnotationResolver(SyntaxNode callsite, SyntaxNode methodDefinition);
+ protected abstract TriviaResolver GetTriviaResolver(SyntaxNode methodDefinition);
+
+ public SemanticDocument SemanticDocument { get; }
+
+ public async Task<OperationStatus<SemanticDocument>> ApplyAsync(GeneratedCode generatedCode, CancellationToken cancellationToken)
+ {
+ var document = generatedCode.SemanticDocument;
+ var root = document.Root;
+
+ var callsiteAnnotation = generatedCode.CallSiteAnnotation;
+ var methodDefinitionAnnotation = generatedCode.MethodDefinitionAnnotation;
+
+ var callsite = root.GetAnnotatedNodesAndTokens(callsiteAnnotation).SingleOrDefault().AsNode();
+ var method = root.GetAnnotatedNodesAndTokens(methodDefinitionAnnotation).SingleOrDefault().AsNode();
+
+ var annotationResolver = GetAnnotationResolver(callsite, method);
+ var triviaResolver = GetTriviaResolver(method);
+ if (annotationResolver == null || triviaResolver == null)
+ {
+ // bug # 6644
+ // this could happen in malformed code. return as it was.
+ var status = new OperationStatus(OperationStatusFlag.None, "FeaturesResources.CantNotConstructFinalTree");
+ return status.With(document);
+ }
+
+ return OperationStatus.Succeeded.With(
+ await document.WithSyntaxRootAsync(_result.RestoreTrivia(root, annotationResolver, triviaResolver), cancellationToken).ConfigureAwait(false));
+ }
+
+ protected IEnumerable<SyntaxTrivia> FilterTriviaList(IEnumerable<SyntaxTrivia> list)
+ {
+ // has noisy token
+ if (list.Any(t => t.RawKind != _endOfLineKind && t.RawKind != _whitespaceKind))
+ {
+ return RemoveLeadingElasticBeforeEndOfLine(list);
+ }
+
+ // whitespace only
+ return MergeLineBreaks(list);
+ }
+
+ protected IEnumerable<SyntaxTrivia> RemoveBlankLines(IEnumerable<SyntaxTrivia> list)
+ {
+ // remove any blank line at the beginging
+ var currentLine = new List<SyntaxTrivia>();
+ var result = new List<SyntaxTrivia>();
+
+ var seenFirstEndOfLine = false;
+ int i = 0;
+
+ foreach (var trivia in list)
+ {
+ i++;
+
+ if (trivia.RawKind == _endOfLineKind)
+ {
+ if (seenFirstEndOfLine)
+ {
+ // empty line. remove it
+ if (currentLine.All(t => t.RawKind == _endOfLineKind || t.RawKind == _whitespaceKind))
+ {
+ continue;
+ }
+
+ // non empty line after the first end of line.
+ // return now
+ return result.Concat(currentLine).Concat(list.Skip(i - 1));
+ }
+ else
+ {
+ seenFirstEndOfLine = true;
+
+ result.AddRange(currentLine);
+ result.Add(trivia);
+ currentLine.Clear();
+
+ continue;
+ }
+ }
+
+ currentLine.Add(trivia);
+ }
+
+ return result.Concat(currentLine);
+ }
+
+ protected IEnumerable<SyntaxTrivia> RemoveLeadingElasticBeforeEndOfLine(IEnumerable<SyntaxTrivia> list)
+ {
+ var trivia = list.FirstOrDefault();
+ if (!trivia.IsElastic())
+ {
+ return list;
+ }
+
+ var listWithoutHead = list.Skip(1);
+ trivia = listWithoutHead.FirstOrDefault();
+ if (trivia.RawKind == _endOfLineKind)
+ {
+ return listWithoutHead;
+ }
+
+ if (trivia.IsElastic())
+ {
+ return RemoveLeadingElasticBeforeEndOfLine(listWithoutHead);
+ }
+
+ return list;
+ }
+
+ protected IEnumerable<SyntaxTrivia> MergeLineBreaks(IEnumerable<SyntaxTrivia> list)
+ {
+ // this will make sure that it doesn't have more than two subsequent end of line
+ // trivia without any noisy trivia
+ var stack = new Stack<SyntaxTrivia>();
+ int numberOfEndOfLinesWithoutAnyNoisyTrivia = 0;
+
+ foreach (var trivia in list)
+ {
+ if (trivia.IsElastic())
+ {
+ stack.Push(trivia);
+ continue;
+ }
+
+ if (trivia.RawKind == _endOfLineKind)
+ {
+ numberOfEndOfLinesWithoutAnyNoisyTrivia++;
+
+ if (numberOfEndOfLinesWithoutAnyNoisyTrivia > 2)
+ {
+ // get rid of any whitespace trivia from stack
+ var top = stack.Peek();
+ while (!top.IsElastic() && top.RawKind == _whitespaceKind)
+ {
+ stack.Pop();
+ top = stack.Peek();
+ }
+
+ continue;
+ }
+ }
+
+ stack.Push(trivia);
+ }
+
+ return stack.Reverse();
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.TypeParameterCollector.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.TypeParameterCollector.cs
new file mode 100644
index 0000000000..9b19db0f03
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.TypeParameterCollector.cs
@@ -0,0 +1,57 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public abstract partial class MethodExtractor
+ {
+ protected class TypeParameterCollector : SymbolVisitor
+ {
+ private readonly List<ITypeParameterSymbol> _typeParameters = new List<ITypeParameterSymbol>();
+
+ public static IEnumerable<ITypeParameterSymbol> Collect(ITypeSymbol typeSymbol)
+ {
+ var collector = new TypeParameterCollector();
+ typeSymbol.Accept(collector);
+
+ return collector._typeParameters;
+ }
+
+ public override void DefaultVisit(ISymbol node)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void VisitDynamicType(IDynamicTypeSymbol dynamicTypeSymbol)
+ {
+ }
+
+ public override void VisitArrayType(IArrayTypeSymbol arrayTypeSymbol)
+ {
+ arrayTypeSymbol.ElementType.Accept(this);
+ }
+
+ public override void VisitPointerType(IPointerTypeSymbol pointerTypeSymbol)
+ {
+ pointerTypeSymbol.PointedAtType.Accept(this);
+ }
+
+ public override void VisitNamedType(INamedTypeSymbol namedTypeSymbol)
+ {
+ foreach (var argument in namedTypeSymbol.GetAllTypeArguments())
+ {
+ argument.Accept(this);
+ }
+ }
+
+ public override void VisitTypeParameter(ITypeParameterSymbol typeParameterTypeSymbol)
+ {
+ _typeParameters.Add(typeParameterTypeSymbol);
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.VariableInfo.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.VariableInfo.cs
new file mode 100644
index 0000000000..d7a4b47b3f
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.VariableInfo.cs
@@ -0,0 +1,138 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public abstract partial class MethodExtractor
+ {
+ protected class VariableInfo
+ {
+ private readonly VariableSymbol _variableSymbol;
+ private readonly VariableStyle _variableStyle;
+ private readonly bool _useAsReturnValue;
+
+ public VariableInfo(
+ VariableSymbol variableSymbol,
+ VariableStyle variableStyle,
+ bool useAsReturnValue = false)
+ {
+ _variableSymbol = variableSymbol;
+ _variableStyle = variableStyle;
+ _useAsReturnValue = useAsReturnValue;
+ }
+
+ public bool UseAsReturnValue
+ {
+ get
+ {
+ //Contract.ThrowIfFalse(!_useAsReturnValue || _variableStyle.ReturnStyle.ReturnBehavior != ReturnBehavior.None);
+ return _useAsReturnValue;
+ }
+ }
+
+ public bool CanBeUsedAsReturnValue
+ {
+ get
+ {
+ return _variableStyle.ReturnStyle.ReturnBehavior != ReturnBehavior.None;
+ }
+ }
+
+ public bool UseAsParameter
+ {
+ get
+ {
+ return (!_useAsReturnValue && _variableStyle.ParameterStyle.ParameterBehavior != ParameterBehavior.None) ||
+ (_useAsReturnValue && _variableStyle.ReturnStyle.ParameterBehavior != ParameterBehavior.None);
+ }
+ }
+
+ public ParameterBehavior ParameterModifier
+ {
+ get
+ {
+ return _useAsReturnValue ? _variableStyle.ReturnStyle.ParameterBehavior : _variableStyle.ParameterStyle.ParameterBehavior;
+ }
+ }
+
+ public DeclarationBehavior GetDeclarationBehavior(CancellationToken cancellationToken)
+ {
+ if (_useAsReturnValue)
+ {
+ return _variableStyle.ReturnStyle.DeclarationBehavior;
+ }
+
+ if (_variableSymbol.GetUseSaferDeclarationBehavior(cancellationToken))
+ {
+ return _variableStyle.ParameterStyle.SaferDeclarationBehavior;
+ }
+
+ return _variableStyle.ParameterStyle.DeclarationBehavior;
+ }
+
+ public ReturnBehavior ReturnBehavior
+ {
+ get
+ {
+ if (_useAsReturnValue)
+ {
+ return _variableStyle.ReturnStyle.ReturnBehavior;
+ }
+
+ return ReturnBehavior.None;
+ }
+ }
+
+ public static VariableInfo CreateReturnValue(VariableInfo variable)
+ {
+ //Contract.ThrowIfNull(variable);
+ //Contract.ThrowIfFalse(variable.CanBeUsedAsReturnValue);
+ //Contract.ThrowIfFalse(variable.ParameterModifier == ParameterBehavior.Out || variable.ParameterModifier == ParameterBehavior.Ref);
+
+ return new VariableInfo(variable._variableSymbol, variable._variableStyle, useAsReturnValue: true);
+ }
+
+ public void AddIdentifierTokenAnnotationPair(
+ List<Tuple<SyntaxToken, SyntaxAnnotation>> annotations, CancellationToken cancellationToken)
+ {
+ _variableSymbol.AddIdentifierTokenAnnotationPair(annotations, cancellationToken);
+ }
+
+ public string Name
+ {
+ get { return _variableSymbol.Name; }
+ }
+
+ public bool OriginalTypeHadAnonymousTypeOrDelegate
+ {
+ get { return _variableSymbol.OriginalTypeHadAnonymousTypeOrDelegate; }
+ }
+
+ public ITypeSymbol GetVariableType(SemanticDocument document)
+ {
+ return document.SemanticModel.ResolveType(_variableSymbol.OriginalType);
+ }
+
+ public SyntaxToken GetIdentifierTokenAtDeclaration(SemanticDocument document)
+ {
+ return document.GetTokenWithAnnotaton(_variableSymbol.IdentifierTokenAnnotation);
+ }
+
+ public SyntaxToken GetIdentifierTokenAtDeclaration(SyntaxNode node)
+ {
+ return node.GetAnnotatedTokens(_variableSymbol.IdentifierTokenAnnotation).SingleOrDefault();
+ }
+
+ public static int Compare(VariableInfo left, VariableInfo right)
+ {
+ return VariableSymbol.Compare(left._variableSymbol, right._variableSymbol);
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.VariableSymbol.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.VariableSymbol.cs
new file mode 100644
index 0000000000..4dc5b590c1
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.VariableSymbol.cs
@@ -0,0 +1,357 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Roslyn.Utilities;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public abstract partial class MethodExtractor
+ {
+ /// <summary>
+ /// temporary symbol until we have a symbol that can hold onto both local and parameter symbol
+ /// </summary>
+ protected abstract class VariableSymbol
+ {
+ protected VariableSymbol(Compilation compilation, ITypeSymbol type)
+ {
+ this.OriginalTypeHadAnonymousTypeOrDelegate = type.ContainsAnonymousType();
+ this.OriginalType = this.OriginalTypeHadAnonymousTypeOrDelegate ? type.RemoveAnonymousTypes(compilation) : type;
+ }
+
+ public abstract int DisplayOrder { get; }
+ public abstract string Name { get; }
+
+ public abstract bool GetUseSaferDeclarationBehavior(CancellationToken cancellationToken);
+ public abstract SyntaxAnnotation IdentifierTokenAnnotation { get; }
+ public abstract SyntaxToken GetOriginalIdentifierToken(CancellationToken cancellationToken);
+
+ public abstract void AddIdentifierTokenAnnotationPair(
+ List<Tuple<SyntaxToken, SyntaxAnnotation>> annotations, CancellationToken cancellationToken);
+
+ protected abstract int CompareTo(VariableSymbol right);
+
+ /// <summary>
+ /// return true if original type had anonymous type or delegate somewhere in the type
+ /// </summary>
+ public bool OriginalTypeHadAnonymousTypeOrDelegate { get; }
+
+ /// <summary>
+ /// get the original type with anonymous type removed
+ /// </summary>
+ public ITypeSymbol OriginalType { get; }
+
+ public static int Compare(VariableSymbol left, VariableSymbol right)
+ {
+ if (left.DisplayOrder == right.DisplayOrder)
+ {
+ return left.CompareTo(right);
+ }
+
+ return left.DisplayOrder - right.DisplayOrder;
+ }
+ }
+
+ protected abstract class NotMovableVariableSymbol : VariableSymbol
+ {
+ public NotMovableVariableSymbol(Compilation compilation, ITypeSymbol type) :
+ base(compilation, type)
+ {
+ }
+
+ public override bool GetUseSaferDeclarationBehavior(CancellationToken cancellationToken)
+ {
+ // decl never get moved
+ return false;
+ }
+
+ [ExcludeFromCodeCoverage]
+ public override SyntaxToken GetOriginalIdentifierToken(CancellationToken cancellationToken)
+ {
+ throw new InvalidOperationException();
+ }
+
+ [ExcludeFromCodeCoverage]
+ public override SyntaxAnnotation IdentifierTokenAnnotation
+ {
+ get { throw new InvalidOperationException(); }
+ }
+
+ public override void AddIdentifierTokenAnnotationPair(
+ List<Tuple<SyntaxToken, SyntaxAnnotation>> annotations, CancellationToken cancellationToken)
+ {
+ // do nothing for parameter
+ }
+ }
+
+ protected class ParameterVariableSymbol : NotMovableVariableSymbol, IComparable<ParameterVariableSymbol>
+ {
+ private readonly IParameterSymbol _parameterSymbol;
+
+ public ParameterVariableSymbol(Compilation compilation, IParameterSymbol parameterSymbol, ITypeSymbol type) :
+ base(compilation, type)
+ {
+ //Contract.ThrowIfNull(parameterSymbol);
+ _parameterSymbol = parameterSymbol;
+ }
+
+ public override int DisplayOrder
+ {
+ get { return 0; }
+ }
+
+ protected override int CompareTo(VariableSymbol right)
+ {
+ return this.CompareTo((ParameterVariableSymbol)right);
+ }
+
+ public int CompareTo(ParameterVariableSymbol other)
+ {
+ //Contract.ThrowIfNull(other);
+
+ if (this == other)
+ {
+ return 0;
+ }
+
+ var compare = CompareTo((IMethodSymbol)_parameterSymbol.ContainingSymbol, (IMethodSymbol)other._parameterSymbol.ContainingSymbol);
+ if (compare != 0)
+ {
+ return compare;
+ }
+
+ // Contract.ThrowIfFalse(_parameterSymbol.Ordinal != other._parameterSymbol.Ordinal);
+ return (_parameterSymbol.Ordinal > other._parameterSymbol.Ordinal) ? 1 : -1;
+ }
+
+ private int CompareTo(IMethodSymbol left, IMethodSymbol right)
+ {
+ if (left == null && right == null)
+ {
+ return 0;
+ }
+
+ if (left.Equals(right))
+ {
+ return 0;
+ }
+
+ if (left.MethodKind == MethodKind.AnonymousFunction &&
+ right.MethodKind != MethodKind.AnonymousFunction)
+ {
+ return 1;
+ }
+
+ if (left.MethodKind != MethodKind.AnonymousFunction &&
+ right.MethodKind == MethodKind.AnonymousFunction)
+ {
+ return -1;
+ }
+
+ if (left.MethodKind == MethodKind.AnonymousFunction &&
+ right.MethodKind == MethodKind.AnonymousFunction)
+ {
+ //Contract.ThrowIfFalse(left.Locations.Length == 1);
+ //Contract.ThrowIfFalse(right.Locations.Length == 1);
+
+ return left.Locations[0].SourceSpan.Start - right.Locations[0].SourceSpan.Start;
+ }
+
+ return 0;//Contract.FailWithReturn<int>("Shouldn't reach here");
+ }
+
+ public override string Name
+ {
+ get
+ {
+ return _parameterSymbol.ToDisplayString(
+ new SymbolDisplayFormat(
+ parameterOptions: SymbolDisplayParameterOptions.IncludeName,
+ miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers));
+ }
+ }
+ }
+
+ protected class LocalVariableSymbol<T> : VariableSymbol, IComparable<LocalVariableSymbol<T>> where T : SyntaxNode
+ {
+ private readonly SyntaxAnnotation _annotation;
+ private readonly ILocalSymbol _localSymbol;
+ private readonly HashSet<int> _nonNoisySet;
+
+ public LocalVariableSymbol(Compilation compilation, ILocalSymbol localSymbol, ITypeSymbol type, HashSet<int> nonNoisySet) :
+ base(compilation, type)
+ {
+// Contract.ThrowIfNull(localSymbol);
+// Contract.ThrowIfNull(nonNoisySet);
+
+ _annotation = new SyntaxAnnotation();
+ _localSymbol = localSymbol;
+ _nonNoisySet = nonNoisySet;
+ }
+
+ public override int DisplayOrder
+ {
+ get { return 1; }
+ }
+
+ protected override int CompareTo(VariableSymbol right)
+ {
+ return this.CompareTo((LocalVariableSymbol<T>)right);
+ }
+
+ public int CompareTo(LocalVariableSymbol<T> other)
+ {
+ //Contract.ThrowIfNull(other);
+
+ if (this == other)
+ {
+ return 0;
+ }
+
+ //Contract.ThrowIfFalse(_localSymbol.Locations.Length == 1);
+ //Contract.ThrowIfFalse(other._localSymbol.Locations.Length == 1);
+ //Contract.ThrowIfFalse(_localSymbol.Locations[0].IsInSource);
+ //Contract.ThrowIfFalse(other._localSymbol.Locations[0].IsInSource);
+ //Contract.ThrowIfFalse(_localSymbol.Locations[0].SourceTree == other._localSymbol.Locations[0].SourceTree);
+ //Contract.ThrowIfFalse(_localSymbol.Locations[0].SourceSpan.Start != other._localSymbol.Locations[0].SourceSpan.Start);
+
+ return _localSymbol.Locations[0].SourceSpan.Start - other._localSymbol.Locations[0].SourceSpan.Start;
+ }
+
+ public override string Name
+ {
+ get
+ {
+ return _localSymbol.ToDisplayString(
+ new SymbolDisplayFormat(
+ miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers));
+ }
+ }
+
+ public override SyntaxToken GetOriginalIdentifierToken(CancellationToken cancellationToken)
+ {
+// Contract.ThrowIfFalse(_localSymbol.Locations.Length == 1);
+// Contract.ThrowIfFalse(_localSymbol.Locations[0].IsInSource);
+// Contract.ThrowIfNull(_localSymbol.Locations[0].SourceTree);
+
+ var tree = _localSymbol.Locations[0].SourceTree;
+ var span = _localSymbol.Locations[0].SourceSpan;
+
+ var token = tree.GetRoot(cancellationToken).FindToken(span.Start);
+ //Contract.ThrowIfFalse(token.Span.Equals(span));
+
+ return token;
+ }
+
+ public override SyntaxAnnotation IdentifierTokenAnnotation
+ {
+ get { return _annotation; }
+ }
+
+ public override void AddIdentifierTokenAnnotationPair(
+ List<Tuple<SyntaxToken, SyntaxAnnotation>> annotations, CancellationToken cancellationToken)
+ {
+ annotations.Add(Tuple.Create(this.GetOriginalIdentifierToken(cancellationToken), _annotation));
+ }
+
+ public override bool GetUseSaferDeclarationBehavior(CancellationToken cancellationToken)
+ {
+ var identifier = this.GetOriginalIdentifierToken(cancellationToken);
+
+ // check whether there is a noisy trivia around the token.
+ if (ContainsNoisyTrivia(identifier.LeadingTrivia))
+ {
+ return true;
+ }
+
+ if (ContainsNoisyTrivia(identifier.TrailingTrivia))
+ {
+ return true;
+ }
+
+ var declStatement = identifier.Parent.FirstAncestorOrSelf<T>((n) => true);
+ if (declStatement == null)
+ {
+ return true;
+ }
+
+ foreach (var token in declStatement.DescendantTokens())
+ {
+ if (ContainsNoisyTrivia(token.LeadingTrivia))
+ {
+ return true;
+ }
+
+ if (ContainsNoisyTrivia(token.TrailingTrivia))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private bool ContainsNoisyTrivia(SyntaxTriviaList list)
+ {
+ return list.Any(t => !_nonNoisySet.Contains(t.RawKind));
+ }
+ }
+
+ protected class QueryVariableSymbol : NotMovableVariableSymbol, IComparable<QueryVariableSymbol>
+ {
+ private readonly IRangeVariableSymbol _symbol;
+
+ public QueryVariableSymbol(Compilation compilation, IRangeVariableSymbol symbol, ITypeSymbol type) :
+ base(compilation, type)
+ {
+ //Contract.ThrowIfNull(symbol);
+ _symbol = symbol;
+ }
+
+ public override int DisplayOrder
+ {
+ get { return 2; }
+ }
+
+ protected override int CompareTo(VariableSymbol right)
+ {
+ return this.CompareTo((QueryVariableSymbol)right);
+ }
+
+ public int CompareTo(QueryVariableSymbol other)
+ {
+ //Contract.ThrowIfNull(other);
+
+ if (this == other)
+ {
+ return 0;
+ }
+
+ var locationLeft = _symbol.Locations.First();
+ var locationRight = other._symbol.Locations.First();
+
+// Contract.ThrowIfFalse(locationLeft.IsInSource);
+// Contract.ThrowIfFalse(locationRight.IsInSource);
+// Contract.ThrowIfFalse(locationLeft.SourceTree == locationRight.SourceTree);
+// Contract.ThrowIfFalse(locationLeft.SourceSpan.Start != locationRight.SourceSpan.Start);
+
+ return locationLeft.SourceSpan.Start - locationRight.SourceSpan.Start;
+ }
+
+ public override string Name
+ {
+ get
+ {
+ return _symbol.ToDisplayString(
+ new SymbolDisplayFormat(
+ miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers));
+ }
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.cs
new file mode 100644
index 0000000000..7bff9c7fd9
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/MethodExtractor.cs
@@ -0,0 +1,171 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.Formatting;
+using Microsoft.CodeAnalysis.Formatting.Rules;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public abstract partial class MethodExtractor
+ {
+ protected readonly SelectionResult OriginalSelectionResult;
+
+ public MethodExtractor(SelectionResult selectionResult)
+ {
+ //Contract.ThrowIfNull(selectionResult);
+ this.OriginalSelectionResult = selectionResult;
+ }
+
+ protected abstract Task<AnalyzerResult> AnalyzeAsync(SelectionResult selectionResult, CancellationToken cancellationToken);
+ protected abstract Task<InsertionPoint> GetInsertionPointAsync(SemanticDocument document, int position, CancellationToken cancellationToken);
+ protected abstract Task<TriviaResult> PreserveTriviaAsync(SelectionResult selectionResult, CancellationToken cancellationToken);
+ protected abstract Task<SemanticDocument> ExpandAsync(SelectionResult selection, CancellationToken cancellationToken);
+
+ protected abstract Task<GeneratedCode> GenerateCodeAsync(InsertionPoint insertionPoint, SelectionResult selectionResult, AnalyzerResult analyzeResult, CancellationToken cancellationToken);
+
+ protected abstract SyntaxToken GetMethodNameAtInvocation(IEnumerable<SyntaxNodeOrToken> methodNames);
+ // protected abstract IEnumerable<IFormattingRule> GetFormattingRules(Document document);
+
+ protected abstract Task<OperationStatus> CheckTypeAsync(Document document, SyntaxNode contextNode, Location location, ITypeSymbol type, CancellationToken cancellationToken);
+
+ public async Task<ExtractMethodResult> ExtractMethodAsync(CancellationToken cancellationToken)
+ {
+ var operationStatus = this.OriginalSelectionResult.Status;
+
+ var analyzeResult = await AnalyzeAsync(this.OriginalSelectionResult, cancellationToken).ConfigureAwait(false);
+ cancellationToken.ThrowIfCancellationRequested();
+
+ operationStatus = await CheckVariableTypesAsync(analyzeResult.Status.With(operationStatus), analyzeResult, cancellationToken).ConfigureAwait(false);
+ if (operationStatus.FailedWithNoBestEffortSuggestion())
+ {
+ return new FailedExtractMethodResult(operationStatus);
+ }
+
+ var insertionPoint = await GetInsertionPointAsync(analyzeResult.SemanticDocument, this.OriginalSelectionResult.OriginalSpan.Start, cancellationToken).ConfigureAwait(false);
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var triviaResult = await PreserveTriviaAsync(this.OriginalSelectionResult.With(insertionPoint.SemanticDocument), cancellationToken).ConfigureAwait(false);
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var expandedDocument = await ExpandAsync(this.OriginalSelectionResult.With(triviaResult.SemanticDocument), cancellationToken).ConfigureAwait(false);
+
+ var generatedCode = await GenerateCodeAsync(
+ insertionPoint.With(expandedDocument),
+ this.OriginalSelectionResult.With(expandedDocument),
+ analyzeResult.With(expandedDocument),
+ cancellationToken).ConfigureAwait(false);
+
+ var applied = await triviaResult.ApplyAsync(generatedCode, cancellationToken).ConfigureAwait(false);
+ var afterTriviaRestored = applied.With(operationStatus);
+ cancellationToken.ThrowIfCancellationRequested();
+
+ if (afterTriviaRestored.Status.FailedWithNoBestEffortSuggestion())
+ {
+ return CreateExtractMethodResult(
+ operationStatus, generatedCode.SemanticDocument, generatedCode.MethodNameAnnotation, generatedCode.MethodDefinitionAnnotation);
+ }
+
+ var finalDocument = afterTriviaRestored.Data.Document;
+ finalDocument = await Formatter.FormatAsync(finalDocument, Formatter.Annotation, options: null,/* rules: GetFormattingRules(finalDocument), */cancellationToken: cancellationToken).ConfigureAwait(false);
+
+ cancellationToken.ThrowIfCancellationRequested();
+ return CreateExtractMethodResult(
+ operationStatus.With(generatedCode.Status),
+ await SemanticDocument.CreateAsync(finalDocument, cancellationToken).ConfigureAwait(false),
+ generatedCode.MethodNameAnnotation,
+ generatedCode.MethodDefinitionAnnotation);
+ }
+
+ private ExtractMethodResult CreateExtractMethodResult(
+ OperationStatus status, SemanticDocument semanticDocument,
+ SyntaxAnnotation invocationAnnotation, SyntaxAnnotation methodAnnotation)
+ {
+ var newRoot = semanticDocument.Root;
+ var annotatedTokens = newRoot.GetAnnotatedNodesAndTokens(invocationAnnotation);
+ var methodDefinition = newRoot.GetAnnotatedNodesAndTokens(methodAnnotation).FirstOrDefault().AsNode();
+
+ return new SimpleExtractMethodResult(status, semanticDocument.Document, GetMethodNameAtInvocation(annotatedTokens), methodDefinition);
+ }
+
+ private async Task<OperationStatus> CheckVariableTypesAsync(
+ OperationStatus status,
+ AnalyzerResult analyzeResult,
+ CancellationToken cancellationToken)
+ {
+ var document = analyzeResult.SemanticDocument;
+
+ // sync selection result to same semantic data as analyzeResult
+ var firstToken = this.OriginalSelectionResult.With(document).GetFirstTokenInSelection();
+ var context = firstToken.Parent;
+
+ var result = await TryCheckVariableTypeAsync(document, context, analyzeResult.GetVariablesToMoveIntoMethodDefinition(cancellationToken), status, cancellationToken).ConfigureAwait(false);
+ if (!result.Item1)
+ {
+ result = await TryCheckVariableTypeAsync(document, context, analyzeResult.GetVariablesToSplitOrMoveIntoMethodDefinition(cancellationToken), result.Item2, cancellationToken).ConfigureAwait(false);
+ if (!result.Item1)
+ {
+ result = await TryCheckVariableTypeAsync(document, context, analyzeResult.MethodParameters, result.Item2, cancellationToken).ConfigureAwait(false);
+ if (!result.Item1)
+ {
+ result = await TryCheckVariableTypeAsync(document, context, analyzeResult.GetVariablesToMoveOutToCallSite(cancellationToken), result.Item2, cancellationToken).ConfigureAwait(false);
+ if (!result.Item1)
+ {
+ result = await TryCheckVariableTypeAsync(document, context, analyzeResult.GetVariablesToSplitOrMoveOutToCallSite(cancellationToken), result.Item2, cancellationToken).ConfigureAwait(false);
+ if (!result.Item1)
+ {
+ return result.Item2;
+ }
+ }
+ }
+ }
+ }
+
+ status = result.Item2;
+
+ var checkedStatus = await CheckTypeAsync(document.Document, context, context.GetLocation(), analyzeResult.ReturnType, cancellationToken).ConfigureAwait(false);
+ return checkedStatus.With(status);
+ }
+
+ private async Task<Tuple<bool, OperationStatus>> TryCheckVariableTypeAsync(
+ SemanticDocument document, SyntaxNode contextNode, IEnumerable<VariableInfo> variables,
+ OperationStatus status, CancellationToken cancellationToken)
+ {
+ if (status.FailedWithNoBestEffortSuggestion())
+ {
+ return Tuple.Create(false, status);
+ }
+
+ var location = contextNode.GetLocation();
+
+ foreach (var variable in variables)
+ {
+ var originalType = variable.GetVariableType(document);
+ var result = await CheckTypeAsync(document.Document, contextNode, location, originalType, cancellationToken).ConfigureAwait(false);
+ if (result.FailedWithNoBestEffortSuggestion())
+ {
+ status = status.With(result);
+ return Tuple.Create(false, status);
+ }
+ }
+
+ return Tuple.Create(true, status);
+ }
+
+ public static string MakeMethodName(string prefix, string originalName)
+ {
+ var startingWithLetter = originalName.SkipWhile(c => !char.IsLetter(c)).ToArray();
+ var name = startingWithLetter.Length == 0 ? originalName : new string(startingWithLetter);
+
+ return char.IsUpper(name[0]) ?
+ prefix + name :
+ prefix + char.ToUpper(name[0]).ToString() + name.Substring(1);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/OperationStatus.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/OperationStatus.cs
new file mode 100644
index 0000000000..1abdadffd7
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/OperationStatus.cs
@@ -0,0 +1,68 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using Roslyn.Utilities;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public partial class OperationStatus
+ {
+ public OperationStatus(OperationStatusFlag flag, string reason)
+ {
+ //Contract.ThrowIfTrue(flag.Succeeded() && flag.HasBestEffort());
+
+ this.Flag = flag;
+ this.Reasons = reason == null ? SpecializedCollections.EmptyEnumerable<string>() : SpecializedCollections.SingletonEnumerable(reason);
+ }
+
+ private OperationStatus(OperationStatusFlag flag, IEnumerable<string> reasons)
+ {
+ //Contract.ThrowIfNull(reasons);
+ //Contract.ThrowIfTrue(flag.Succeeded() && flag.HasBestEffort());
+
+ this.Flag = flag;
+ this.Reasons = reasons;
+ }
+
+ public OperationStatus With(OperationStatusFlag flag, string reason)
+ {
+ var newFlag = this.Flag | flag;
+
+ newFlag = (this.Failed() || flag.Failed()) ? newFlag.RemoveFlag(OperationStatusFlag.Succeeded) : newFlag;
+ newFlag = newFlag.Succeeded() ? newFlag.RemoveFlag(OperationStatusFlag.BestEffort) : newFlag;
+
+ var reasons = reason == null ? this.Reasons : this.Reasons.Concat(reason);
+ return new OperationStatus(newFlag, reasons);
+ }
+
+ public OperationStatus With(OperationStatus operationStatus)
+ {
+ var newFlag = this.Flag | operationStatus.Flag;
+
+ newFlag = (this.Failed() || operationStatus.Failed()) ? newFlag.RemoveFlag(OperationStatusFlag.Succeeded) : newFlag;
+ newFlag = newFlag.Succeeded() ? newFlag.RemoveFlag(OperationStatusFlag.BestEffort) : newFlag;
+
+ var reasons = this.Reasons.Concat(operationStatus.Reasons);
+ return new OperationStatus(newFlag, reasons);
+ }
+
+ public OperationStatus MakeFail()
+ {
+ return new OperationStatus(OperationStatusFlag.None, this.Reasons);
+ }
+
+ public OperationStatus MarkSuggestion()
+ {
+ return new OperationStatus(this.Flag | OperationStatusFlag.Suggestion, this.Reasons);
+ }
+
+ public OperationStatus<T> With<T>(T data)
+ {
+ return Create(this, data);
+ }
+
+ public OperationStatusFlag Flag { get; }
+ public IEnumerable<string> Reasons { get; }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/OperationStatus_Statics.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/OperationStatus_Statics.cs
new file mode 100644
index 0000000000..d15dc7388b
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/OperationStatus_Statics.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public partial class OperationStatus
+ {
+ public static readonly OperationStatus Succeeded = new OperationStatus(OperationStatusFlag.Succeeded, reason: null);
+ public static readonly OperationStatus FailedWithUnknownReason = new OperationStatus(OperationStatusFlag.None, reason: "FeaturesResources.ExtractMethodFailedWithUnknownReasons");
+ public static readonly OperationStatus OverlapsHiddenPosition = new OperationStatus(OperationStatusFlag.None, "FeaturesResources.GeneratedCodeIsOverlapping");
+
+ public static readonly OperationStatus NoActiveStatement = new OperationStatus(OperationStatusFlag.BestEffort, "FeaturesResources.NoActiveStatement");
+ public static readonly OperationStatus ErrorOrUnknownType = new OperationStatus(OperationStatusFlag.BestEffort, "FeaturesResources.ErrorOrUnknownType");
+ public static readonly OperationStatus UnsafeAddressTaken = new OperationStatus(OperationStatusFlag.BestEffort, "FeaturesResources.TheAddressOfAVariableIsUsed");
+
+ /// <summary>
+ /// create operation status with the given data
+ /// </summary>
+ public static OperationStatus<T> Create<T>(OperationStatus status, T data)
+ {
+ return new OperationStatus<T>(status, data);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/OperationStatus`1.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/OperationStatus`1.cs
new file mode 100644
index 0000000000..b339377cbe
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/OperationStatus`1.cs
@@ -0,0 +1,29 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ /// <summary>
+ /// operation status paired with data
+ /// </summary>
+ public class OperationStatus<T>
+ {
+ public OperationStatus(OperationStatus status, T data)
+ {
+ this.Status = status;
+ this.Data = data;
+ }
+
+ public OperationStatus Status { get; }
+ public T Data { get; }
+
+ public OperationStatus<T> With(OperationStatus status)
+ {
+ return new OperationStatus<T>(status, this.Data);
+ }
+
+ public OperationStatus<TNew> With<TNew>(TNew data)
+ {
+ return new OperationStatus<TNew>(this.Status, data);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ParameterStyle.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ParameterStyle.cs
new file mode 100644
index 0000000000..aed67dc170
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ParameterStyle.cs
@@ -0,0 +1,41 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public class ParameterStyle
+ {
+ public ParameterBehavior ParameterBehavior { get; private set; }
+ public DeclarationBehavior DeclarationBehavior { get; private set; }
+ public DeclarationBehavior SaferDeclarationBehavior { get; private set; }
+
+ public static readonly ParameterStyle None =
+ new ParameterStyle() { ParameterBehavior = ParameterBehavior.None, DeclarationBehavior = DeclarationBehavior.None, SaferDeclarationBehavior = DeclarationBehavior.None };
+
+ public static readonly ParameterStyle InputOnly =
+ new ParameterStyle() { ParameterBehavior = ParameterBehavior.Input, DeclarationBehavior = DeclarationBehavior.None, SaferDeclarationBehavior = DeclarationBehavior.None };
+
+ public static readonly ParameterStyle Delete =
+ new ParameterStyle() { ParameterBehavior = ParameterBehavior.None, DeclarationBehavior = DeclarationBehavior.Delete, SaferDeclarationBehavior = DeclarationBehavior.None };
+
+ public static readonly ParameterStyle MoveOut =
+ new ParameterStyle() { ParameterBehavior = ParameterBehavior.None, DeclarationBehavior = DeclarationBehavior.MoveOut, SaferDeclarationBehavior = DeclarationBehavior.SplitOut };
+
+ public static readonly ParameterStyle SplitOut =
+ new ParameterStyle() { ParameterBehavior = ParameterBehavior.None, DeclarationBehavior = DeclarationBehavior.SplitOut, SaferDeclarationBehavior = DeclarationBehavior.SplitOut };
+
+ public static readonly ParameterStyle MoveIn =
+ new ParameterStyle() { ParameterBehavior = ParameterBehavior.None, DeclarationBehavior = DeclarationBehavior.MoveIn, SaferDeclarationBehavior = DeclarationBehavior.SplitIn };
+
+ public static readonly ParameterStyle SplitIn =
+ new ParameterStyle() { ParameterBehavior = ParameterBehavior.None, DeclarationBehavior = DeclarationBehavior.SplitIn, SaferDeclarationBehavior = DeclarationBehavior.SplitIn };
+
+ public static readonly ParameterStyle Out =
+ new ParameterStyle() { ParameterBehavior = ParameterBehavior.Out, DeclarationBehavior = DeclarationBehavior.None, SaferDeclarationBehavior = DeclarationBehavior.None };
+
+ public static readonly ParameterStyle Ref =
+ new ParameterStyle() { ParameterBehavior = ParameterBehavior.Ref, DeclarationBehavior = DeclarationBehavior.None, SaferDeclarationBehavior = DeclarationBehavior.None };
+
+ public static readonly ParameterStyle OutWithMoveOut =
+ new ParameterStyle() { ParameterBehavior = ParameterBehavior.Out, DeclarationBehavior = DeclarationBehavior.MoveOut, SaferDeclarationBehavior = DeclarationBehavior.MoveOut };
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ReturnStyle.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ReturnStyle.cs
new file mode 100644
index 0000000000..d9b7c50fcc
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/ReturnStyle.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public class ReturnStyle
+ {
+ public ParameterBehavior ParameterBehavior { get; private set; }
+ public ReturnBehavior ReturnBehavior { get; private set; }
+ public DeclarationBehavior DeclarationBehavior { get; private set; }
+
+ public static readonly ReturnStyle None =
+ new ReturnStyle() { ParameterBehavior = ParameterBehavior.None, ReturnBehavior = ReturnBehavior.None, DeclarationBehavior = DeclarationBehavior.None };
+
+ public static readonly ReturnStyle AssignmentWithInput =
+ new ReturnStyle() { ParameterBehavior = ParameterBehavior.Input, ReturnBehavior = ReturnBehavior.Assignment, DeclarationBehavior = DeclarationBehavior.None };
+
+ public static readonly ReturnStyle AssignmentWithNoInput =
+ new ReturnStyle() { ParameterBehavior = ParameterBehavior.None, ReturnBehavior = ReturnBehavior.Assignment, DeclarationBehavior = DeclarationBehavior.SplitIn };
+
+ public static readonly ReturnStyle Initialization =
+ new ReturnStyle() { ParameterBehavior = ParameterBehavior.None, ReturnBehavior = ReturnBehavior.Initialization, DeclarationBehavior = DeclarationBehavior.SplitOut };
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/SelectionResult.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/SelectionResult.cs
new file mode 100644
index 0000000000..49ca208b8d
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/SelectionResult.cs
@@ -0,0 +1,158 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.Options;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Text;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ /// <summary>
+ /// clean up this code when we do selection validator work.
+ /// </summary>
+ public abstract class SelectionResult
+ {
+ protected SelectionResult(OperationStatus status)
+ {
+ // Contract.ThrowIfNull(status);
+
+ this.Status = status;
+ }
+
+ protected SelectionResult(
+ OperationStatus status,
+ TextSpan originalSpan,
+ TextSpan finalSpan,
+ OptionSet options,
+ bool selectionInExpression,
+ SemanticDocument document,
+ SyntaxAnnotation firstTokenAnnotation,
+ SyntaxAnnotation lastTokenAnnotation)
+ {
+ this.Status = status;
+
+ this.OriginalSpan = originalSpan;
+ this.FinalSpan = finalSpan;
+
+ this.SelectionInExpression = selectionInExpression;
+ this.Options = options;
+
+ this.FirstTokenAnnotation = firstTokenAnnotation;
+ this.LastTokenAnnotation = lastTokenAnnotation;
+
+ this.SemanticDocument = document;
+ }
+
+ protected abstract bool UnderAsyncAnonymousMethod(SyntaxToken token, SyntaxToken firstToken, SyntaxToken lastToken);
+
+ public abstract bool ContainingScopeHasAsyncKeyword();
+
+ public abstract SyntaxNode GetContainingScope();
+ public abstract ITypeSymbol GetContainingScopeType();
+
+ public OperationStatus Status { get; }
+ public TextSpan OriginalSpan { get; }
+ public TextSpan FinalSpan { get; }
+ public OptionSet Options { get; }
+ public bool SelectionInExpression { get; }
+ public SemanticDocument SemanticDocument { get; private set; }
+ public SyntaxAnnotation FirstTokenAnnotation { get; }
+ public SyntaxAnnotation LastTokenAnnotation { get; }
+
+ public SelectionResult With(SemanticDocument document)
+ {
+ if (this.SemanticDocument == document)
+ {
+ return this;
+ }
+
+ var clone = (SelectionResult)this.MemberwiseClone();
+ clone.SemanticDocument = document;
+
+ return clone;
+ }
+
+ public bool ContainsValidContext
+ {
+ get
+ {
+ return this.SemanticDocument != null;
+ }
+ }
+
+ public SyntaxToken GetFirstTokenInSelection()
+ {
+ return this.SemanticDocument.GetTokenWithAnnotaton(this.FirstTokenAnnotation);
+ }
+
+ public SyntaxToken GetLastTokenInSelection()
+ {
+ return this.SemanticDocument.GetTokenWithAnnotaton(this.LastTokenAnnotation);
+ }
+
+ public TNode GetContainingScopeOf<TNode>() where TNode : SyntaxNode
+ {
+ var containingScope = this.GetContainingScope();
+ return containingScope.GetAncestorOrThis<TNode>();
+ }
+
+ protected T GetFirstStatement<T>() where T : SyntaxNode
+ {
+ //Contract.ThrowIfTrue(this.SelectionInExpression);
+
+ var token = this.GetFirstTokenInSelection();
+ return token.GetAncestor<T>();
+ }
+
+ protected T GetLastStatement<T>() where T : SyntaxNode
+ {
+ //Contract.ThrowIfTrue(this.SelectionInExpression);
+
+ var token = this.GetLastTokenInSelection();
+ return token.GetAncestor<T>();
+ }
+
+ public bool ShouldPutAsyncModifier()
+ {
+ var firstToken = this.GetFirstTokenInSelection();
+ var lastToken = this.GetLastTokenInSelection();
+
+ for (var currentToken = firstToken;
+ currentToken.Span.End < lastToken.SpanStart;
+ currentToken = currentToken.GetNextToken())
+ {
+ // [|
+ // async () => await ....
+ // |]
+ //
+ // for the case above, even if the selection contains "await", it doesn't belong to the enclosing block
+ // which extract method is applied to
+ if (currentToken.IsAwaitKeyword()
+ && !UnderAsyncAnonymousMethod(currentToken, firstToken, lastToken))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public bool AllowMovingDeclaration
+ {
+ get
+ {
+ return this.Options.GetOption(ExtractMethodOptions.AllowMovingDeclaration, this.SemanticDocument.Project.Language);
+ }
+ }
+
+ public bool DontPutOutOrRefOnStruct
+ {
+ get
+ {
+ return this.Options.GetOption(ExtractMethodOptions.DontPutOutOrRefOnStruct, this.SemanticDocument.Project.Language);
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/SelectionValidator.NullSelectionResult.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/SelectionValidator.NullSelectionResult.cs
new file mode 100644
index 0000000000..59f8ff9b70
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/SelectionValidator.NullSelectionResult.cs
@@ -0,0 +1,52 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public partial class SelectionValidator
+ {
+ // null object
+ protected class NullSelectionResult : SelectionResult
+ {
+ public NullSelectionResult() :
+ this(OperationStatus.FailedWithUnknownReason)
+ {
+ }
+
+ protected NullSelectionResult(OperationStatus status) :
+ base(status)
+ {
+ }
+
+ protected override bool UnderAsyncAnonymousMethod(SyntaxToken token, SyntaxToken firstToken, SyntaxToken lastToken)
+ {
+ throw new InvalidOperationException();
+ }
+
+ public override bool ContainingScopeHasAsyncKeyword()
+ {
+ throw new InvalidOperationException();
+ }
+
+ public override SyntaxNode GetContainingScope()
+ {
+ throw new InvalidOperationException();
+ }
+
+ public override ITypeSymbol GetContainingScopeType()
+ {
+ throw new InvalidOperationException();
+ }
+ }
+
+ protected class ErrorSelectionResult : NullSelectionResult
+ {
+ public ErrorSelectionResult(OperationStatus status) :
+ base(status.MakeFail())
+ {
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/SelectionValidator.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/SelectionValidator.cs
new file mode 100644
index 0000000000..440d7e9207
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/SelectionValidator.cs
@@ -0,0 +1,185 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Options;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Text;
+using Roslyn.Utilities;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public abstract partial class SelectionValidator
+ {
+ protected static readonly SelectionResult NullSelection = new NullSelectionResult();
+
+ protected readonly SemanticDocument SemanticDocument;
+ protected readonly TextSpan OriginalSpan;
+ protected readonly OptionSet Options;
+
+ protected SelectionValidator(
+ SemanticDocument document,
+ TextSpan textSpan,
+ OptionSet options)
+ {
+ //Contract.ThrowIfNull(document);
+
+ this.SemanticDocument = document;
+ this.OriginalSpan = textSpan;
+ this.Options = options;
+ }
+
+ public bool ContainsValidSelection
+ {
+ get
+ {
+ return !this.OriginalSpan.IsEmpty;
+ }
+ }
+
+ public abstract Task<SelectionResult> GetValidSelectionAsync(CancellationToken cancellationToken);
+ public abstract IEnumerable<SyntaxNode> GetOuterReturnStatements(SyntaxNode commonRoot, IEnumerable<SyntaxNode> jumpsOutOfRegion);
+ public abstract bool IsFinalSpanSemanticallyValidSpan(SyntaxNode node, TextSpan textSpan, IEnumerable<SyntaxNode> returnStatements, CancellationToken cancellationToken);
+ public abstract bool ContainsNonReturnExitPointsStatements(IEnumerable<SyntaxNode> jumpsOutOfRegion);
+
+ protected bool IsFinalSpanSemanticallyValidSpan(
+ SemanticModel semanticModel, TextSpan textSpan, Tuple<SyntaxNode, SyntaxNode> range, CancellationToken cancellationToken)
+ {
+ //Contract.ThrowIfNull(range);
+
+ var controlFlowAnalysisData = semanticModel.AnalyzeControlFlow(range.Item1, range.Item2);
+
+ // there must be no control in and out of given span
+ if (controlFlowAnalysisData.EntryPoints.Any())
+ {
+ return false;
+ }
+
+ // check something like continue, break, yield break, yield return, and etc
+ if (ContainsNonReturnExitPointsStatements(controlFlowAnalysisData.ExitPoints))
+ {
+ return false;
+ }
+
+ // okay, there is no branch out, check whether next statement can be executed normally
+ var returnStatements = GetOuterReturnStatements(range.Item1.GetCommonRoot(range.Item2), controlFlowAnalysisData.ExitPoints);
+ if (!returnStatements.Any())
+ {
+ if (!controlFlowAnalysisData.EndPointIsReachable)
+ {
+ // REVIEW: should we just do extract method regardless or show some warning to user?
+ // in dev10, looks like we went ahead and did the extract method even if selection contains
+ // unreachable code.
+ }
+
+ return true;
+ }
+
+ // okay, only branch was return. make sure we have all return in the selection. (?)
+ if (!controlFlowAnalysisData.EndPointIsReachable)
+ {
+ return true;
+ }
+
+ // there is a return statement, and current position is reachable. let's check whether this is a case where that is okay
+ return IsFinalSpanSemanticallyValidSpan(semanticModel.SyntaxTree.GetRoot(cancellationToken), textSpan, returnStatements, cancellationToken);
+ }
+
+ protected Tuple<SyntaxNode, SyntaxNode> GetStatementRangeContainingSpan<T>(
+ SyntaxNode root, TextSpan textSpan, CancellationToken cancellationToken) where T : SyntaxNode
+ {
+ // use top-down approach to find smallest statement range that contains given span.
+ // this approach is more expansive than bottom-up approach I used before but way simpler and easy to understand
+ var token1 = root.FindToken(textSpan.Start);
+ var token2 = root.FindTokenFromEnd(textSpan.End);
+
+ var commonRoot = token1.GetCommonRoot(token2).GetAncestorOrThis<T>() ?? root;
+
+ var firstStatement = default(T);
+ var lastStatement = default(T);
+
+ var spine = new List<T>();
+
+ foreach (var stmt in commonRoot.DescendantNodesAndSelf().OfType<T>())
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ // quick skip check.
+ // - not containing at all
+ if (stmt.Span.End < textSpan.Start)
+ {
+ continue;
+ }
+
+ // quick exit check
+ // - passed candidate statements
+ if (textSpan.End < stmt.SpanStart)
+ {
+ break;
+ }
+
+ if (stmt.SpanStart <= textSpan.Start)
+ {
+ // keep track spine
+ spine.Add(stmt);
+ }
+
+ if (textSpan.End <= stmt.Span.End && spine.Any(s => s.Parent == stmt.Parent))
+ {
+ // malformed code or selection can make spine to have more than an elements
+ firstStatement = spine.First(s => s.Parent == stmt.Parent);
+ lastStatement = stmt;
+
+ spine.Clear();
+ }
+ }
+
+ if (firstStatement == null || lastStatement == null)
+ {
+ return null;
+ }
+
+ return new Tuple<SyntaxNode, SyntaxNode>(firstStatement, lastStatement);
+ }
+
+ protected Tuple<SyntaxNode, SyntaxNode> GetStatementRangeContainedInSpan<T>(
+ SyntaxNode root, TextSpan textSpan, CancellationToken cancellationToken) where T : SyntaxNode
+ {
+ // use top-down approach to find largest statement range contained in the given span
+ // this method is a bit more expensive than bottom-up approach, but way more simpler than the other approach.
+ var token1 = root.FindToken(textSpan.Start);
+ var token2 = root.FindTokenFromEnd(textSpan.End);
+
+ var commonRoot = token1.GetCommonRoot(token2).GetAncestorOrThis<T>() ?? root;
+
+ T firstStatement = null;
+ T lastStatement = null;
+
+ foreach (var stmt in commonRoot.DescendantNodesAndSelf().OfType<T>())
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ if (firstStatement == null && stmt.SpanStart >= textSpan.Start)
+ {
+ firstStatement = stmt;
+ }
+
+ if (firstStatement != null && stmt.Span.End <= textSpan.End && stmt.Parent == firstStatement.Parent)
+ {
+ lastStatement = stmt;
+ }
+ }
+
+ if (firstStatement == null || lastStatement == null)
+ {
+ return null;
+ }
+
+ return new Tuple<SyntaxNode, SyntaxNode>(firstStatement, lastStatement);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/SimpleExtractMethodResult.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/SimpleExtractMethodResult.cs
new file mode 100644
index 0000000000..4be54b61b3
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/SimpleExtractMethodResult.cs
@@ -0,0 +1,17 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public class SimpleExtractMethodResult : ExtractMethodResult
+ {
+ public SimpleExtractMethodResult(
+ OperationStatus status,
+ Document document,
+ SyntaxToken invocationNameToken,
+ SyntaxNode methodDefinition)
+ : base(status.Flag, status.Reasons, document, invocationNameToken, methodDefinition)
+ {
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/UniqueNameGenerator.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/UniqueNameGenerator.cs
new file mode 100644
index 0000000000..4ee4242b76
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/UniqueNameGenerator.cs
@@ -0,0 +1,28 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.CodeAnalysis.Shared.Utilities;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public class UniqueNameGenerator
+ {
+ private readonly SemanticModel _semanticModel;
+
+ public UniqueNameGenerator(SemanticModel semanticModel)
+ {
+ //Contract.ThrowIfNull(semanticModel);
+ _semanticModel = semanticModel;
+ }
+
+ public string CreateUniqueMethodName(SyntaxNode contextNode, string baseName)
+ {
+ //Contract.ThrowIfNull(contextNode);
+ //Contract.ThrowIfNull(baseName);
+
+ return NameGenerator.GenerateUniqueName(baseName, string.Empty,
+ n => _semanticModel.LookupSymbols(contextNode.SpanStart, /*container*/null, n).Length == 0);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/VariableStyle.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/VariableStyle.cs
new file mode 100644
index 0000000000..f648a048b3
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ExtractMethod/VariableStyle.cs
@@ -0,0 +1,49 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace ICSharpCode.NRefactory6.CSharp.ExtractMethod
+{
+ public class VariableStyle
+ {
+ public ParameterStyle ParameterStyle { get; private set; }
+ public ReturnStyle ReturnStyle { get; private set; }
+
+ public static readonly VariableStyle None =
+ new VariableStyle() { ParameterStyle = ParameterStyle.None, ReturnStyle = ReturnStyle.None };
+
+ public static readonly VariableStyle InputOnly =
+ new VariableStyle() { ParameterStyle = ParameterStyle.InputOnly, ReturnStyle = ReturnStyle.None };
+
+ public static readonly VariableStyle Delete =
+ new VariableStyle() { ParameterStyle = ParameterStyle.Delete, ReturnStyle = ReturnStyle.None };
+
+ public static readonly VariableStyle MoveOut =
+ new VariableStyle() { ParameterStyle = ParameterStyle.MoveOut, ReturnStyle = ReturnStyle.None };
+
+ public static readonly VariableStyle SplitOut =
+ new VariableStyle() { ParameterStyle = ParameterStyle.SplitOut, ReturnStyle = ReturnStyle.None };
+
+ public static readonly VariableStyle MoveIn =
+ new VariableStyle() { ParameterStyle = ParameterStyle.MoveIn, ReturnStyle = ReturnStyle.None };
+
+ public static readonly VariableStyle SplitIn =
+ new VariableStyle() { ParameterStyle = ParameterStyle.SplitIn, ReturnStyle = ReturnStyle.None };
+
+ public static readonly VariableStyle NotUsed =
+ new VariableStyle() { ParameterStyle = ParameterStyle.MoveOut, ReturnStyle = ReturnStyle.Initialization };
+
+ public static readonly VariableStyle Ref =
+ new VariableStyle() { ParameterStyle = ParameterStyle.Ref, ReturnStyle = ReturnStyle.AssignmentWithInput };
+
+ public static readonly VariableStyle OnlyAsRefParam =
+ new VariableStyle() { ParameterStyle = ParameterStyle.Ref, ReturnStyle = ReturnStyle.None };
+
+ public static readonly VariableStyle Out =
+ new VariableStyle() { ParameterStyle = ParameterStyle.Out, ReturnStyle = ReturnStyle.AssignmentWithNoInput };
+
+ public static readonly VariableStyle OutWithErrorInput =
+ new VariableStyle() { ParameterStyle = ParameterStyle.Out, ReturnStyle = ReturnStyle.AssignmentWithInput };
+
+ public static readonly VariableStyle OutWithMoveOut =
+ new VariableStyle() { ParameterStyle = ParameterStyle.OutWithMoveOut, ReturnStyle = ReturnStyle.Initialization };
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Formatter/CSharpEditorFormattingService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Formatter/CSharpEditorFormattingService.cs
new file mode 100644
index 0000000000..0507520c2d
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Formatter/CSharpEditorFormattingService.cs
@@ -0,0 +1,292 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Composition;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.Formatting;
+using Microsoft.CodeAnalysis.Formatting.Rules;
+using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.Options;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Text;
+using Roslyn.Utilities;
+using System.Text;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp
+{
+ public partial class CSharpEditorFormattingService
+ {
+ private readonly ImmutableHashSet<char> _supportedChars;
+ private readonly ImmutableHashSet<char> _autoFormattingTriggerChars;
+ private readonly ImmutableDictionary<char, ImmutableHashSet<SyntaxKind>> _multiWordsMap;
+
+ public CSharpEditorFormattingService()
+ {
+ _autoFormattingTriggerChars = ImmutableHashSet.CreateRange<char>(";}");
+
+ // add all auto formatting trigger to supported char
+ _supportedChars = _autoFormattingTriggerChars.Union("{}#nte:)");
+
+ // set up multi words map
+ _multiWordsMap = ImmutableDictionary.CreateRange(new[]
+ {
+ new KeyValuePair<char, ImmutableHashSet<SyntaxKind>> ('n', ImmutableHashSet.Create(SyntaxKind.RegionKeyword, SyntaxKind.EndRegionKeyword)),
+ new KeyValuePair<char, ImmutableHashSet<SyntaxKind>> ('t', ImmutableHashSet.Create(SyntaxKind.SelectKeyword)),
+ new KeyValuePair<char, ImmutableHashSet<SyntaxKind>> ('e', ImmutableHashSet.Create(SyntaxKind.WhereKeyword)),
+ });
+ }
+
+ public bool SupportsFormatDocument { get { return true; } }
+
+ public bool SupportsFormatOnPaste { get { return true; } }
+
+ public bool SupportsFormatSelection { get { return true; } }
+
+ public bool SupportsFormatOnReturn { get { return true; } }
+
+ public bool SupportsFormattingOnTypedCharacter(Document document, char ch)
+ {
+ var optionsService = document.Project.Solution.Workspace.Options;
+ // if ((ch == '}' && !optionsService.GetOption(FeatureOnOffOptions.AutoFormattingOnCloseBrace, document.Project.Language)) ||
+ // (ch == ';' && !optionsService.GetOption(FeatureOnOffOptions.AutoFormattingOnSemicolon, document.Project.Language)))
+ // {
+ // return false;
+ // }
+
+ return _supportedChars.Contains(ch);
+ }
+
+ // public async Task<IList<TextChange>> GetFormattingChangesAsync(Document document, TextSpan? textSpan, CancellationToken cancellationToken)
+ // {
+ // var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
+ //
+ // var span = textSpan.HasValue ? textSpan.Value : new TextSpan(0, root.FullSpan.Length);
+ // var formattingSpan = CommonFormattingHelpers.GetFormattingSpan(root, span);
+ // return Formatter.GetFormattedTextChanges(root, new TextSpan[] { formattingSpan }, document.Project.Solution.Workspace, cancellationToken: cancellationToken);
+ // }
+ //
+ // public async Task<IList<TextChange>> GetFormattingChangesOnPasteAsync(Document document, TextSpan textSpan, CancellationToken cancellationToken)
+ // {
+ // var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
+ // var formattingSpan = CommonFormattingHelpers.GetFormattingSpan(root, textSpan);
+ // var service = document.GetLanguageService<ISyntaxFormattingService>();
+ // if (service == null)
+ // {
+ // return SpecializedCollections.EmptyList<TextChange>();
+ // }
+ //
+ // var rules = new List<IFormattingRule>() { new PasteFormattingRule() };
+ // rules.AddRange(service.GetDefaultFormattingRules());
+ //
+ // return Formatter.GetFormattedTextChanges(root, new[] { formattingSpan }, document.Project.Solution.Workspace, rules: rules, cancellationToken: cancellationToken);
+ // }
+ //
+ // private IEnumerable<IFormattingRule> GetFormattingRules(Document document, int position)
+ // {
+ // var workspace = document.Project.Solution.Workspace;
+ // var formattingRuleFactory = workspace.Services.GetService<IHostDependentFormattingRuleFactoryService>();
+ // return formattingRuleFactory.CreateRule(document, position).Concat(Formatter.GetDefaultFormattingRules(document));
+ // }
+ //
+ // public async Task<IList<TextChange>> GetFormattingChangesOnReturnAsync(Document document, int caretPosition, CancellationToken cancellationToken)
+ // {
+ // var formattingRules = this.GetFormattingRules(document, caretPosition);
+ //
+ // // first, find the token user just typed.
+ // SyntaxToken token = await GetTokenBeforeTheCaretAsync(document, caretPosition, cancellationToken).ConfigureAwait(false);
+ //
+ // if (token.IsMissing)
+ // {
+ // return null;
+ // }
+ //
+ // string text = null;
+ // if (IsInvalidToken(token, ref text))
+ // {
+ // return null;
+ // }
+ //
+ // // Check to see if the token is ')' and also the parent is a using statement. If not, bail
+ // if (TokenShouldNotFormatOnReturn(token))
+ // {
+ // return null;
+ // }
+ //
+ // // if formatting range fails, do format token one at least
+ // var changes = await FormatRangeAsync(document, token, formattingRules, cancellationToken).ConfigureAwait(false);
+ // if (changes.Count > 0)
+ // {
+ // return changes;
+ // }
+ //
+ // // if we can't, do normal smart indentation
+ // return await FormatTokenAsync(document, token, formattingRules, cancellationToken).ConfigureAwait(false);
+ // }
+ //
+ public static bool TokenShouldNotFormatOnReturn(SyntaxToken token)
+ {
+ return !token.IsKind(SyntaxKind.CloseParenToken) || !token.Parent.IsKind(SyntaxKind.UsingStatement);
+ }
+
+ public static bool TokenShouldNotFormatOnTypeChar(SyntaxToken token)
+ {
+ return (token.IsKind(SyntaxKind.CloseParenToken) && !token.Parent.IsKind(SyntaxKind.UsingStatement)) ||
+ (token.IsKind(SyntaxKind.ColonToken) && !(token.Parent.IsKind(SyntaxKind.LabeledStatement) || token.Parent.IsKind(SyntaxKind.CaseSwitchLabel) || token.Parent.IsKind(SyntaxKind.DefaultSwitchLabel)));
+ }
+
+ // public async Task<IList<TextChange>> GetFormattingChangesAsync(Document document, char typedChar, int caretPosition, CancellationToken cancellationToken)
+ // {
+ // var formattingRules = this.GetFormattingRules(document, caretPosition);
+ //
+ // // first, find the token user just typed.
+ // SyntaxToken token = await GetTokenBeforeTheCaretAsync(document, caretPosition, cancellationToken).ConfigureAwait(false);
+ //
+ // if (token.IsMissing ||
+ // !ValidSingleOrMultiCharactersTokenKind(typedChar, token.Kind()) ||
+ // token.IsKind(SyntaxKind.EndOfFileToken, SyntaxKind.None))
+ // {
+ // return null;
+ // }
+ //
+ // var service = document.GetLanguageService<ISyntaxFactsService>();
+ // if (service != null && service.IsInNonUserCode(token.SyntaxTree, caretPosition, cancellationToken))
+ // {
+ // return null;
+ // }
+ //
+ // // Check to see if any of the below. If not, bail.
+ // // case 1: The token is ')' and the parent is an using statement.
+ // // case 2: The token is ':' and the parent is either labelled statement or case switch or default switch
+ // if (TokenShouldNotFormatOnTypeChar(token))
+ // {
+ // return null;
+ // }
+ //
+ // // if formatting range fails, do format token one at least
+ // var changes = await FormatRangeAsync(document, token, formattingRules, cancellationToken).ConfigureAwait(false);
+ // if (changes.Count > 0)
+ // {
+ // return changes;
+ // }
+ //
+ // // if we can't, do normal smart indentation
+ // return await FormatTokenAsync(document, token, formattingRules, cancellationToken).ConfigureAwait(false);
+ // }
+
+ public static async Task<SyntaxToken> GetTokenBeforeTheCaretAsync(Document document, int caretPosition, CancellationToken cancellationToken)
+ {
+ var tree = await document.GetCSharpSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
+
+ var position = Math.Max(0, caretPosition - 1);
+ var root = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false);
+ var token = root.FindToken(position, findInsideTrivia: true);
+ return token;
+ }
+
+ // private async Task<IList<TextChange>> FormatTokenAsync(Document document, SyntaxToken token, IEnumerable<IFormattingRule> formattingRules, CancellationToken cancellationToken)
+ // {
+ // var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
+ // var formatter = CreateSmartTokenFormatter(document.Project.Solution.Workspace.Options, formattingRules, root);
+ // var changes = formatter.FormatToken(document.Project.Solution.Workspace, token, cancellationToken);
+ // return changes;
+ // }
+ //
+ // private ISmartTokenFormatter CreateSmartTokenFormatter(OptionSet optionSet, IEnumerable<IFormattingRule> formattingRules, SyntaxNode root)
+ // {
+ // return new SmartTokenFormatter(optionSet, formattingRules, (CompilationUnitSyntax)root);
+ // }
+ //
+ // private async Task<IList<TextChange>> FormatRangeAsync(
+ // Document document, SyntaxToken endToken, IEnumerable<IFormattingRule> formattingRules,
+ // CancellationToken cancellationToken)
+ // {
+ // if (!IsEndToken(endToken))
+ // {
+ // return SpecializedCollections.EmptyList<TextChange>();
+ // }
+ //
+ // var tokenRange = FormattingRangeHelper.FindAppropriateRange(endToken);
+ // if (tokenRange == null || tokenRange.Value.Item1.Equals(tokenRange.Value.Item2))
+ // {
+ // return SpecializedCollections.EmptyList<TextChange>();
+ // }
+ //
+ // if (IsInvalidTokenKind(tokenRange.Value.Item1) || IsInvalidTokenKind(tokenRange.Value.Item2))
+ // {
+ // return SpecializedCollections.EmptyList<TextChange>();
+ // }
+ //
+ // var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
+ // var formatter = new SmartTokenFormatter(document.Project.Solution.Workspace.Options, formattingRules, (CompilationUnitSyntax)root);
+ //
+ // var changes = formatter.FormatRange(document.Project.Solution.Workspace, tokenRange.Value.Item1, tokenRange.Value.Item2, cancellationToken);
+ // return changes;
+ // }
+ //
+ // private bool IsEndToken(SyntaxToken endToken)
+ // {
+ // if (endToken.IsKind(SyntaxKind.OpenBraceToken))
+ // {
+ // return false;
+ // }
+ //
+ // return true;
+ // }
+ //
+ public bool ValidSingleOrMultiCharactersTokenKind(char typedChar, SyntaxKind kind)
+ {
+ ImmutableHashSet<SyntaxKind> set;
+ if (!_multiWordsMap.TryGetValue(typedChar, out set))
+ {
+ // all single char token is valid
+ return true;
+ }
+
+ return set.Contains(kind);
+ }
+
+ public bool IsInvalidToken(char typedChar, SyntaxToken token)
+ {
+ string text = null;
+ if (IsInvalidToken(token, ref text))
+ {
+ return true;
+ }
+
+ return text[0] != typedChar;
+ }
+
+ public bool IsInvalidToken(SyntaxToken token, ref string text)
+ {
+ if (IsInvalidTokenKind(token))
+ {
+ return true;
+ }
+
+ text = token.ToString();
+ if (text.Length != 1)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ private bool IsInvalidTokenKind(SyntaxToken token)
+ {
+ // invalid token to be formatted
+ return token.IsKind(SyntaxKind.None) ||
+ token.IsKind(SyntaxKind.EndOfDirectiveToken) ||
+ token.IsKind(SyntaxKind.EndOfFileToken);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Formatter/CommonFormattingHelpers.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Formatter/CommonFormattingHelpers.cs
new file mode 100644
index 0000000000..4c3610cfc2
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Formatter/CommonFormattingHelpers.cs
@@ -0,0 +1,372 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.CodeAnalysis.Formatting;
+using Microsoft.CodeAnalysis.Formatting.Rules;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Text;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp
+{
+ public static class CommonFormattingHelpers
+ {
+// public static readonly Comparison<SuppressOperation> SuppressOperationComparer = (o1, o2) =>
+// {
+// return o1.TextSpan.Start - o2.TextSpan.Start;
+// };
+//
+// public static readonly Comparison<IndentBlockOperation> IndentBlockOperationComparer = (o1, o2) =>
+// {
+// // smaller one goes left
+// var s = o1.TextSpan.Start - o2.TextSpan.Start;
+// if (s != 0)
+// {
+// return s;
+// }
+//
+// // bigger one goes left
+// var e = o2.TextSpan.End - o1.TextSpan.End;
+// if (e != 0)
+// {
+// return e;
+// }
+//
+// return 0;
+// };
+//
+// public static IEnumerable<ValueTuple<SyntaxToken, SyntaxToken>> ConvertToTokenPairs(this SyntaxNode root, IList<TextSpan> spans)
+// {
+// Contract.ThrowIfNull(root);
+// Contract.ThrowIfFalse(spans.Count > 0);
+//
+// if (spans.Count == 1)
+// {
+// // special case, if there is only one span, return right away
+// yield return root.ConvertToTokenPair(spans[0]);
+// yield break;
+// }
+//
+// var pairs = new List<ValueTuple<SyntaxToken, SyntaxToken>>();
+// var previousOne = root.ConvertToTokenPair(spans[0]);
+//
+// // iterate through each spans and make sure each one doesn't overlap each other
+// for (int i = 1; i < spans.Count; i++)
+// {
+// var currentOne = root.ConvertToTokenPair(spans[i]);
+// if (currentOne.Item1.SpanStart <= previousOne.Item2.Span.End)
+// {
+// // oops, looks like two spans are overlapping each other. merge them
+// previousOne = ValueTuple.Create(previousOne.Item1, previousOne.Item2.Span.End < currentOne.Item2.Span.End ? currentOne.Item2 : previousOne.Item2);
+// continue;
+// }
+//
+// // okay, looks like things are in good shape
+// yield return previousOne;
+//
+// // move to next one
+// previousOne = currentOne;
+// }
+//
+// // give out the last one
+// yield return previousOne;
+// }
+//
+// public static ValueTuple<SyntaxToken, SyntaxToken> ConvertToTokenPair(this SyntaxNode root, TextSpan textSpan)
+// {
+// Contract.ThrowIfNull(root);
+// Contract.ThrowIfTrue(textSpan.IsEmpty);
+//
+// var startToken = root.FindToken(textSpan.Start);
+//
+// // empty token, get previous non-zero length token
+// if (startToken.IsMissing)
+// {
+// // if there is no previous token, startToken will be set to SyntaxKind.None
+// startToken = startToken.GetPreviousToken();
+// }
+//
+// // span is on leading trivia
+// if (textSpan.Start < startToken.SpanStart)
+// {
+// // if there is no previous token, startToken will be set to SyntaxKind.None
+// startToken = startToken.GetPreviousToken();
+// }
+//
+// // adjust position where we try to search end token
+// var endToken = (root.FullSpan.End <= textSpan.End) ?
+// root.GetLastToken(includeZeroWidth: true) : root.FindToken(textSpan.End);
+//
+// // empty token, get next token
+// if (endToken.IsMissing)
+// {
+// endToken = endToken.GetNextToken();
+// }
+//
+// // span is on trailing trivia
+// if (endToken.Span.End < textSpan.End)
+// {
+// endToken = endToken.GetNextToken();
+// }
+//
+// // make sure tokens are not SyntaxKind.None
+// startToken = (startToken.RawKind != 0) ? startToken : root.GetFirstToken(includeZeroWidth: true);
+// endToken = (endToken.RawKind != 0) ? endToken : root.GetLastToken(includeZeroWidth: true);
+//
+// // token is in right order
+// Contract.ThrowIfFalse(startToken.Equals(endToken) || startToken.Span.End <= endToken.SpanStart);
+// return ValueTuple.Create(startToken, endToken);
+// }
+//
+// public static bool IsInvalidTokenRange(this SyntaxNode root, SyntaxToken startToken, SyntaxToken endToken)
+// {
+// // given token must be token exist excluding EndOfFile token.
+// if (startToken.RawKind == 0 || endToken.RawKind == 0)
+// {
+// return true;
+// }
+//
+// if (startToken.Equals(endToken))
+// {
+// return false;
+// }
+//
+// // regular case.
+// // start token can't be end of file token and start token must be before end token if it's not the same token.
+// return root.FullSpan.End == startToken.SpanStart || startToken.FullSpan.End > endToken.FullSpan.Start;
+// }
+//
+// public static int GetTokenColumn(this SyntaxTree tree, SyntaxToken token, int tabSize)
+// {
+// Contract.ThrowIfNull(tree);
+// Contract.ThrowIfTrue(token.RawKind == 0);
+//
+// var startPosition = token.SpanStart;
+// var line = tree.GetText().Lines.GetLineFromPosition(startPosition);
+//
+// return line.GetColumnFromLineOffset(startPosition - line.Start, tabSize);
+// }
+//
+// public static string GetText(this SourceText text, SyntaxToken token1, SyntaxToken token2)
+// {
+// return (token1.RawKind == 0) ? text.ToString(TextSpan.FromBounds(0, token2.SpanStart)) : text.ToString(TextSpan.FromBounds(token1.Span.End, token2.SpanStart));
+// }
+//
+ public static string GetTextBetween(SyntaxToken token1, SyntaxToken token2)
+ {
+ var builder = new StringBuilder();
+ AppendTextBetween(token1, token2, builder);
+
+ return builder.ToString();
+ }
+
+ public static void AppendTextBetween(SyntaxToken token1, SyntaxToken token2, StringBuilder builder)
+ {
+// Contract.ThrowIfTrue(token1.RawKind == 0 && token2.RawKind == 0);
+// Contract.ThrowIfTrue(token1.Equals(token2));
+//
+ if (token1.RawKind == 0)
+ {
+ AppendLeadingTriviaText(token2, builder);
+ return;
+ }
+
+ if (token2.RawKind == 0)
+ {
+ AppendTrailingTriviaText(token1, builder);
+ return;
+ }
+
+ var token1PartOftoken2LeadingTrivia = token1.FullSpan.Start > token2.FullSpan.Start;
+
+ if (token1.FullSpan.End == token2.FullSpan.Start)
+ {
+ AppendTextBetweenTwoAdjacentTokens(token1, token2, builder);
+ return;
+ }
+
+ AppendTrailingTriviaText(token1, builder);
+
+ for (var token = token1.GetNextToken(includeZeroWidth: true); token.FullSpan.End <= token2.FullSpan.Start; token = token.GetNextToken(includeZeroWidth: true))
+ {
+ builder.Append(token.ToFullString());
+ }
+
+ AppendPartialLeadingTriviaText(token2, builder, token1.TrailingTrivia.FullSpan.End);
+ }
+
+ private static void AppendTextBetweenTwoAdjacentTokens(SyntaxToken token1, SyntaxToken token2, StringBuilder builder)
+ {
+ AppendTrailingTriviaText(token1, builder);
+ AppendLeadingTriviaText(token2, builder);
+ }
+
+ private static void AppendLeadingTriviaText(SyntaxToken token, StringBuilder builder)
+ {
+ if (!token.HasLeadingTrivia)
+ {
+ return;
+ }
+
+ foreach (var trivia in token.LeadingTrivia)
+ {
+ builder.Append(trivia.ToFullString());
+ }
+ }
+
+ /// <summary>
+ /// If the token1 is expected to be part of the leading trivia of the token2 then the trivia
+ /// before the token1FullSpanEnd, which the fullspan end of the token1 should be ignored
+ /// </summary>
+ private static void AppendPartialLeadingTriviaText(SyntaxToken token, StringBuilder builder, int token1FullSpanEnd)
+ {
+ if (!token.HasLeadingTrivia)
+ {
+ return;
+ }
+
+ foreach (var trivia in token.LeadingTrivia)
+ {
+ if (trivia.FullSpan.End <= token1FullSpanEnd)
+ {
+ continue;
+ }
+
+ builder.Append(trivia.ToFullString());
+ }
+ }
+
+ private static void AppendTrailingTriviaText(SyntaxToken token, StringBuilder builder)
+ {
+ if (!token.HasTrailingTrivia)
+ {
+ return;
+ }
+
+ foreach (var trivia in token.TrailingTrivia)
+ {
+ builder.Append(trivia.ToFullString());
+ }
+ }
+
+// /// <summary>
+// /// this will create a span that includes its trailing trivia of its previous token and leading trivia of its next token
+// /// for example, for code such as "class A { int ...", if given tokens are "A" and "{", this will return span [] of "class[ A { ]int ..."
+// /// which included trailing trivia of "class" which is previous token of "A", and leading trivia of "int" which is next token of "{"
+// /// </summary>
+// public static TextSpan GetSpanIncludingTrailingAndLeadingTriviaOfAdjacentTokens(SyntaxToken startToken, SyntaxToken endToken)
+// {
+// // most of cases we can just ask previous and next token to create the span, but in some corner cases such as omitted token case,
+// // those navigation function doesn't work, so we have to explore the tree ourselves to create rigth span
+// var startPosition = GetStartPositionOfSpan(startToken);
+// var endPosition = GetEndPositionOfSpan(endToken);
+//
+// return TextSpan.FromBounds(startPosition, endPosition);
+// }
+//
+// private static int GetEndPositionOfSpan(SyntaxToken token)
+// {
+// var nextToken = token.GetNextToken();
+// if (nextToken.RawKind != 0)
+// {
+// return nextToken.SpanStart;
+// }
+//
+// var backwardPosition = token.FullSpan.End;
+// var parentNode = GetParentThatContainsGivenSpan(token.Parent, backwardPosition, forward: false);
+// if (parentNode == null)
+// {
+// // reached the end of tree
+// return token.FullSpan.End;
+// }
+//
+// Contract.ThrowIfFalse(backwardPosition < parentNode.FullSpan.End);
+//
+// nextToken = parentNode.FindToken(backwardPosition + 1);
+//
+// Contract.ThrowIfTrue(nextToken.RawKind == 0);
+//
+// return nextToken.SpanStart;
+// }
+//
+// public static int GetStartPositionOfSpan(SyntaxToken token)
+// {
+// var previousToken = token.GetPreviousToken();
+// if (previousToken.RawKind != 0)
+// {
+// return previousToken.Span.End;
+// }
+//
+// // first token in the tree
+// var forwardPosition = token.FullSpan.Start;
+// if (forwardPosition <= 0)
+// {
+// return 0;
+// }
+//
+// var parentNode = GetParentThatContainsGivenSpan(token.Parent, forwardPosition, forward: true);
+// if (parentNode == null)
+// {
+// return Contract.FailWithReturn<int>("This can't happen");
+// }
+//
+// Contract.ThrowIfFalse(parentNode.FullSpan.Start < forwardPosition);
+//
+// previousToken = parentNode.FindToken(forwardPosition + 1);
+//
+// Contract.ThrowIfTrue(previousToken.RawKind == 0);
+//
+// return previousToken.Span.End;
+// }
+//
+// private static SyntaxNode GetParentThatContainsGivenSpan(SyntaxNode node, int position, bool forward)
+// {
+// while (node != null)
+// {
+// var fullSpan = node.FullSpan;
+// if (forward)
+// {
+// if (fullSpan.Start < position)
+// {
+// return node;
+// }
+// }
+// else
+// {
+// if (position > fullSpan.End)
+// {
+// return node;
+// }
+// }
+//
+// node = node.Parent;
+// }
+//
+// return null;
+// }
+//
+// public static bool HasAnyWhitespaceElasticTrivia(SyntaxToken previousToken, SyntaxToken currentToken)
+// {
+// if ((!previousToken.ContainsAnnotations && !currentToken.ContainsAnnotations) ||
+// (!previousToken.HasTrailingTrivia && !currentToken.HasLeadingTrivia))
+// {
+// return false;
+// }
+//
+// return previousToken.TrailingTrivia.HasAnyWhitespaceElasticTrivia() || currentToken.LeadingTrivia.HasAnyWhitespaceElasticTrivia();
+// }
+//
+// public static bool IsNull<T>(T t) where T : class
+// {
+// return t == null;
+// }
+//
+// public static bool IsNotNull<T>(T t) where T : class
+// {
+// return !IsNull(t);
+// }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Formatter/FormattingHelpers.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Formatter/FormattingHelpers.cs
new file mode 100644
index 0000000000..357f4e46aa
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Formatter/FormattingHelpers.cs
@@ -0,0 +1,552 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp
+{
+ public static class FormattingHelpers
+ {
+ // TODO: Need to determine correct way to handle newlines
+ public const string NewLine = "\r\n";
+
+ public static string GetIndent(this SyntaxToken token)
+ {
+ var precedingTrivia = token.GetAllPrecedingTriviaToPreviousToken();
+
+ // indent is the spaces/tabs between last new line (if there is one) and end of trivia
+ var indent = precedingTrivia.AsString();
+ int lastNewLinePos = indent.LastIndexOf(NewLine);
+ if (lastNewLinePos != -1)
+ {
+ int start = lastNewLinePos + NewLine.Length;
+ indent = indent.Substring(start, indent.Length - start);
+ }
+
+ return indent;
+ }
+
+ public static string ContentBeforeLastNewLine(this IEnumerable<SyntaxTrivia> trivia)
+ {
+ var leading = trivia.AsString();
+ int lastNewLinePos = leading.LastIndexOf(NewLine);
+ if (lastNewLinePos == -1)
+ {
+ return string.Empty;
+ }
+ else
+ {
+ return leading.Substring(0, lastNewLinePos);
+ }
+ }
+
+ public static ValueTuple<SyntaxToken, SyntaxToken> GetBracePair(this SyntaxNode node)
+ {
+ return node.GetBraces();
+ }
+
+ public static bool IsValidBracePair(this ValueTuple<SyntaxToken, SyntaxToken> bracePair)
+ {
+ if (bracePair.Item1.IsKind(SyntaxKind.None) ||
+ bracePair.Item1.IsMissing ||
+ bracePair.Item2.IsKind(SyntaxKind.None))
+ {
+ return false;
+ }
+
+ // don't check whether token is actually braces as long as it is not none.
+ return true;
+ }
+
+ public static bool IsOpenParenInParameterList(this SyntaxToken token)
+ {
+ return token.Kind() == SyntaxKind.OpenParenToken && token.Parent.Kind() == SyntaxKind.ParameterList;
+ }
+
+ public static bool IsCloseParenInParameterList(this SyntaxToken token)
+ {
+ return token.Kind() == SyntaxKind.CloseParenToken && token.Parent.Kind() == SyntaxKind.ParameterList;
+ }
+
+ public static bool IsOpenParenInArgumentList(this SyntaxToken token)
+ {
+ return token.Kind() == SyntaxKind.OpenParenToken && token.Parent.Kind() == SyntaxKind.ArgumentList;
+ }
+
+ public static bool IsCloseParenInArgumentList(this SyntaxToken token)
+ {
+ return token.Kind() == SyntaxKind.CloseParenToken && token.Parent.Kind() == SyntaxKind.ArgumentList;
+ }
+
+ public static bool IsColonInTypeBaseList(this SyntaxToken token)
+ {
+ return token.Kind() == SyntaxKind.ColonToken && token.Parent.Kind() == SyntaxKind.BaseList;
+ }
+
+ public static bool IsCommaInArgumentOrParameterList(this SyntaxToken token)
+ {
+ return token.Kind() == SyntaxKind.CommaToken && (token.Parent.IsAnyArgumentList() || token.Parent.Kind() == SyntaxKind.ParameterList);
+ }
+
+ public static bool IsLambdaBodyBlock(this SyntaxNode node)
+ {
+ if (node.Kind() != SyntaxKind.Block)
+ {
+ return false;
+ }
+
+ return node.Parent.Kind() == SyntaxKind.SimpleLambdaExpression ||
+ node.Parent.Kind() == SyntaxKind.ParenthesizedLambdaExpression;
+ }
+
+ public static bool IsAnonymousMethodBlock(this SyntaxNode node)
+ {
+ if (node.Kind() != SyntaxKind.Block)
+ {
+ return false;
+ }
+
+ return node.Parent.Kind() == SyntaxKind.AnonymousMethodExpression;
+ }
+
+ public static bool IsSemicolonInForStatement(this SyntaxToken token)
+ {
+ var forStatement = token.Parent as ForStatementSyntax;
+ return
+ token.Kind() == SyntaxKind.SemicolonToken &&
+ forStatement != null &&
+ (forStatement.FirstSemicolonToken == token || forStatement.SecondSemicolonToken == token);
+ }
+
+ public static bool IsSemicolonOfEmbeddedStatement(this SyntaxToken token)
+ {
+ if (token.Kind() != SyntaxKind.SemicolonToken)
+ {
+ return false;
+ }
+
+ var statement = token.Parent as StatementSyntax;
+ if (statement == null ||
+ statement.GetLastToken() != token)
+ {
+ return false;
+ }
+
+ return IsEmbeddedStatement(statement);
+ }
+
+ public static bool IsCloseBraceOfExpression(this SyntaxToken token)
+ {
+ if (token.Kind() != SyntaxKind.CloseBraceToken)
+ {
+ return false;
+ }
+
+ return token.Parent is ExpressionSyntax;
+ }
+
+ public static bool IsCloseBraceOfEmbeddedBlock(this SyntaxToken token)
+ {
+ if (token.Kind() != SyntaxKind.CloseBraceToken)
+ {
+ return false;
+ }
+
+ var block = token.Parent as BlockSyntax;
+ if (block == null ||
+ block.CloseBraceToken != token)
+ {
+ return false;
+ }
+
+ return IsEmbeddedStatement(block);
+ }
+
+ public static bool IsEmbeddedStatement(this SyntaxNode node)
+ {
+ SyntaxNode statementOrElse = node as StatementSyntax;
+ if (statementOrElse == null)
+ {
+ statementOrElse = node as ElseClauseSyntax;
+ }
+
+ return statementOrElse != null
+ && statementOrElse.Parent != null
+ && statementOrElse.Parent.IsEmbeddedStatementOwner();
+ }
+
+ public static bool IsCommaInEnumDeclaration(this SyntaxToken token)
+ {
+ return token.Kind() == SyntaxKind.CommaToken &&
+ token.Parent.IsKind(SyntaxKind.EnumDeclaration);
+ }
+
+ public static bool IsCommaInAnyArgumentsList(this SyntaxToken token)
+ {
+ return token.Kind() == SyntaxKind.CommaToken &&
+ token.Parent.IsAnyArgumentList();
+ }
+
+ public static bool IsParenInParenthesizedExpression(this SyntaxToken token)
+ {
+ var parenthesizedExpression = token.Parent as ParenthesizedExpressionSyntax;
+ if (parenthesizedExpression == null)
+ {
+ return false;
+ }
+
+ return parenthesizedExpression.OpenParenToken.Equals(token) || parenthesizedExpression.CloseParenToken.Equals(token);
+ }
+
+ public static bool IsParenInArgumentList(this SyntaxToken token)
+ {
+ var parent = token.Parent;
+ switch (parent.Kind())
+ {
+ case SyntaxKind.SizeOfExpression:
+ var sizeOfExpression = (SizeOfExpressionSyntax)parent;
+ return sizeOfExpression.OpenParenToken == token || sizeOfExpression.CloseParenToken == token;
+
+ case SyntaxKind.TypeOfExpression:
+ var typeOfExpression = (TypeOfExpressionSyntax)parent;
+ return typeOfExpression.OpenParenToken == token || typeOfExpression.CloseParenToken == token;
+
+ case SyntaxKind.CheckedExpression:
+ case SyntaxKind.UncheckedExpression:
+ var checkedOfExpression = (CheckedExpressionSyntax)parent;
+ return checkedOfExpression.OpenParenToken == token || checkedOfExpression.CloseParenToken == token;
+
+ case SyntaxKind.DefaultExpression:
+ var defaultExpression = (DefaultExpressionSyntax)parent;
+ return defaultExpression.OpenParenToken == token || defaultExpression.CloseParenToken == token;
+
+ case SyntaxKind.MakeRefExpression:
+ var makeRefExpression = (MakeRefExpressionSyntax)parent;
+ return makeRefExpression.OpenParenToken == token || makeRefExpression.CloseParenToken == token;
+
+ case SyntaxKind.RefTypeExpression:
+ var refTypeOfExpression = (RefTypeExpressionSyntax)parent;
+ return refTypeOfExpression.OpenParenToken == token || refTypeOfExpression.CloseParenToken == token;
+
+ case SyntaxKind.RefValueExpression:
+ var refValueExpression = (RefValueExpressionSyntax)parent;
+ return refValueExpression.OpenParenToken == token || refValueExpression.CloseParenToken == token;
+
+ case SyntaxKind.ArgumentList:
+ var argumentList = (ArgumentListSyntax)parent;
+ return argumentList.OpenParenToken == token || argumentList.CloseParenToken == token;
+
+ case SyntaxKind.AttributeArgumentList:
+ var attributeArgumentList = (AttributeArgumentListSyntax)parent;
+ return attributeArgumentList.OpenParenToken == token || attributeArgumentList.CloseParenToken == token;
+ }
+
+ return false;
+ }
+
+ public static bool IsCloseParenInStatement(this SyntaxToken token)
+ {
+ var statement = token.Parent as StatementSyntax;
+ if (statement == null)
+ {
+ return false;
+ }
+
+ var ifStatement = statement as IfStatementSyntax;
+ if (ifStatement != null)
+ {
+ return ifStatement.CloseParenToken.Equals(token);
+ }
+
+ var switchStatement = statement as SwitchStatementSyntax;
+ if (switchStatement != null)
+ {
+ return switchStatement.CloseParenToken.Equals(token);
+ }
+
+ var whileStatement = statement as WhileStatementSyntax;
+ if (whileStatement != null)
+ {
+ return whileStatement.CloseParenToken.Equals(token);
+ }
+
+ var doStatement = statement as DoStatementSyntax;
+ if (doStatement != null)
+ {
+ return doStatement.CloseParenToken.Equals(token);
+ }
+
+ var forStatement = statement as ForStatementSyntax;
+ if (forStatement != null)
+ {
+ return forStatement.CloseParenToken.Equals(token);
+ }
+
+ var foreachStatement = statement as ForEachStatementSyntax;
+ if (foreachStatement != null)
+ {
+ return foreachStatement.CloseParenToken.Equals(token);
+ }
+
+ var lockStatement = statement as LockStatementSyntax;
+ if (lockStatement != null)
+ {
+ return lockStatement.CloseParenToken.Equals(token);
+ }
+
+ var usingStatement = statement as UsingStatementSyntax;
+ if (usingStatement != null)
+ {
+ return usingStatement.CloseParenToken.Equals(token);
+ }
+
+ return false;
+ }
+
+ public static bool IsDotInMemberAccessOrQualifiedName(this SyntaxToken token)
+ {
+ return token.IsDotInMemberAccess() || (token.Kind() == SyntaxKind.DotToken && token.Parent.Kind() == SyntaxKind.QualifiedName);
+ }
+
+ public static bool IsDotInMemberAccess(this SyntaxToken token)
+ {
+ var memberAccess = token.Parent as MemberAccessExpressionSyntax;
+ if (memberAccess == null)
+ {
+ return false;
+ }
+
+ return token.Kind() == SyntaxKind.DotToken
+ && memberAccess.OperatorToken.Equals(token);
+ }
+
+ public static bool IsGenericGreaterThanToken(this SyntaxToken token)
+ {
+ if (token.Kind() == SyntaxKind.GreaterThanToken)
+ {
+ return token.Parent.IsKind(SyntaxKind.TypeParameterList, SyntaxKind.TypeArgumentList);
+ }
+
+ return false;
+ }
+
+ public static bool IsCommaInInitializerExpression(this SyntaxToken token)
+ {
+ return token.Kind() == SyntaxKind.CommaToken &&
+ ((token.Parent is InitializerExpressionSyntax) ||
+ (token.Parent is AnonymousObjectCreationExpressionSyntax));
+ }
+
+ public static bool IsIdentiferInLabeledStatement(this SyntaxToken token)
+ {
+ var labeledStatement = token.Parent as LabeledStatementSyntax;
+ return token.Kind() == SyntaxKind.IdentifierToken &&
+ labeledStatement != null &&
+ labeledStatement.Identifier == token;
+ }
+
+ public static bool IsColonInSwitchLabel(this SyntaxToken token)
+ {
+ return FormattingRangeHelper.IsColonInSwitchLabel(token);
+ }
+
+ public static bool IsColonInLabeledStatement(this SyntaxToken token)
+ {
+ var labeledStatement = token.Parent as LabeledStatementSyntax;
+ return token.Kind() == SyntaxKind.ColonToken &&
+ labeledStatement != null &&
+ labeledStatement.ColonToken == token;
+ }
+
+ public static bool IsEmbeddedStatementOwnerWithCloseParen(this SyntaxNode node)
+ {
+ return node is IfStatementSyntax ||
+ node is WhileStatementSyntax ||
+ node is ForStatementSyntax ||
+ node is ForEachStatementSyntax ||
+ node is UsingStatementSyntax;
+ }
+
+ public static bool IsNestedQueryExpression(this SyntaxToken token)
+ {
+ var fromClause = token.Parent as FromClauseSyntax;
+ return token.Kind() == SyntaxKind.InKeyword &&
+ fromClause != null &&
+ fromClause.Expression is QueryExpressionSyntax;
+ }
+
+ public static bool IsFirstFromKeywordInExpression(this SyntaxToken token)
+ {
+ var queryExpression = token.Parent.Parent as QueryExpressionSyntax;
+ return token.Kind() == SyntaxKind.FromKeyword &&
+ queryExpression != null &&
+ queryExpression.GetFirstToken().Equals(token);
+ }
+
+ public static bool IsInitializerForObjectOrAnonymousObjectCreationExpression(this SyntaxNode node)
+ {
+ var initializer = node as InitializerExpressionSyntax;
+ AnonymousObjectMemberDeclaratorSyntax anonymousObjectInitializer = null;
+ if (initializer == null)
+ {
+ anonymousObjectInitializer = node as AnonymousObjectMemberDeclaratorSyntax;
+ if (anonymousObjectInitializer == null)
+ {
+ return false;
+ }
+ }
+
+ var parent = initializer != null ? initializer.Parent : anonymousObjectInitializer.Parent;
+ if (parent is AnonymousObjectCreationExpressionSyntax)
+ {
+ return true;
+ }
+
+ if (parent is ObjectCreationExpressionSyntax)
+ {
+ if (initializer.Expressions.Count <= 0)
+ {
+ return true;
+ }
+
+ var expression = initializer.Expressions[0];
+ if (expression.Kind() == SyntaxKind.SimpleAssignmentExpression)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static bool IsInitializerForArrayOrCollectionCreationExpression(this SyntaxNode node)
+ {
+ var initializer = node as InitializerExpressionSyntax;
+ AnonymousObjectMemberDeclaratorSyntax anonymousObjectInitializer = null;
+ if (initializer == null)
+ {
+ anonymousObjectInitializer = node as AnonymousObjectMemberDeclaratorSyntax;
+ if (anonymousObjectInitializer == null)
+ {
+ return false;
+ }
+ }
+
+ var parent = initializer != null ? initializer.Parent : anonymousObjectInitializer.Parent;
+ if (parent is ArrayCreationExpressionSyntax ||
+ parent is ImplicitArrayCreationExpressionSyntax ||
+ parent is EqualsValueClauseSyntax ||
+ parent.Kind() == SyntaxKind.SimpleAssignmentExpression)
+ {
+ return true;
+ }
+
+ if (parent is ObjectCreationExpressionSyntax)
+ {
+ return !IsInitializerForObjectOrAnonymousObjectCreationExpression(initializer);
+ }
+
+ return false;
+ }
+
+ public static bool ParenOrBracketContainsNothing(this SyntaxToken token1, SyntaxToken token2)
+ {
+ return (token1.Kind() == SyntaxKind.OpenParenToken && token2.Kind() == SyntaxKind.CloseParenToken) ||
+ (token1.Kind() == SyntaxKind.OpenBracketToken && token2.Kind() == SyntaxKind.CloseBracketToken);
+ }
+
+ public static bool IsLastTokenInLabelStatement(this SyntaxToken token)
+ {
+ if (token.Kind() != SyntaxKind.SemicolonToken && token.Kind() != SyntaxKind.CloseBraceToken)
+ {
+ return false;
+ }
+
+ if (token.Parent == null)
+ {
+ return false;
+ }
+
+ return token.Parent.Parent is LabeledStatementSyntax;
+ }
+
+ public static ValueTuple<SyntaxToken, SyntaxToken> GetFirstAndLastMemberDeclarationTokensAfterAttributes(this MemberDeclarationSyntax node)
+ {
+ // Contract.ThrowIfNull(node);
+
+ // there are no attributes associated with the node. return back first and last token of the node.
+ var attributes = node.GetAttributes();
+ if (attributes.Count == 0)
+ {
+ return ValueTuple.Create(node.GetFirstToken(includeZeroWidth: true), node.GetLastToken(includeZeroWidth: true));
+ }
+
+ var lastToken = node.GetLastToken(includeZeroWidth: true);
+ var lastAttributeToken = attributes.Last().GetLastToken(includeZeroWidth: true);
+ if (lastAttributeToken.Equals(lastToken))
+ {
+ return ValueTuple.Create(default(SyntaxToken), default(SyntaxToken));
+ }
+
+ var firstTokenAfterAttribute = lastAttributeToken.GetNextToken(includeZeroWidth: true);
+
+ // there are attributes, get first token after the tokens belong to attributes
+ return ValueTuple.Create(firstTokenAfterAttribute, lastToken);
+ }
+
+ public static bool IsBlockBody(this SyntaxNode node)
+ {
+ // Contract.ThrowIfNull(node);
+
+ var blockNode = node as BlockSyntax;
+ if (blockNode == null)
+ {
+ return false;
+ }
+
+ switch (blockNode.Parent.Kind())
+ {
+ case SyntaxKind.AnonymousMethodExpression:
+ case SyntaxKind.CheckedStatement:
+ case SyntaxKind.UncheckedStatement:
+ case SyntaxKind.UnsafeStatement:
+ case SyntaxKind.TryStatement:
+ case SyntaxKind.CatchClause:
+ case SyntaxKind.FinallyClause:
+ case SyntaxKind.MethodDeclaration:
+ case SyntaxKind.OperatorDeclaration:
+ case SyntaxKind.ConversionOperatorDeclaration:
+ case SyntaxKind.ConstructorDeclaration:
+ case SyntaxKind.DestructorDeclaration:
+ case SyntaxKind.AddAccessorDeclaration:
+ case SyntaxKind.GetAccessorDeclaration:
+ case SyntaxKind.SetAccessorDeclaration:
+ case SyntaxKind.RemoveAccessorDeclaration:
+ case SyntaxKind.UnknownAccessorDeclaration:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public static bool IsPlusOrMinusExpression(this SyntaxToken token)
+ {
+ if (token.Kind() != SyntaxKind.PlusToken && token.Kind() != SyntaxKind.MinusToken)
+ {
+ return false;
+ }
+
+ return token.Parent is PrefixUnaryExpressionSyntax;
+ }
+
+ public static bool IsInterpolation(this SyntaxToken currentToken)
+ {
+ return currentToken.Parent.IsKind(SyntaxKind.Interpolation);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Formatter/FormattingOptionsFactory.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Formatter/FormattingOptionsFactory.cs
new file mode 100644
index 0000000000..950f735934
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Formatter/FormattingOptionsFactory.cs
@@ -0,0 +1,170 @@
+//
+// FormattingOptionsFactory.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2012 Xamarin Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using Microsoft.CodeAnalysis.Options;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Host;
+using Microsoft.CodeAnalysis.CSharp.Formatting;
+using Microsoft.CodeAnalysis.Formatting;
+
+namespace ICSharpCode.NRefactory6.CSharp
+{
+ /// <summary>
+ /// The formatting options factory creates pre defined formatting option styles.
+ /// </summary>
+ public static class FormattingOptionsFactory
+ {
+ readonly static Workspace defaultWs = new TestWorkspace ();
+
+ internal class TestWorkspace : Workspace
+ {
+ readonly static HostServices services = Microsoft.CodeAnalysis.Host.Mef.MefHostServices.DefaultHost;
+ public TestWorkspace(string workspaceKind = "Test") : base(services , workspaceKind)
+ {
+ }
+
+ }
+// /// <summary>
+// /// Creates empty CSharpFormatting options.
+// /// </summary>
+// public static CSharpFormattingOptions CreateEmpty()
+// {
+// return new CSharpFormattingOptions();
+// }
+
+ /// <summary>
+ /// Creates mono indent style CSharpFormatting options.
+ /// </summary>
+ public static OptionSet CreateMono()
+ {
+ var options = defaultWs.Options;
+ options = options.WithChangedOption(CSharpFormattingOptions.SpaceAfterMethodCallName, true);
+ options = options.WithChangedOption(CSharpFormattingOptions.SpaceAfterSemicolonsInForStatement, true);
+
+ options = options.WithChangedOption(CSharpFormattingOptions.NewLineForCatch, false);
+ options = options.WithChangedOption(CSharpFormattingOptions.NewLineForFinally, false);
+ options = options.WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInAnonymousMethods, false);
+ options = options.WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInControlBlocks, false);
+ options = options.WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInLambdaExpressionBody, false);
+
+ options = options.WithChangedOption(CSharpFormattingOptions.IndentSwitchSection, false);
+
+ options = options.WithChangedOption(FormattingOptions.UseTabs, LanguageNames.CSharp, true);
+ options = options.WithChangedOption(FormattingOptions.TabSize, LanguageNames.CSharp, 4);
+ options = options.WithChangedOption(FormattingOptions.NewLine, LanguageNames.CSharp, "\n");
+
+ return options;
+ }
+
+ /// <summary>
+ /// Creates sharp develop indent style CSharpFormatting options.
+ /// </summary>
+ public static OptionSet CreateSharpDevelop()
+ {
+ var baseOptions = CreateKRStyle();
+ return baseOptions;
+ }
+
+ /// <summary>
+ /// The K&R style, so named because it was used in Kernighan and Ritchie's book The C Programming Language,
+ /// is commonly used in C. It is less common for C++, C#, and others.
+ /// </summary>
+ public static OptionSet CreateKRStyle()
+ {
+ var options = defaultWs.Options;
+ options = options.WithChangedOption(CSharpFormattingOptions.NewLineForCatch, false);
+ options = options.WithChangedOption(CSharpFormattingOptions.NewLineForFinally, false);
+ options = options.WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInAnonymousMethods, false);
+ options = options.WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInControlBlocks, false);
+ options = options.WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInLambdaExpressionBody, false);
+
+ options = options.WithChangedOption(FormattingOptions.UseTabs, LanguageNames.CSharp, true);
+ options = options.WithChangedOption(FormattingOptions.TabSize, LanguageNames.CSharp, 4);
+ options = options.WithChangedOption(FormattingOptions.NewLine, LanguageNames.CSharp, "\n");
+
+ return options;
+ }
+
+ /// <summary>
+ /// Creates allman indent style CSharpFormatting options used in Visual Studio.
+ /// </summary>
+ public static OptionSet CreateAllman()
+ {
+ var options = defaultWs.Options;
+ options = options.WithChangedOption(FormattingOptions.UseTabs, LanguageNames.CSharp, true);
+ options = options.WithChangedOption(FormattingOptions.TabSize, LanguageNames.CSharp, 4);
+ options = options.WithChangedOption(FormattingOptions.NewLine, LanguageNames.CSharp, "\n");
+ return options;
+ }
+
+// /// <summary>
+// /// The Whitesmiths style, also called Wishart style to a lesser extent, is less common today than the previous three. It was originally used in the documentation for the first commercial C compiler, the Whitesmiths Compiler.
+// /// </summary>
+// public static CSharpFormattingOptions CreateWhitesmiths()
+// {
+// var baseOptions = CreateKRStyle();
+//
+// baseOptions.NamespaceBraceStyle = BraceStyle.NextLineShifted;
+// baseOptions.ClassBraceStyle = BraceStyle.NextLineShifted;
+// baseOptions.InterfaceBraceStyle = BraceStyle.NextLineShifted;
+// baseOptions.StructBraceStyle = BraceStyle.NextLineShifted;
+// baseOptions.EnumBraceStyle = BraceStyle.NextLineShifted;
+// baseOptions.MethodBraceStyle = BraceStyle.NextLineShifted;
+// baseOptions.ConstructorBraceStyle = BraceStyle.NextLineShifted;
+// baseOptions.DestructorBraceStyle = BraceStyle.NextLineShifted;
+// baseOptions.AnonymousMethodBraceStyle = BraceStyle.NextLineShifted;
+// baseOptions.PropertyBraceStyle = BraceStyle.NextLineShifted;
+// baseOptions.PropertyGetBraceStyle = BraceStyle.NextLineShifted;
+// baseOptions.PropertySetBraceStyle = BraceStyle.NextLineShifted;
+//
+// baseOptions.EventBraceStyle = BraceStyle.NextLineShifted;
+// baseOptions.EventAddBraceStyle = BraceStyle.NextLineShifted;
+// baseOptions.EventRemoveBraceStyle = BraceStyle.NextLineShifted;
+// baseOptions.StatementBraceStyle = BraceStyle.NextLineShifted;
+// baseOptions.IndentBlocksInsideExpressions = true;
+// return baseOptions;
+// }
+//
+// /// <summary>
+// /// Like the Allman and Whitesmiths styles, GNU style puts braces on a line by themselves, indented by 2 spaces,
+// /// except when opening a function definition, where they are not indented.
+// /// In either case, the contained code is indented by 2 spaces from the braces.
+// /// Popularised by Richard Stallman, the layout may be influenced by his background of writing Lisp code.
+// /// In Lisp the equivalent to a block (a progn)
+// /// is a first class data entity and giving it its own indent level helps to emphasize that,
+// /// whereas in C a block is just syntax.
+// /// Although not directly related to indentation, GNU coding style also includes a space before the bracketed
+// /// list of arguments to a function.
+// /// </summary>
+// public static CSharpFormattingOptions CreateGNU()
+// {
+// var baseOptions = CreateAllman();
+// baseOptions.StatementBraceStyle = BraceStyle.NextLineShifted2;
+// return baseOptions;
+// }
+ }
+}
+
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Formatter/FormattingRangeHelper.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Formatter/FormattingRangeHelper.cs
new file mode 100644
index 0000000000..a8a7cb7b91
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Formatter/FormattingRangeHelper.cs
@@ -0,0 +1,434 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Formatting;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Shared.Utilities;
+using Microsoft.CodeAnalysis.Text;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp
+{
+ /// <summary>
+ /// this help finding a range of tokens to format based on given ending token
+ /// </summary>
+ public static class FormattingRangeHelper
+ {
+ public static ValueTuple<SyntaxToken, SyntaxToken>? FindAppropriateRange(SyntaxToken endToken, bool useDefaultRange = true)
+ {
+ // Contract.ThrowIfTrue(endToken.Kind() == SyntaxKind.None);
+
+ return FixupOpenBrace(FindAppropriateRangeWorker(endToken, useDefaultRange));
+ }
+
+ private static ValueTuple<SyntaxToken, SyntaxToken>? FixupOpenBrace(ValueTuple<SyntaxToken, SyntaxToken>? tokenRange)
+ {
+ if (!tokenRange.HasValue)
+ {
+ return tokenRange;
+ }
+
+ // with a auto brace completion which will do auto formatting when a user types "{", it is quite common that we will automatically put a space
+ // between "{" and "}". but user might blindly type without knowing that " " has automatically inserted for him. and ends up have two spaces.
+ // for those cases, whenever we see previous token of the range is "{", we expand the range to include preceeding "{"
+ var currentToken = tokenRange.Value.Item1;
+ var previousToken = currentToken.GetPreviousToken();
+
+ while (currentToken.Kind() != SyntaxKind.CloseBraceToken && previousToken.Kind() == SyntaxKind.OpenBraceToken)
+ {
+ var pair = previousToken.Parent.GetBracePair();
+ if (pair.Item2.Kind() == SyntaxKind.None || !AreTwoTokensOnSameLine(previousToken, pair.Item2))
+ {
+ return ValueTuple.Create(currentToken, tokenRange.Value.Item2);
+ }
+
+ currentToken = previousToken;
+ previousToken = currentToken.GetPreviousToken();
+ }
+
+ return ValueTuple.Create(currentToken, tokenRange.Value.Item2);
+ }
+
+ private static ValueTuple<SyntaxToken, SyntaxToken>? FindAppropriateRangeWorker(SyntaxToken endToken, bool useDefaultRange)
+ {
+ // special token that we know how to find proper starting token
+ switch (endToken.Kind())
+ {
+ case SyntaxKind.CloseBraceToken:
+ {
+ return FindAppropriateRangeForCloseBrace(endToken);
+ }
+
+ case SyntaxKind.SemicolonToken:
+ {
+ return FindAppropriateRangeForSemicolon(endToken);
+ }
+
+ case SyntaxKind.ColonToken:
+ {
+ return FindAppropriateRangeForColon(endToken);
+ }
+
+ default:
+ {
+ // default case
+ if (!useDefaultRange)
+ {
+ return null;
+ }
+
+ // if given token is skipped token, don't bother to find appropriate
+ // starting point
+ if (endToken.Kind() == SyntaxKind.SkippedTokensTrivia)
+ {
+ return null;
+ }
+
+ var parent = endToken.Parent;
+ if (parent == null)
+ {
+ // if there is no parent setup yet, nothing we can do here.
+ return null;
+ }
+
+ // if we are called due to things in trivia or literals, don't bother
+ // finding a starting token
+ if (parent.Kind() == SyntaxKind.StringLiteralExpression ||
+ parent.Kind() == SyntaxKind.CharacterLiteralExpression)
+ {
+ return null;
+ }
+
+ // format whole node that containing the end token + its previous one
+ // to do indentation
+ return ValueTuple.Create(GetAppropriatePreviousToken(parent.GetFirstToken()), parent.GetLastToken());
+ }
+ }
+ }
+
+ private static ValueTuple<SyntaxToken, SyntaxToken>? FindAppropriateRangeForSemicolon(SyntaxToken endToken)
+ {
+ var parent = endToken.Parent;
+ if (parent == null || parent.Kind() == SyntaxKind.SkippedTokensTrivia)
+ {
+ return null;
+ }
+
+ if ((parent is UsingDirectiveSyntax) ||
+ (parent is DelegateDeclarationSyntax) ||
+ (parent is FieldDeclarationSyntax) ||
+ (parent is EventFieldDeclarationSyntax) ||
+ (parent is MethodDeclarationSyntax))
+ {
+ return ValueTuple.Create(GetAppropriatePreviousToken(parent.GetFirstToken(), canTokenBeFirstInABlock: true), parent.GetLastToken());
+ }
+
+ if (parent is AccessorDeclarationSyntax)
+ {
+ // if both accessors are on the same line, format the accessor list
+ // { get; set; }
+ var propertyDeclaration = GetEnclosingMember(endToken) as PropertyDeclarationSyntax;
+ if (propertyDeclaration != null &&
+ AreTwoTokensOnSameLine(propertyDeclaration.AccessorList.OpenBraceToken, propertyDeclaration.AccessorList.CloseBraceToken))
+ {
+ return ValueTuple.Create(propertyDeclaration.AccessorList.OpenBraceToken, propertyDeclaration.AccessorList.CloseBraceToken);
+ }
+
+ // otherwise, just format the accessor
+ return ValueTuple.Create(GetAppropriatePreviousToken(parent.GetFirstToken(), canTokenBeFirstInABlock: true), parent.GetLastToken());
+ }
+
+ if (parent is StatementSyntax && !endToken.IsSemicolonInForStatement())
+ {
+ var container = GetTopContainingNode(parent);
+ if (container == null)
+ {
+ return ValueTuple.Create(GetAppropriatePreviousToken(parent.GetFirstToken()), parent.GetLastToken());
+ }
+
+ if (IsSpecialContainingNode(container))
+ {
+ return ValueTuple.Create(GetAppropriatePreviousToken(container.GetFirstToken()), container.GetLastToken());
+ }
+
+ return ValueTuple.Create(GetAppropriatePreviousToken(parent.GetFirstToken(), canTokenBeFirstInABlock: true), parent.GetLastToken());
+ }
+
+ // don't do anything
+ return null;
+ }
+
+ private static ValueTuple<SyntaxToken, SyntaxToken>? FindAppropriateRangeForCloseBrace(SyntaxToken endToken)
+ {
+ // don't do anything if there is no proper parent
+ var parent = endToken.Parent;
+ if (parent == null || parent.Kind() == SyntaxKind.SkippedTokensTrivia)
+ {
+ return null;
+ }
+
+ // cases such as namespace, type, enum, method almost any top level elements
+ if (parent is MemberDeclarationSyntax ||
+ parent is SwitchStatementSyntax)
+ {
+ return ValueTuple.Create(GetAppropriatePreviousToken(parent.GetFirstToken()), parent.GetLastToken());
+ }
+
+ // property decl body or initializer
+ if (parent is AccessorListSyntax)
+ {
+ // include property decl
+ var containerOfList = parent.Parent;
+ if (containerOfList == null)
+ {
+ return ValueTuple.Create(GetAppropriatePreviousToken(parent.GetFirstToken()), parent.GetLastToken());
+ }
+
+ return ValueTuple.Create(containerOfList.GetFirstToken(), containerOfList.GetLastToken());
+ }
+
+ if (parent is AnonymousObjectCreationExpressionSyntax)
+ {
+ return ValueTuple.Create(parent.GetFirstToken(), parent.GetLastToken());
+ }
+
+ if (parent is InitializerExpressionSyntax)
+ {
+ var parentOfParent = parent.Parent;
+ if (parentOfParent == null)
+ {
+ return ValueTuple.Create(GetAppropriatePreviousToken(parent.GetFirstToken()), parent.GetLastToken());
+ }
+
+ // double initializer case such as
+ // { { }
+ var doubleInitializer = parentOfParent as InitializerExpressionSyntax;
+ if (doubleInitializer != null)
+ {
+ // if parent block has a missing brace, and current block is on same line, then
+ // don't try to indent inner block.
+ var firstTokenOfInnerBlock = parent.GetFirstToken();
+ var lastTokenOfInnerBlock = parent.GetLastToken();
+
+ var twoTokensOnSameLine = AreTwoTokensOnSameLine(firstTokenOfInnerBlock, lastTokenOfInnerBlock);
+ if (twoTokensOnSameLine)
+ {
+ return ValueTuple.Create(firstTokenOfInnerBlock, lastTokenOfInnerBlock);
+ }
+ }
+
+ // include owner of the initializer node such as creation node
+ return ValueTuple.Create(parentOfParent.GetFirstToken(), parentOfParent.GetLastToken());
+ }
+
+ if (parent is BlockSyntax)
+ {
+ var containerOfBlock = GetTopContainingNode(parent);
+ if (containerOfBlock == null)
+ {
+ return ValueTuple.Create(GetAppropriatePreviousToken(parent.GetFirstToken()), parent.GetLastToken());
+ }
+
+ // things like method, constructor, etc and special cases
+ if (containerOfBlock is MemberDeclarationSyntax ||
+ IsSpecialContainingNode(containerOfBlock))
+ {
+ return ValueTuple.Create(GetAppropriatePreviousToken(containerOfBlock.GetFirstToken()), containerOfBlock.GetLastToken());
+ }
+
+ // double block case on single line case
+ // { { }
+ if (containerOfBlock is BlockSyntax)
+ {
+ // if parent block has a missing brace, and current block is on same line, then
+ // don't try to indent inner block.
+ var firstTokenOfInnerBlock = parent.GetFirstToken();
+ var lastTokenOfInnerBlock = parent.GetLastToken();
+
+ var twoTokensOnSameLine = AreTwoTokensOnSameLine(firstTokenOfInnerBlock, lastTokenOfInnerBlock);
+ if (twoTokensOnSameLine)
+ {
+ return ValueTuple.Create(firstTokenOfInnerBlock, lastTokenOfInnerBlock);
+ }
+ }
+
+ // okay, for block, indent regardless whether it is first one on the line
+ return ValueTuple.Create(GetPreviousTokenIfNotFirstTokenInTree(parent.GetFirstToken()), parent.GetLastToken());
+ }
+
+ // don't do anything
+ return null;
+ }
+
+ private static ValueTuple<SyntaxToken, SyntaxToken>? FindAppropriateRangeForColon(SyntaxToken endToken)
+ {
+ // don't do anything if there is no proper parent
+ var parent = endToken.Parent;
+ if (parent == null || parent.Kind() == SyntaxKind.SkippedTokensTrivia)
+ {
+ return null;
+ }
+
+ // cases such as namespace, type, enum, method almost any top level elements
+ if (IsColonInSwitchLabel(endToken))
+ {
+ return ValueTuple.Create(GetPreviousTokenIfNotFirstTokenInTree(parent.GetFirstToken()), parent.GetLastToken());
+ }
+
+ return null;
+ }
+
+ private static SyntaxToken GetPreviousTokenIfNotFirstTokenInTree(SyntaxToken token)
+ {
+ var previousToken = token.GetPreviousToken();
+ return previousToken.Kind() == SyntaxKind.None ? token : previousToken;
+ }
+
+ private static bool AreTwoTokensOnSameLine(SyntaxToken token1, SyntaxToken token2)
+ {
+ var tree = token1.SyntaxTree;
+ var text = default(SourceText);
+ if (tree != null && tree.TryGetText(out text))
+ {
+ var line1 = text.Lines.IndexOf(token1.Span.End);
+ var line2 = text.Lines.IndexOf(token2.SpanStart);
+
+ return line1 == line2;
+ }
+
+ return CommonFormattingHelpers.GetTextBetween(token1, token2).ContainsLineBreak();
+ }
+
+ private static SyntaxToken GetAppropriatePreviousToken(SyntaxToken startToken, bool canTokenBeFirstInABlock = false)
+ {
+ var previousToken = startToken.GetPreviousToken();
+ if (previousToken.Kind() == SyntaxKind.None)
+ {
+ // no previous token, return as it is
+ return startToken;
+ }
+
+ if (AreTwoTokensOnSameLine(previousToken, startToken))
+ {
+ // The previous token can be '{' of a block and type declaration
+ // { int s = 0;
+ if (canTokenBeFirstInABlock)
+ {
+ if (IsOpenBraceTokenOfABlockOrTypeOrNamespace(previousToken))
+ {
+ return previousToken;
+ }
+ }
+
+ // there is another token on same line.
+ return startToken;
+ }
+
+ // start token is the first token on line
+
+ // now check a special case where previous token belongs to a label.
+ if (previousToken.IsLastTokenInLabelStatement())
+ {
+ var labelNode = previousToken.Parent.Parent;
+ return GetAppropriatePreviousToken(labelNode.GetFirstToken());
+ }
+
+ return previousToken;
+ }
+
+ private static bool IsOpenBraceTokenOfABlockOrTypeOrNamespace(SyntaxToken previousToken)
+ {
+ return previousToken.IsKind(SyntaxKind.OpenBraceToken) &&
+ (previousToken.Parent.IsKind(SyntaxKind.Block) ||
+ previousToken.Parent is TypeDeclarationSyntax ||
+ previousToken.Parent is NamespaceDeclarationSyntax);
+ }
+
+ private static bool IsSpecialContainingNode(SyntaxNode node)
+ {
+ return
+ node.Kind() == SyntaxKind.IfStatement ||
+ node.Kind() == SyntaxKind.ElseClause ||
+ node.Kind() == SyntaxKind.WhileStatement ||
+ node.Kind() == SyntaxKind.ForStatement ||
+ node.Kind() == SyntaxKind.ForEachStatement ||
+ node.Kind() == SyntaxKind.UsingStatement ||
+ node.Kind() == SyntaxKind.DoStatement ||
+ node.Kind() == SyntaxKind.TryStatement ||
+ node.Kind() == SyntaxKind.CatchClause ||
+ node.Kind() == SyntaxKind.FinallyClause ||
+ node.Kind() == SyntaxKind.LabeledStatement;
+ }
+
+ private static SyntaxNode GetTopContainingNode(SyntaxNode node)
+ {
+ node = node.Parent;
+ if (!IsSpecialContainingNode(node))
+ {
+ return node;
+ }
+
+ var lastSpecialContainingNode = node;
+ node = node.Parent;
+
+ while (node != null)
+ {
+ if (!IsSpecialContainingNode(node))
+ {
+ return lastSpecialContainingNode;
+ }
+
+ lastSpecialContainingNode = node;
+ node = node.Parent;
+ }
+
+ return null;
+ }
+
+ public static bool IsColonInSwitchLabel(SyntaxToken token)
+ {
+ var switchLabel = token.Parent as SwitchLabelSyntax;
+ return token.Kind() == SyntaxKind.ColonToken &&
+ switchLabel != null &&
+ switchLabel.ColonToken == token;
+ }
+
+ public static bool InBetweenTwoMembers(SyntaxToken previousToken, SyntaxToken currentToken)
+ {
+ if (previousToken.Kind() != SyntaxKind.SemicolonToken && previousToken.Kind() != SyntaxKind.CloseBraceToken)
+ {
+ return false;
+ }
+
+ if (currentToken.Kind() == SyntaxKind.CloseBraceToken)
+ {
+ return false;
+ }
+
+ var previousMember = GetEnclosingMember(previousToken);
+ var nextMember = GetEnclosingMember(currentToken);
+
+ return previousMember != null
+ && nextMember != null
+ && previousMember != nextMember;
+ }
+
+ public static MemberDeclarationSyntax GetEnclosingMember(SyntaxToken token)
+ {
+ if (token.Kind() == SyntaxKind.CloseBraceToken)
+ {
+ if (token.Parent.Kind() == SyntaxKind.Block ||
+ token.Parent.Kind() == SyntaxKind.AccessorList)
+ {
+ return token.Parent.Parent as MemberDeclarationSyntax;
+ }
+ }
+
+ return token.Parent.FirstAncestorOrSelf<MemberDeclarationSyntax>();
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Formatter/Indent.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Formatter/Indent.cs
new file mode 100644
index 0000000000..8945bcb262
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/Formatter/Indent.cs
@@ -0,0 +1,249 @@
+//
+// Indent.cs
+//
+// Author:
+// Mike Krüger <mkrueger@novell.com>
+//
+// Copyright (c) 2010 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.CodeAnalysis.Options;
+using Microsoft.CodeAnalysis.Formatting;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp
+{
+ public enum IndentType
+ {
+ Block,
+ DoubleBlock,
+ Continuation,
+ Alignment,
+ Label,
+ Empty
+ }
+
+ public class Indent
+ {
+ readonly CloneableStack<IndentType> indentStack = new CloneableStack<IndentType>();
+ readonly OptionSet options;
+ int curIndent;
+ int extraSpaces;
+ string indentString;
+
+ public int CurIndent {
+ get {
+ return curIndent;
+ }
+ }
+
+ public Indent(OptionSet options)
+ {
+ this.options = options;
+ Reset();
+ }
+
+ Indent(Indent engine)
+ {
+ this.indentStack = engine.indentStack.Clone();
+ this.options = engine.options;
+ this.curIndent = engine.curIndent;
+ this.extraSpaces = engine.extraSpaces;
+ this.indentString = engine.indentString;
+ }
+
+ public Indent Clone()
+ {
+ return new Indent(this);
+ }
+
+ public void Reset()
+ {
+ curIndent = 0;
+ indentString = "";
+ indentStack.Clear();
+ }
+
+ public void Push(IndentType type)
+ {
+ indentStack.Push(type);
+ curIndent += GetIndent(type);
+ Update();
+ }
+
+ public void Push(Indent indent)
+ {
+ foreach (var i in indent.indentStack)
+ Push(i);
+ }
+
+ public void Pop()
+ {
+ curIndent -= GetIndent(indentStack.Pop());
+ Update();
+ }
+
+ public bool PopIf(IndentType type)
+ {
+ if (Count > 0 && Peek() == type)
+ {
+ Pop();
+ return true;
+ }
+
+ return false;
+ }
+
+ public void PopWhile(IndentType type)
+ {
+ while (Count > 0 && Peek() == type)
+ {
+ Pop();
+ }
+ }
+
+ public bool PopTry()
+ {
+ if (Count > 0)
+ {
+ Pop();
+ return true;
+ }
+
+ return false;
+ }
+
+ public int Count {
+ get {
+ return indentStack.Count;
+ }
+ }
+
+ public IndentType Peek()
+ {
+ return indentStack.Peek();
+ }
+
+ int GetIndent(IndentType indentType)
+ {
+ switch (indentType) {
+ case IndentType.Block:
+ return options.GetOption(FormattingOptions.IndentationSize, LanguageNames.CSharp);
+ case IndentType.DoubleBlock:
+ return options.GetOption(FormattingOptions.IndentationSize, LanguageNames.CSharp) * 2;
+ case IndentType.Alignment:
+ case IndentType.Continuation:
+ return options.GetOption(FormattingOptions.IndentationSize, LanguageNames.CSharp);
+ case IndentType.Label:
+ return options.GetOption(FormattingOptions.IndentationSize, LanguageNames.CSharp);
+ case IndentType.Empty:
+ return 0;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ void Update()
+ {
+ if (!options.GetOption(FormattingOptions.UseTabs, LanguageNames.CSharp)) {
+ indentString = new string(' ', curIndent + ExtraSpaces);
+ return;
+ }
+ var tabSize = options.GetOption(FormattingOptions.TabSize, LanguageNames.CSharp);
+ indentString = new string('\t', curIndent / tabSize) + new string(' ', curIndent % tabSize) + new string(' ', ExtraSpaces);
+ }
+
+ public int ExtraSpaces {
+ get {
+ return extraSpaces;
+ }
+ set {
+ if (value < 0)
+ throw new ArgumentOutOfRangeException("ExtraSpaces >= 0 but was " + value);
+ extraSpaces = value;
+ Update();
+ }
+ }
+
+
+ public string IndentString {
+ get {
+ return indentString;
+ }
+ }
+
+ public override string ToString()
+ {
+ return string.Format("[Indent: curIndent={0}]", curIndent);
+ }
+
+ public Indent GetIndentWithoutSpace ()
+ {
+ var result = new Indent(options);
+ foreach (var i in indentStack)
+ result.Push(i);
+ return result;
+ }
+
+ public static Indent ConvertFrom(string indentString, Indent correctIndent, OptionSet options = null)
+ {
+ options = options ?? correctIndent.options;
+ var result = new Indent(options);
+
+ var indent = string.Concat(indentString.Where(c => c == ' ' || c == '\t'));
+ var indentTypes = new Stack<IndentType>(correctIndent.indentStack);
+
+ foreach (var _ in indent.TakeWhile(c => c == '\t'))
+ {
+ if (indentTypes.Count > 0)
+ result.Push(indentTypes.Pop());
+ else
+ result.Push(IndentType.Continuation);
+ }
+
+ result.ExtraSpaces = indent
+ .SkipWhile(c => c == '\t')
+ .TakeWhile(c => c == ' ')
+ .Count();
+
+ return result;
+ }
+
+ public void RemoveAlignment()
+ {
+ ExtraSpaces = 0;
+ if (Count > 0 && Peek() == IndentType.Alignment)
+ Pop();
+ }
+
+ public void SetAlignment(int i, bool forceSpaces = false)
+ {
+ var alignChars = Math.Max(0, i);
+ if (forceSpaces) {
+ ExtraSpaces = alignChars;
+ return;
+ }
+ RemoveAlignment();
+ Push(IndentType.Alignment);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/FrameworkLookup/FrameworkLookup.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/FrameworkLookup/FrameworkLookup.cs
new file mode 100644
index 0000000000..eaeba76ab5
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/FrameworkLookup/FrameworkLookup.cs
@@ -0,0 +1,490 @@
+//
+// FrameworkLookup.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2013 Xamarin Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using ICSharpCode.NRefactory6.Semantics;
+using ICSharpCode.NRefactory6.TypeSystem;
+using ICSharpCode.NRefactory6.TypeSystem.Implementation;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ /// <summary>
+ /// The framework lookup provides a fast lookup where an unknow type or extension method may be defined in.
+ /// </summary>
+ public sealed class FrameworkLookup
+ {
+ /* Binary format:
+ * [Header]
+ * [Version] : [Major (byte)] [Minor (byte)] [Build (byte)]
+ * [#Types (int)]
+ * [#Methods (int)]
+ * [#Assemblies (int)]
+ * [AssemblyListTable] : #Assemblies x [OffsetToAssemblyLists (int)]
+ * [TypeLookupTable] : #Types x ( [NameHash (int)] [AssemblyPtrToAssemblyListTable (ushort)]
+ * [ExtMethodLookupTable] : #Methods x ( [NameHash (int)] [AssemblyPtrToAssemblyListTable (ushort)]
+ * [AssemblyLists]
+ * [#Count (byte)]
+ * #Count x [AssemblyLookup] : [Package (string)] [FullName (string)] [Namespace (string)]
+ */
+ const int headerSize =
+ 3 + // Version
+ 4 + // #Types
+ 4 + // #Methods
+ 4 // #Assembly
+ /* + 4*/;
+
+ public static readonly Version CurrentVersion = new Version (2, 0, 1);
+ public static readonly FrameworkLookup Empty = new FrameworkLookup ();
+
+ string fileName;
+ int[] assemblyListTable;
+ int[] typeLookupTable;
+ int[] extLookupTable;
+
+ /// <summary>
+ /// This method tries to get a matching extension method.
+ /// </summary>
+ /// <returns>The extension method lookups.</returns>
+ /// <param name="resolveResult">The resolve result.</param>
+ public IEnumerable<AssemblyLookup> GetExtensionMethodLookups (UnknownMemberResolveResult resolveResult)
+ {
+ return GetLookup (resolveResult.MemberName, extLookupTable, headerSize + assemblyListTable.Length * 4 + typeLookupTable.Length * 8);
+ }
+
+ /// <summary>
+ /// Tries to get a type out of an unknow identifier result.
+ /// </summary>
+ /// <returns>The assemblies the type may be defined (if any).</returns>
+ /// <param name="resolveResult">The resolve result.</param>
+ /// <param name="typeParameterCount">Type parameter count.</param>
+ /// <param name="isInsideAttributeType">If set to <c>true</c> this resolve result may be inside an attribute.</param>
+ public IEnumerable<AssemblyLookup> GetLookups (UnknownIdentifierResolveResult resolveResult, int typeParameterCount, bool isInsideAttributeType)
+ {
+ string name = isInsideAttributeType ? resolveResult.Identifier + "Attribute" : resolveResult.Identifier;
+
+ var identifier = GetIdentifier (name, typeParameterCount);
+ return GetLookup (identifier, typeLookupTable, headerSize + assemblyListTable.Length * 4);
+ }
+
+ /// <summary>
+ /// Tries to get a type out of an unknow member resolve result. (In case of fully qualified names)
+ /// </summary>
+ /// <returns>The assemblies the type may be defined (if any).</returns>
+ /// <param name="resolveResult">The resolve result.</param>
+ /// <param name="fullMemberName"></param>
+ /// <param name="typeParameterCount">Type parameter count.</param>
+ /// <param name="isInsideAttributeType">If set to <c>true</c> this resolve result may be inside an attribute.</param>
+ public IEnumerable<AssemblyLookup> GetLookups (UnknownMemberResolveResult resolveResult, string fullMemberName, int typeParameterCount, bool isInsideAttributeType)
+ {
+ string name = isInsideAttributeType ? resolveResult.MemberName + "Attribute" : resolveResult.MemberName;
+
+ var identifier = GetIdentifier (name, typeParameterCount);
+ foreach (var lookup in GetLookup (identifier, typeLookupTable, headerSize + assemblyListTable.Length * 4)) {
+ if (fullMemberName.StartsWith (lookup.Namespace, StringComparison.Ordinal))
+ yield return lookup;
+ }
+ }
+
+ /// <summary>
+ /// The assembly lookup determines where a type might be defined.
+ /// It contains the assembly &amp; the namespace.
+ /// </summary>
+ public struct AssemblyLookup
+ {
+ readonly string nspace;
+
+ /// <summary>
+ /// The namespace the requested type is in.
+ /// </summary>
+ public string Namespace {
+ get {
+ return nspace;
+ }
+ }
+
+ readonly string fullName;
+ /// <summary>
+ /// Gets the full name af the assembly.
+ /// </summary>
+ public string FullName {
+ get {
+ return fullName;
+ }
+ }
+
+ readonly string package;
+ /// <summary>
+ /// Gets the package the assembly is in.
+ /// </summary>
+ public string Package {
+ get {
+ return package;
+ }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AssemblyLookup"/> struct.
+ /// </summary>
+ /// <param name="package">The package name.</param>
+ /// <param name="fullName">The full name of the assembly.</param>
+ /// <param name="nspace">The namespace the type is in.</param>
+ internal AssemblyLookup (string package, string fullName, string nspace)
+ {
+ if (nspace == null)
+ throw new ArgumentNullException ("nspace");
+ if (fullName == null)
+ throw new ArgumentNullException ("fullName");
+ this.package = package;
+ this.fullName = fullName;
+ this.nspace = nspace;
+ }
+
+ public override string ToString ()
+ {
+ return string.Format ("[AssemblyLookup: Namespace={0}, FullName={1}, Package={2}]", Namespace, FullName, Package);
+ }
+
+ public override bool Equals (object obj)
+ {
+ if (obj == null)
+ return false;
+ // if (ReferenceEquals (this, obj))
+ // return true;
+ if (obj.GetType () != typeof(AssemblyLookup))
+ return false;
+ var other = (AssemblyLookup)obj;
+ return Namespace == other.Namespace && FullName == other.FullName && Package == other.Package;
+ }
+
+ public override int GetHashCode ()
+ {
+ unchecked {
+ return (Namespace != null ? Namespace.GetHashCode () : 0) ^
+ (FullName != null ? FullName.GetHashCode () : 0) ^
+ (Package != null ? Package.GetHashCode () : 0);
+ }
+ }
+ }
+
+ /// <summary>
+ /// This method returns a new framework builder to build a new framework lookup data file.
+ /// </summary>
+ /// <param name="fileName">The file name of the data file.</param>
+ public static FrameworkBuilder Create (string fileName)
+ {
+ return new FrameworkBuilder (fileName);
+ }
+
+ /// <summary>
+ /// Loads a framework lookup object from a file. May return null, if the file wasn't found or has a version mismatch.
+ /// </summary>
+ /// <param name="fileName">File name.</param>
+ public static FrameworkLookup Load (string fileName)
+ {
+ try {
+ if (!File.Exists (fileName))
+ return null;
+ } catch (Exception) {
+ return null;
+ }
+ var result = new FrameworkLookup ();
+ result.fileName = fileName;
+ var fs = File.OpenRead (fileName);
+ using (var reader = new BinaryReader (fs, Encoding.UTF8)) {
+ var major = reader.ReadByte ();
+ var minor = reader.ReadByte ();
+ var build = reader.ReadByte ();
+ var version = new Version (major, minor, build);
+ if (version != CurrentVersion)
+ return null;
+ int typeLookupListCount = reader.ReadInt32 ();
+ int extLookupListCount = reader.ReadInt32 ();
+ int assemblyLookupCount = reader.ReadInt32 ();
+
+ result.assemblyListTable = new int[assemblyLookupCount];
+ for (int i = 0; i < assemblyLookupCount; i++) {
+ result.assemblyListTable[i] = reader.ReadInt32 ();
+ }
+
+ result.typeLookupTable = new int[typeLookupListCount];
+ for (int i = 0; i < typeLookupListCount; i++) {
+ result.typeLookupTable [i] = reader.ReadInt32 ();
+ // skip list offset
+ reader.ReadInt32 ();
+ }
+
+ result.extLookupTable = new int[extLookupListCount];
+ for (int i = 0; i < extLookupListCount; i++) {
+ result.extLookupTable [i] = reader.ReadInt32 ();
+ // skip list offset
+ reader.ReadInt32 ();
+ }
+ }
+ return result;
+ }
+
+ FrameworkLookup ()
+ {
+ }
+
+ IEnumerable<AssemblyLookup> GetLookup (string identifier, int[] lookupTable, int tableOffset)
+ {
+ if (lookupTable == null)
+ yield break;
+
+ int index = Array.BinarySearch (lookupTable, GetStableHashCode (identifier));
+ if (index < 0)
+ yield break;
+
+ using (var reader = new BinaryReader (File.Open (fileName, FileMode.Open, FileAccess.Read, FileShare.Read), Encoding.UTF8)) {
+ reader.BaseStream.Seek (tableOffset + index * 8 + 4, SeekOrigin.Begin);
+ int listPtr = reader.ReadInt32 ();
+
+ reader.BaseStream.Seek (listPtr, SeekOrigin.Begin);
+ var b = reader.ReadInt32 ();
+ var assemblies = new List<ushort> ();
+ while (b-- > 0) {
+ var assembly = reader.ReadUInt16 ();
+ if (assembly < 0 || assembly >= assemblyListTable.Length)
+ throw new InvalidDataException ("Assembly lookup was " + assembly + " but only " + assemblyListTable.Length + " are known.");
+ assemblies.Add (assembly);
+ }
+ foreach (var assembly in assemblies) {
+ reader.BaseStream.Seek (assemblyListTable [assembly], SeekOrigin.Begin);
+
+ var package = reader.ReadString ();
+ var fullName = reader.ReadString ();
+ var ns = reader.ReadString ();
+ yield return new AssemblyLookup (package, fullName, ns);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Retrieves a hash code for the specified string that is stable across
+ /// .NET upgrades.
+ ///
+ /// Use this method instead of the normal <c>string.GetHashCode</c> if the hash code
+ /// is persisted to disk.
+ /// </summary>
+ static int GetStableHashCode(string text)
+ {
+ unchecked {
+ int h = 0;
+ foreach (char c in text) {
+ h = (h << 5) - h + c;
+ }
+ return h;
+ }
+ }
+
+ static string GetIdentifier (string identifier, int tc)
+ {
+ if (tc == 0)
+ return identifier;
+ return identifier + "`" + tc;
+ }
+
+ public class FrameworkBuilder : IDisposable
+ {
+ readonly string fileName;
+
+ Dictionary<int, List<ushort>> typeLookup = new Dictionary<int, List<ushort>> ();
+ Dictionary<int, List<ushort>> extensionMethodLookup = new Dictionary<int, List<ushort>> ();
+ List<AssemblyLookup> assemblyLookups = new List<AssemblyLookup> ();
+ Dictionary<int, string> methodCheck = new Dictionary<int, string> ();
+ Dictionary<int, string> typeCheck = new Dictionary<int, string> ();
+
+ internal FrameworkBuilder (string fileName)
+ {
+ this.fileName = fileName;
+ }
+
+ static int[] WriteTable (MemoryStream stream, Dictionary<int, List<ushort>> table, out List<KeyValuePair<int, List<ushort>>> list)
+ {
+ list = new List<KeyValuePair<int, List<ushort>>> (table);
+ list.Sort ((x, y) => x.Key.CompareTo (y.Key));
+
+ var result = new int[list.Count];
+ using (var bw = new BinaryWriter (stream)) {
+ for (int i = 0; i < result.Length; i++) {
+ result [i] = (int)stream.Length;
+ bw.Write (list [i].Value.Count);
+ foreach (var ii in list [i].Value)
+ bw.Write (ii);
+ }
+ }
+
+ return result;
+ }
+
+ #region IDisposable implementation
+
+ void IDisposable.Dispose ()
+ {
+ var typeLookupMemory = new MemoryStream ();
+ List<KeyValuePair<int, List<ushort>>> typeLookupList;
+ var typeTable = WriteTable (typeLookupMemory, typeLookup, out typeLookupList);
+
+ var extMethodLookupMemory = new MemoryStream ();
+ List<KeyValuePair<int, List<ushort>>> extMethodLookuplist;
+ var extMethodTable = WriteTable (extMethodLookupMemory, extensionMethodLookup, out extMethodLookuplist);
+
+ var assemblyLookupMemory = new MemoryStream ();
+ var assemblyPositionTable = new int[assemblyLookups.Count];
+ using (var writer = new BinaryWriter (assemblyLookupMemory, Encoding.UTF8)) {
+ for (int i = 0; i < assemblyLookups.Count; i++) {
+ var lookup = assemblyLookups[i];
+ assemblyPositionTable[i] = (int)assemblyLookupMemory.Length;
+ writer.Write (lookup.Package);
+ writer.Write (lookup.FullName);
+ writer.Write (lookup.Namespace);
+ }
+ }
+
+ using (var stream = new BinaryWriter (File.OpenWrite (fileName), Encoding.UTF8)) {
+ stream.Write ((byte)CurrentVersion.Major);
+ stream.Write ((byte)CurrentVersion.Minor);
+ stream.Write ((byte)CurrentVersion.Build);
+
+ stream.Write (typeLookupList.Count);
+ stream.Write (extMethodLookuplist.Count);
+ stream.Write (assemblyLookups.Count);
+
+ var typeBuffer = typeLookupMemory.ToArray ();
+ var extMethodBuffer = extMethodLookupMemory.ToArray ();
+
+ int dataOffset =
+ headerSize +
+ assemblyLookups.Count * 4 +
+ typeLookupList.Count * (4 + 4) +
+ extMethodLookuplist.Count * (4 + 4);
+
+ for (int i = 0; i < assemblyLookups.Count; i++) {
+ stream.Write ((int)(dataOffset + typeBuffer.Length + extMethodBuffer.Length + assemblyPositionTable[i]));
+ }
+
+ for (int i = 0; i < typeLookupList.Count; i++) {
+ stream.Write (typeLookupList [i].Key);
+ stream.Write (dataOffset + typeTable[i]);
+ }
+
+ for (int i = 0; i < extMethodLookuplist.Count; i++) {
+ stream.Write (extMethodLookuplist [i].Key);
+ stream.Write (dataOffset + typeBuffer.Length + extMethodTable[i]);
+ }
+
+ stream.Write (typeBuffer);
+ stream.Write (extMethodBuffer);
+ stream.Write (assemblyLookupMemory.ToArray ());
+ stream.Flush ();
+ }
+ }
+ #endregion
+
+ struct FrameworkLookupId
+ {
+ public string PackageName;
+ public string AssemblyName;
+ public string NameSpace;
+ }
+
+ Dictionary<FrameworkLookupId, ushort> frameworkLookupTable = new Dictionary<FrameworkLookupId, ushort> ();
+ ushort GetLookup (string packageName, string assemblyName, string ns)
+ {
+ var id = new FrameworkLookupId {
+ PackageName = packageName,
+ AssemblyName = assemblyName,
+ NameSpace = ns
+ };
+ ushort value;
+ if (frameworkLookupTable.TryGetValue (id, out value))
+ return value;
+
+ var result = new AssemblyLookup (packageName, assemblyName, ns);
+ assemblyLookups.Add (result);
+ var index = assemblyLookups.Count - 1;
+ if (index > ushort.MaxValue)
+ throw new InvalidOperationException ("Assembly lookup list overflow > " + ushort.MaxValue + " assemblies.");
+ frameworkLookupTable.Add (id, (ushort)index);
+ return (ushort)index;
+ }
+
+ bool AddToTable (string packageName, string assemblyName, Dictionary<int, List<ushort>> table, Dictionary<int, string> checkTable, string id, string ns)
+ {
+ List<ushort> list;
+ var hash = GetStableHashCode (id);
+
+ if (!table.TryGetValue (hash, out list)) {
+ list = new List<ushort> ();
+ table [hash] = list;
+ } else {
+ string existingString;
+ if (checkTable.TryGetValue (hash, out existingString)) {
+ if (existingString != id)
+ throw new InvalidOperationException ("Duplicate hash for " + existingString + " and "+ id);
+ } else {
+ checkTable.Add (hash, id);
+ }
+ }
+ var assemblyLookup = GetLookup (packageName, assemblyName, ns);
+ if (!list.Any (a => a.Equals (assemblyLookup))) {
+ list.Add (assemblyLookup);
+ return true;
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Add a type to the framework lookup.
+ /// </summary>
+ /// <param name="packageName">The package the assembly of the type is defined (can be null).</param>
+ /// <param name="fullAssemblyName">The full assembly name the type is defined (needs to be != null).</param>
+ /// <param name="type">The type definition (needs to be != null).</param>
+ public void AddLookup (string packageName, string fullAssemblyName, IUnresolvedTypeDefinition type)
+ {
+ if (fullAssemblyName == null)
+ throw new ArgumentNullException ("fullAssemblyName");
+ if (type == null)
+ throw new ArgumentNullException ("type");
+ var id = GetIdentifier (type.Name, type.TypeParameters.Count);
+ if (AddToTable (packageName, fullAssemblyName, typeLookup, typeCheck, id, type.Namespace)) {
+ if (type.IsSealed || type.IsStatic) {
+ foreach (var method in type.Methods) {
+ var m = method as DefaultUnresolvedMethod;
+ if (m == null || !m.IsExtensionMethod)
+ continue;
+ AddToTable (packageName, fullAssemblyName, extensionMethodLookup, methodCheck, method.Name, method.DeclaringTypeDefinition.Namespace);
+ }
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateFromMembers/AbstractCodeRefactoringResult.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateFromMembers/AbstractCodeRefactoringResult.cs
new file mode 100644
index 0000000000..761a71fe6e
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateFromMembers/AbstractCodeRefactoringResult.cs
@@ -0,0 +1,31 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CodeRefactorings;
+using ICSharpCode.NRefactory6.CSharp.Refactoring;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateFromMembers
+{
+ public abstract class AbstractCodeRefactoringResult
+ {
+ private readonly CodeRefactoring _codeRefactoring;
+
+ protected AbstractCodeRefactoringResult(CodeRefactoring codeRefactoring)
+ {
+ _codeRefactoring = codeRefactoring;
+ }
+
+ public bool ContainsChanges
+ {
+ get
+ {
+ return _codeRefactoring != null;
+ }
+ }
+
+ public CodeRefactoring GetCodeRefactoring(CancellationToken cancellationToken)
+ {
+ return _codeRefactoring;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateFromMembers/AbstractGenerateFromMembersService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateFromMembers/AbstractGenerateFromMembersService.cs
new file mode 100644
index 0000000000..bfe16dad94
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateFromMembers/AbstractGenerateFromMembersService.cs
@@ -0,0 +1,160 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.CodeGeneration;
+using Microsoft.CodeAnalysis.CodeRefactorings;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Text;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+using ICSharpCode.NRefactory6.CSharp.Refactoring;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateFromMembers
+{
+ public abstract class AbstractGenerateFromMembersService<TMemberDeclarationSyntax>
+ where TMemberDeclarationSyntax : SyntaxNode
+ {
+ protected AbstractGenerateFromMembersService()
+ {
+ }
+
+ protected abstract Task<IList<TMemberDeclarationSyntax>> GetSelectedMembersAsync(Document document, TextSpan textSpan, CancellationToken cancellationToken);
+ protected abstract IEnumerable<ISymbol> GetDeclaredSymbols(SemanticModel semanticModel, TMemberDeclarationSyntax memberDeclaration, CancellationToken cancellationToken);
+
+ protected class SelectedMemberInfo
+ {
+ public INamedTypeSymbol ContainingType;
+ public IList<TMemberDeclarationSyntax> SelectedDeclarations;
+ public IList<ISymbol> SelectedMembers;
+ }
+
+ protected async Task<SelectedMemberInfo> GetSelectedMemberInfoAsync(
+ Document document, TextSpan textSpan, CancellationToken cancellationToken)
+ {
+ var selectedDeclarations = await this.GetSelectedMembersAsync(document, textSpan, cancellationToken).ConfigureAwait(false);
+
+ if (selectedDeclarations.Count > 0)
+ {
+ var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
+ var selectedMembers = selectedDeclarations.SelectMany(
+ d => this.GetDeclaredSymbols(semanticModel, d, cancellationToken)).WhereNotNull().ToList();
+ if (selectedMembers.Count > 0)
+ {
+ var containingType = selectedMembers.First().ContainingType;
+ if (containingType != null)
+ {
+ return new SelectedMemberInfo { ContainingType = containingType, SelectedDeclarations = selectedDeclarations, SelectedMembers = selectedMembers };
+ }
+ }
+ }
+
+ return null;
+ }
+
+ protected static bool IsWritableInstanceFieldOrProperty(ISymbol symbol)
+ {
+ // Can use non const fields and properties with setters in them.
+ return
+ IsInstanceFieldOrProperty(symbol) &&
+ IsWritableFieldOrProperty(symbol);
+ }
+
+ private static bool IsWritableFieldOrProperty(ISymbol symbol)
+ {
+ return symbol.TypeSwitch(
+ (IFieldSymbol field) => !field.IsConst,
+ (IPropertySymbol property) => property.SetMethod != null);
+ }
+
+ protected static bool IsInstanceFieldOrProperty(ISymbol symbol)
+ {
+ return !symbol.IsStatic && (IsField(symbol) || IsProperty(symbol));
+ }
+
+ private static bool IsProperty(ISymbol symbol)
+ {
+ return symbol.Kind == SymbolKind.Property;
+ }
+
+ private static bool IsField(ISymbol symbol)
+ {
+ return symbol.Kind == SymbolKind.Field;
+ }
+
+ protected CodeRefactoring CreateCodeRefactoring(
+ IList<TMemberDeclarationSyntax> selectedDeclarations,
+ IEnumerable<CodeAction> actions)
+ {
+ #if false
+ var lastDeclaration = selectedDeclarations.Last();
+ var endSpan = new TextSpan(lastDeclaration.Span.End - 1, 1);
+ return new CodeRefactoring(actions, endSpan);
+ #endif
+ return new CodeRefactoring(null, actions);
+ }
+
+ protected List<IParameterSymbol> DetermineParameters(
+ IList<ISymbol> selectedMembers)
+ {
+ var parameters = new List<IParameterSymbol>();
+
+ foreach (var symbol in selectedMembers)
+ {
+ var type = symbol is IFieldSymbol
+ ? ((IFieldSymbol)symbol).Type
+ : ((IPropertySymbol)symbol).Type;
+
+ parameters.Add(CodeGenerationSymbolFactory.CreateParameterSymbol(
+ attributes: null,
+ refKind: RefKind.None,
+ isParams: false,
+ type: type,
+ name: symbol.Name.ToCamelCase()));
+ }
+
+ return parameters;
+ }
+
+ protected IMethodSymbol GetDelegatedConstructor(
+ INamedTypeSymbol containingType,
+ List<IParameterSymbol> parameters)
+ {
+ var q =
+ from c in containingType.InstanceConstructors
+ orderby c.Parameters.Length descending
+ where c.Parameters.Length > 0 && c.Parameters.Length < parameters.Count
+ where c.Parameters.All(p => p.RefKind == RefKind.None) && !c.Parameters.Any(p => p.IsParams)
+ let constructorTypes = c.Parameters.Select(p => p.Type)
+ let symbolTypes = parameters.Take(c.Parameters.Length).Select(p => p.Type)
+ where constructorTypes.SequenceEqual(symbolTypes)
+ select c;
+
+ return q.FirstOrDefault();
+ }
+
+ protected bool HasMatchingConstructor(
+ INamedTypeSymbol containingType,
+ List<IParameterSymbol> parameters)
+ {
+ return containingType.InstanceConstructors.Any(c => MatchesConstructor(c, parameters));
+ }
+
+ private bool MatchesConstructor(
+ IMethodSymbol constructor,
+ List<IParameterSymbol> parameters)
+ {
+ return parameters.Select(p => p.Type).SequenceEqual(constructor.Parameters.Select(p => p.Type));
+ }
+
+ protected static readonly SymbolDisplayFormat SimpleFormat =
+ new SymbolDisplayFormat(
+ typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameOnly,
+ genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
+ parameterOptions: SymbolDisplayParameterOptions.IncludeParamsRefOut | SymbolDisplayParameterOptions.IncludeType,
+ miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateFromMembers/GenerateConstructor/AbstractGenerateConstructorService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateFromMembers/GenerateConstructor/AbstractGenerateConstructorService.cs
new file mode 100644
index 0000000000..f8e2bcfd82
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateFromMembers/GenerateConstructor/AbstractGenerateConstructorService.cs
@@ -0,0 +1,267 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.Internal.Log;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Editing;
+using ICSharpCode.NRefactory6.CSharp.CodeGeneration;
+using System.Linq;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateFromMembers.GenerateConstructor
+{
+ public abstract partial class AbstractGenerateConstructorService<TService, TMemberDeclarationSyntax> :
+ AbstractGenerateFromMembersService<TMemberDeclarationSyntax>
+ where TService : AbstractGenerateConstructorService<TService, TMemberDeclarationSyntax>
+ where TMemberDeclarationSyntax : SyntaxNode
+ {
+ protected AbstractGenerateConstructorService()
+ {
+ }
+
+ public async Task<GenerateConstructorResult> GenerateConstructorAsync(
+ Document document, TextSpan textSpan, CancellationToken cancellationToken)
+ {
+// using (Logger.LogBlock(FunctionId.Refactoring_GenerateFromMembers_GenerateConstructor, cancellationToken))
+// {
+ var info = await GetSelectedMemberInfoAsync(document, textSpan, cancellationToken).ConfigureAwait(false);
+ if (info != null)
+ {
+ var state = State.Generate((TService)this, document, textSpan, info.ContainingType, info.SelectedMembers, cancellationToken);
+ if (state != null)
+ {
+ return new GenerateConstructorResult(
+ CreateCodeRefactoring(info.SelectedDeclarations, GetCodeActions(document, state)));
+ }
+ }
+
+ return GenerateConstructorResult.Failure;
+// }
+ }
+
+ private IEnumerable<CodeAction> GetCodeActions(Document document, State state)
+ {
+ yield return new FieldDelegatingCodeAction((TService)this, document, state);
+ if (state.DelegatedConstructor != null)
+ {
+ yield return new ConstructorDelegatingCodeAction((TService)this, document, state);
+ }
+ }
+
+ private class ConstructorDelegatingCodeAction : CodeAction
+ {
+ private readonly TService _service;
+ private readonly Document _document;
+ private readonly State _state;
+
+ public ConstructorDelegatingCodeAction(
+ TService service,
+ Document document,
+ State state)
+ {
+ _service = service;
+ _document = document;
+ _state = state;
+ }
+
+ protected override async Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken)
+ {
+ // First, see if there are any constructors that would take the first 'n' arguments
+ // we've provided. If so, delegate to those, and then create a field for any
+ // remaining arguments. Try to match from largest to smallest.
+ //
+ // Otherwise, just generate a normal constructor that assigns any provided
+ // parameters into fields.
+ var provider = _document.Project.Solution.Workspace.Services.GetLanguageServices(_state.ContainingType.Language);
+ var factory = provider.GetService<SyntaxGenerator>();
+
+ var thisConstructorArguments = _state.DelegatedConstructor.Parameters.Select (par => factory.Argument (par.RefKind, SyntaxFactory.IdentifierName (par.Name))).ToList ();
+ var statements = new List<SyntaxNode>();
+
+ for (var i = _state.DelegatedConstructor.Parameters.Length; i < _state.Parameters.Count; i++)
+ {
+ var symbolName = _state.SelectedMembers[i].Name;
+ var parameterName = _state.Parameters[i].Name;
+ var assignExpression = factory.AssignmentStatement(
+ factory.MemberAccessExpression(
+ factory.ThisExpression(),
+ factory.IdentifierName(symbolName)),
+ factory.IdentifierName(parameterName));
+
+ var expressionStatement = factory.ExpressionStatement(assignExpression);
+ statements.Add(expressionStatement);
+ }
+
+ var syntaxTree = await _document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
+ var codeGenerationService = new CSharpCodeGenerationService (_document.Project.Solution.Workspace.Services.GetLanguageServices (LanguageNames.CSharp));
+ var result = await codeGenerationService.AddMethodAsync(
+ _document.Project.Solution,
+ _state.ContainingType,
+ CodeGenerationSymbolFactory.CreateConstructorSymbol(
+ attributes: null,
+ accessibility: Accessibility.Public,
+ modifiers: new DeclarationModifiers(),
+ typeName: _state.ContainingType.Name,
+ parameters: _state.Parameters,
+ statements: statements,
+ thisConstructorArguments: thisConstructorArguments),
+ new CodeGenerationOptions(contextLocation: syntaxTree.GetLocation(_state.TextSpan), generateDefaultAccessibility: false),
+ cancellationToken: cancellationToken)
+ .ConfigureAwait(false);
+
+ return result;
+ }
+
+ public override string Title
+ {
+ get
+ {
+ // var symbolDisplayService = _document.GetLanguageService<ISymbolDisplayService>();
+ var parameters = _state.Parameters.Select(p => p.ToDisplayString(SimpleFormat));
+ var parameterString = string.Join(", ", parameters);
+
+ return string.Format(Resources.GenerateDelegatingConstructor,
+ _state.ContainingType.Name, parameterString);
+ }
+ }
+ }
+
+ private class FieldDelegatingCodeAction : CodeAction
+ {
+ private readonly TService _service;
+ private readonly Document _document;
+ private readonly State _state;
+
+ public FieldDelegatingCodeAction(
+ TService service,
+ Document document,
+ State state)
+ {
+ _service = service;
+ _document = document;
+ _state = state;
+ }
+
+ protected override async Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken)
+ {
+ // First, see if there are any constructors that would take the first 'n' arguments
+ // we've provided. If so, delegate to those, and then create a field for any
+ // remaining arguments. Try to match from largest to smallest.
+ //
+ // Otherwise, just generate a normal constructor that assigns any provided
+ // parameters into fields.
+ var parameterToExistingFieldMap = new Dictionary<string, ISymbol>();
+ for (int i = 0; i < _state.Parameters.Count; i++)
+ {
+ parameterToExistingFieldMap[_state.Parameters[i].Name] = _state.SelectedMembers[i];
+ }
+
+ var factory = _document.GetLanguageService<SyntaxGenerator>();
+
+ var syntaxTree = await _document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
+ var members = factory.CreateFieldDelegatingConstructor(
+ _state.ContainingType.Name,
+ _state.ContainingType,
+ _state.Parameters,
+ parameterToExistingFieldMap,
+ parameterToNewFieldMap: null,
+ cancellationToken: cancellationToken);
+ var codeGenerationService = new CSharpCodeGenerationService (_document.Project.Solution.Workspace.Services.GetLanguageServices (LanguageNames.CSharp));
+
+ var result = await codeGenerationService.AddMembersAsync(
+ _document.Project.Solution,
+ _state.ContainingType,
+ members,
+ new CodeGenerationOptions(contextLocation: syntaxTree.GetLocation(_state.TextSpan), generateDefaultAccessibility: false),
+ cancellationToken)
+ .ConfigureAwait(false);
+
+ return result;
+ }
+
+
+ public override string Title
+ {
+ get
+ {
+ var parameters = _state.Parameters.Select(p => p.ToDisplayString(SimpleFormat));
+ var parameterString = string.Join(", ", parameters);
+
+ if (_state.DelegatedConstructor == null)
+ {
+ return string.Format(Resources.GenerateConstructor,
+ _state.ContainingType.Name, parameterString);
+ }
+ else
+ {
+ return string.Format(Resources.GenerateFieldAssigningConstructor,
+ _state.ContainingType.Name, parameterString);
+ }
+ }
+ }
+ }
+
+
+ private class State
+ {
+ public TextSpan TextSpan { get; private set; }
+ public IMethodSymbol DelegatedConstructor { get; private set; }
+ public INamedTypeSymbol ContainingType { get; private set; }
+ public IList<ISymbol> SelectedMembers { get; private set; }
+ public List<IParameterSymbol> Parameters { get; private set; }
+
+ public static State Generate(
+ TService service,
+ Document document,
+ TextSpan textSpan,
+ INamedTypeSymbol containingType,
+ IList<ISymbol> selectedMembers,
+ CancellationToken cancellationToken)
+ {
+ var state = new State();
+ if (!state.TryInitialize(service, document, textSpan, containingType, selectedMembers, cancellationToken))
+ {
+ return null;
+ }
+
+ return state;
+ }
+
+ private bool TryInitialize(
+ TService service,
+ Document document,
+ TextSpan textSpan,
+ INamedTypeSymbol containingType,
+ IList<ISymbol> selectedMembers,
+ CancellationToken cancellationToken)
+ {
+ if (!selectedMembers.All(IsWritableInstanceFieldOrProperty))
+ {
+ return false;
+ }
+
+ this.SelectedMembers = selectedMembers;
+ this.ContainingType = containingType;
+ this.TextSpan = textSpan;
+ if (this.ContainingType == null || this.ContainingType.TypeKind == TypeKind.Interface)
+ {
+ return false;
+ }
+
+ this.Parameters = service.DetermineParameters(selectedMembers);
+
+ if (service.HasMatchingConstructor(this.ContainingType, this.Parameters))
+ {
+ return false;
+ }
+
+ this.DelegatedConstructor = service.GetDelegatedConstructor(this.ContainingType, this.Parameters);
+ return true;
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateFromMembers/GenerateConstructor/CSharpGenerateConstructorService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateFromMembers/GenerateConstructor/CSharpGenerateConstructorService.cs
new file mode 100644
index 0000000000..86667889c2
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateFromMembers/GenerateConstructor/CSharpGenerateConstructorService.cs
@@ -0,0 +1,53 @@
+//
+// CSharpGenerateConstructorService.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 Microsoft.CodeAnalysis.CSharp.Syntax;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+using System.Threading;
+using System.Collections.Generic;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateFromMembers.GenerateConstructor
+{
+ public class CSharpGenerateConstructorService :
+ AbstractGenerateConstructorService<CSharpGenerateConstructorService, MemberDeclarationSyntax>
+ {
+ protected override Task<IList<MemberDeclarationSyntax>> GetSelectedMembersAsync(
+ Document document, TextSpan textSpan, CancellationToken cancellationToken)
+ {
+ return GenerateFromMembersHelpers.GetSelectedMembersAsync(document, textSpan, cancellationToken);
+ }
+
+ protected override IEnumerable<ISymbol> GetDeclaredSymbols(
+ SemanticModel semanticModel, MemberDeclarationSyntax memberDeclaration, CancellationToken cancellationToken)
+ {
+ return GenerateFromMembersHelpers.GetDeclaredSymbols(semanticModel, memberDeclaration, cancellationToken);
+ }
+ }
+
+}
+
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateFromMembers/GenerateConstructor/GenerateConstructorResult.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateFromMembers/GenerateConstructor/GenerateConstructorResult.cs
new file mode 100644
index 0000000000..597871d6b7
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateFromMembers/GenerateConstructor/GenerateConstructorResult.cs
@@ -0,0 +1,17 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.CodeAnalysis.CodeRefactorings;
+using ICSharpCode.NRefactory6.CSharp.Refactoring;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateFromMembers.GenerateConstructor
+{
+ public class GenerateConstructorResult : AbstractCodeRefactoringResult
+ {
+ public static readonly GenerateConstructorResult Failure = new GenerateConstructorResult(null);
+
+ public GenerateConstructorResult(CodeRefactoring codeRefactoring)
+ : base(codeRefactoring)
+ {
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateFromMembers/GenerateFromMembersHelpers.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateFromMembers/GenerateFromMembersHelpers.cs
new file mode 100644
index 0000000000..233b242c45
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateFromMembers/GenerateFromMembersHelpers.cs
@@ -0,0 +1,36 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Text;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateFromMembers
+{
+ internal static class GenerateFromMembersHelpers
+ {
+ internal static async Task<IList<MemberDeclarationSyntax>> GetSelectedMembersAsync(
+ Document document, TextSpan textSpan, CancellationToken cancellationToken)
+ {
+ var tree = await document.GetCSharpSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
+ return tree.GetMembersInSpan(textSpan, cancellationToken);
+ }
+
+ internal static IEnumerable<ISymbol> GetDeclaredSymbols(SemanticModel semanticModel, MemberDeclarationSyntax memberDeclaration, CancellationToken cancellationToken)
+ {
+ if (memberDeclaration is FieldDeclarationSyntax)
+ {
+ return ((FieldDeclarationSyntax)memberDeclaration).Declaration.Variables.Select(
+ v => semanticModel.GetDeclaredSymbol(v, cancellationToken));
+ }
+
+ return SpecializedCollections.SingletonEnumerable(
+ semanticModel.GetDeclaredSymbol(memberDeclaration, cancellationToken));
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/AbstractCodeRefactoringResult.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/AbstractCodeRefactoringResult.cs
new file mode 100644
index 0000000000..6b86b6cb09
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/AbstractCodeRefactoringResult.cs
@@ -0,0 +1,31 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using Microsoft.CodeAnalysis.CodeRefactorings;
+using ICSharpCode.NRefactory6.CSharp.Refactoring;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateMember
+{
+ public abstract class AbstractCodeRefactoringResult
+ {
+ private readonly CodeRefactoring _codeRefactoring;
+
+ protected AbstractCodeRefactoringResult(CodeRefactoring codeRefactoring)
+ {
+ _codeRefactoring = codeRefactoring;
+ }
+
+ public bool ContainsChanges
+ {
+ get
+ {
+ return _codeRefactoring != null;
+ }
+ }
+
+ public CodeRefactoring GetCodeRefactoring(CancellationToken cancellationToken)
+ {
+ return _codeRefactoring;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/AbstractGenerateMemberService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/AbstractGenerateMemberService.cs
new file mode 100644
index 0000000000..721f7e6560
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/AbstractGenerateMemberService.cs
@@ -0,0 +1,138 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateMember
+{
+ public abstract partial class AbstractGenerateMemberService<TSimpleNameSyntax, TExpressionSyntax>
+ where TSimpleNameSyntax : TExpressionSyntax
+ where TExpressionSyntax : SyntaxNode
+ {
+ protected AbstractGenerateMemberService()
+ {
+ }
+
+ protected static readonly ISet<TypeKind> EnumType = new HashSet<TypeKind> { TypeKind.Enum };
+ protected static readonly ISet<TypeKind> ClassInterfaceModuleStructTypes = new HashSet<TypeKind>
+ {
+ TypeKind.Class,
+ TypeKind.Module,
+ TypeKind.Struct,
+ TypeKind.Interface
+ };
+
+ protected bool ValidateTypeToGenerateIn(
+ Solution solution,
+ INamedTypeSymbol typeToGenerateIn,
+ bool isStatic,
+ ISet<TypeKind> typeKinds,
+ CancellationToken cancellationToken)
+ {
+ if (typeToGenerateIn == null)
+ {
+ return false;
+ }
+
+ if (typeToGenerateIn.IsAnonymousType)
+ {
+ return false;
+ }
+
+ if (!typeKinds.Contains(typeToGenerateIn.TypeKind))
+ {
+ return false;
+ }
+
+ if (typeToGenerateIn.TypeKind == TypeKind.Interface && isStatic)
+ {
+ return false;
+ }
+
+ // TODO(cyrusn): Make sure that there is a totally visible part somewhere (i.e.
+ // venus) that we can generate into.
+ var locations = typeToGenerateIn.Locations;
+ return locations.Any(loc => loc.IsInSource);
+ }
+
+ protected bool TryDetermineTypeToGenerateIn(
+ SemanticDocument document,
+ INamedTypeSymbol containingType,
+ TExpressionSyntax simpleNameOrMemberAccessExpression,
+ CancellationToken cancellationToken,
+ out INamedTypeSymbol typeToGenerateIn,
+ out bool isStatic)
+ {
+ typeToGenerateIn = null;
+ isStatic = false;
+
+ var semanticModel = document.SemanticModel;
+ var isMemberAccessExpression = simpleNameOrMemberAccessExpression.IsMemberAccessExpression();
+ if (isMemberAccessExpression ||
+ simpleNameOrMemberAccessExpression.IsConditionalMemberAccessExpression())
+ {
+ var beforeDotExpression = isMemberAccessExpression ?
+ simpleNameOrMemberAccessExpression.GetExpressionOfMemberAccessExpression() :
+ simpleNameOrMemberAccessExpression.GetExpressionOfConditionalMemberAccessExpression();
+ if (beforeDotExpression != null)
+ {
+ var typeInfo = semanticModel.GetTypeInfo(beforeDotExpression, cancellationToken);
+ var semanticInfo = semanticModel.GetSymbolInfo(beforeDotExpression, cancellationToken);
+
+ typeToGenerateIn = typeInfo.Type is ITypeParameterSymbol
+ ? ((ITypeParameterSymbol)typeInfo.Type).GetNamedTypeSymbolConstraint()
+ : typeInfo.Type as INamedTypeSymbol;
+
+ isStatic = semanticInfo.Symbol is INamedTypeSymbol;
+ }
+ }
+ else if (simpleNameOrMemberAccessExpression.IsPointerMemberAccessExpression())
+ {
+ var beforeArrowExpression = simpleNameOrMemberAccessExpression.GetExpressionOfMemberAccessExpression();
+ if (beforeArrowExpression != null)
+ {
+ var typeInfo = semanticModel.GetTypeInfo(beforeArrowExpression, cancellationToken);
+
+ if (typeInfo.Type.IsPointerType())
+ {
+ typeToGenerateIn = ((IPointerTypeSymbol)typeInfo.Type).PointedAtType as INamedTypeSymbol;
+ isStatic = false;
+ }
+ }
+ }
+ else if (simpleNameOrMemberAccessExpression.IsAttributeNamedArgumentIdentifier())
+ {
+ var attributeNode = simpleNameOrMemberAccessExpression.GetAncestors().FirstOrDefault(CSharpSyntaxFactsService.IsAttribute);
+ var attributeName = attributeNode.GetNameOfAttribute();
+ var attributeType = semanticModel.GetTypeInfo(attributeName, cancellationToken);
+
+ typeToGenerateIn = attributeType.Type as INamedTypeSymbol;
+ isStatic = false;
+ }
+ else if (simpleNameOrMemberAccessExpression.IsObjectInitializerNamedAssignmentIdentifier())
+ {
+ var objectCreationNode = simpleNameOrMemberAccessExpression.GetAncestors().FirstOrDefault(CSharpSyntaxFactsService.IsObjectCreationExpression);
+ typeToGenerateIn = semanticModel.GetTypeInfo(objectCreationNode, cancellationToken).Type as INamedTypeSymbol;
+ isStatic = false;
+ }
+ else
+ {
+ // Generating into the containing type.
+ typeToGenerateIn = containingType;
+ isStatic = simpleNameOrMemberAccessExpression.IsInStaticContext();
+ }
+
+ if (typeToGenerateIn != null)
+ {
+ typeToGenerateIn = typeToGenerateIn.OriginalDefinition;
+ }
+
+ return typeToGenerateIn != null;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateConstructor/AbstractGenerateConstructorService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateConstructor/AbstractGenerateConstructorService.cs
new file mode 100644
index 0000000000..6f7df24395
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateConstructor/AbstractGenerateConstructorService.cs
@@ -0,0 +1,703 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.Internal.Log;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+using System.Linq;
+using Microsoft.CodeAnalysis.Editing;
+using ICSharpCode.NRefactory6.CSharp.CodeGeneration;
+using System;
+using Microsoft.CodeAnalysis.FindSymbols;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateMember.GenerateConstructor
+{
+ public abstract partial class AbstractGenerateConstructorService<TService, TArgumentSyntax, TAttributeArgumentSyntax>
+ where TService : AbstractGenerateConstructorService<TService, TArgumentSyntax, TAttributeArgumentSyntax>
+ where TArgumentSyntax : SyntaxNode
+ where TAttributeArgumentSyntax : SyntaxNode
+ {
+
+ protected AbstractGenerateConstructorService()
+ {
+ }
+
+ protected abstract bool IsSimpleNameGeneration(SemanticDocument document, SyntaxNode node, CancellationToken cancellationToken);
+ protected abstract bool IsConstructorInitializerGeneration(SemanticDocument document, SyntaxNode node, CancellationToken cancellationToken);
+
+ protected abstract bool TryInitializeConstructorInitializerGeneration(SemanticDocument document, SyntaxNode constructorInitializer, CancellationToken cancellationToken, out SyntaxToken token, out IList<TArgumentSyntax> arguments, out INamedTypeSymbol typeToGenerateIn);
+ protected abstract bool TryInitializeSimpleNameGenerationState(SemanticDocument document, SyntaxNode simpleName, CancellationToken cancellationToken, out SyntaxToken token, out IList<TArgumentSyntax> arguments, out INamedTypeSymbol typeToGenerateIn);
+ protected abstract bool TryInitializeSimpleAttributeNameGenerationState(SemanticDocument document, SyntaxNode simpleName, CancellationToken cancellationToken, out SyntaxToken token, out IList<TArgumentSyntax> arguments, out IList<TAttributeArgumentSyntax> attributeArguments, out INamedTypeSymbol typeToGenerateIn);
+
+ protected abstract IList<string> GenerateParameterNames(SemanticModel semanticModel, IEnumerable<TArgumentSyntax> arguments, IList<string> reservedNames = null);
+ protected virtual IList<string> GenerateParameterNames(SemanticModel semanticModel, IEnumerable<TAttributeArgumentSyntax> arguments, IList<string> reservedNames = null) { return null; }
+ protected abstract string GenerateNameForArgument(SemanticModel semanticModel, TArgumentSyntax argument);
+ protected virtual string GenerateNameForArgument(SemanticModel semanticModel, TAttributeArgumentSyntax argument) { return null; }
+ protected abstract RefKind GetRefKind(TArgumentSyntax argument);
+ protected abstract bool IsNamedArgument(TArgumentSyntax argument);
+ protected abstract ITypeSymbol GetArgumentType(SemanticModel semanticModel, TArgumentSyntax argument, CancellationToken cancellationToken);
+ protected virtual ITypeSymbol GetAttributeArgumentType(SemanticModel semanticModel, TAttributeArgumentSyntax argument, CancellationToken cancellationToken) { return null; }
+
+ public async Task<IEnumerable<CodeAction>> GenerateConstructorAsync(Document document, SyntaxNode node, CancellationToken cancellationToken)
+ {
+ var semanticDocument = await SemanticDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false);
+
+ var state = await State.GenerateAsync((TService)this, semanticDocument, node, cancellationToken).ConfigureAwait(false);
+ if (state == null)
+ {
+ return SpecializedCollections.EmptyEnumerable<CodeAction>();
+ }
+
+ return GetActions(document, state);
+ }
+
+ private IEnumerable<CodeAction> GetActions(Document document, State state)
+ {
+ yield return new GenerateConstructorCodeAction((TService)this, document, state);
+ }
+
+ private class GenerateConstructorCodeAction : CodeAction
+ {
+ private readonly State _state;
+ private readonly Document _document;
+ private readonly TService _service;
+
+ public GenerateConstructorCodeAction(
+ TService service,
+ Document document,
+ State state)
+ {
+ _service = service;
+ _document = document;
+ _state = state;
+ }
+
+ protected override async Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken)
+ {
+ var semanticDocument = await SemanticDocument.CreateAsync(_document, cancellationToken).ConfigureAwait(false);
+ var editor = new Editor(_service, semanticDocument, _state, cancellationToken);
+ return await editor.GetEditAsync().ConfigureAwait(false);
+ }
+
+ public override string Title
+ {
+ get
+ {
+ return string.Format(Resources.GenerateNewConstructorIn,
+ _state.TypeToGenerateIn.Name);
+ }
+ }
+ }
+
+ protected abstract bool IsConversionImplicit(Compilation compilation, ITypeSymbol sourceType, ITypeSymbol targetType);
+
+ internal abstract IMethodSymbol GetDelegatingConstructor(State state, SemanticDocument document, int argumentCount, INamedTypeSymbol namedType, ISet<IMethodSymbol> candidates, CancellationToken cancellationToken);
+
+ private partial class Editor
+ {
+ private readonly TService _service;
+ private readonly SemanticDocument _document;
+ private readonly State _state;
+ private readonly CancellationToken _cancellationToken;
+
+ public Editor(
+ TService service,
+ SemanticDocument document,
+ State state,
+ CancellationToken cancellationToken)
+ {
+ _service = service;
+ _document = document;
+ _state = state;
+ _cancellationToken = cancellationToken;
+ }
+
+ internal async Task<Document> GetEditAsync()
+ {
+ // First, see if there's an accessible base constructor that would accept these
+ // types, then just call into that instead of generating fields.
+ //
+ // then, see if there are any constructors that would take the first 'n' arguments
+ // we've provided. If so, delegate to those, and then create a field for any
+ // remaining arguments. Try to match from largest to smallest.
+ //
+ // Otherwise, just generate a normal constructor that assigns any provided
+ // parameters into fields.
+
+ var edit = await GenerateThisOrBaseDelegatingConstructorAsync().ConfigureAwait(false);
+ if (edit != null)
+ {
+ return edit;
+ }
+
+ return await GenerateFieldDelegatingConstructorAsync().ConfigureAwait(false);
+ }
+
+ private async Task<Document> GenerateThisOrBaseDelegatingConstructorAsync()
+ {
+ // We don't have to deal with the zero length case, since there's nothing to
+ // delegate. It will fall out of the GenerateFieldDelegatingConstructor above.
+ for (int i = _state.Arguments.Count; i >= 1; i--)
+ {
+ var edit = await GenerateThisOrBaseDelegatingConstructorAsync(i).ConfigureAwait(false);
+ if (edit != null)
+ {
+ return edit;
+ }
+ }
+
+ return null;
+ }
+
+ private async Task<Document> GenerateThisOrBaseDelegatingConstructorAsync(int argumentCount)
+ {
+ Document edit;
+ if ((edit = await GenerateDelegatingConstructorAsync(argumentCount, _state.TypeToGenerateIn).ConfigureAwait(false)) != null ||
+ (edit = await GenerateDelegatingConstructorAsync(argumentCount, _state.TypeToGenerateIn.BaseType).ConfigureAwait(false)) != null)
+ {
+ return edit;
+ }
+
+ return null;
+ }
+
+ private async Task<Document> GenerateDelegatingConstructorAsync(
+ int argumentCount,
+ INamedTypeSymbol namedType)
+ {
+ if (namedType == null)
+ {
+ return null;
+ }
+
+ // We can't resolve overloads across language.
+ if (_document.Project.Language != namedType.Language)
+ {
+ return null;
+ }
+
+ var arguments = _state.Arguments.Take(argumentCount).ToList();
+ var remainingArguments = _state.Arguments.Skip(argumentCount).ToList();
+ var remainingAttributeArguments = _state.AttributeArguments != null ? _state.AttributeArguments.Skip(argumentCount).ToList() : null;
+ var remainingParameterTypes = _state.ParameterTypes.Skip(argumentCount).ToList();
+
+ var instanceConstructors = namedType.InstanceConstructors.Where(IsSymbolAccessible).ToSet();
+ if (instanceConstructors.IsEmpty())
+ {
+ return null;
+ }
+
+ var delegatedConstructor = _service.GetDelegatingConstructor(_state, _document, argumentCount, namedType, instanceConstructors, _cancellationToken);
+ if (delegatedConstructor == null)
+ {
+ return null;
+ }
+
+ // There was a best match. Call it directly.
+ var provider = _document.Project.Solution.Workspace.Services.GetLanguageServices(_state.TypeToGenerateIn.Language);
+ var syntaxFactory = provider.GetService<SyntaxGenerator>();
+ var codeGenerationService = new CSharpCodeGenerationService (_document.Project.Solution.Workspace);
+
+ // Map the first N parameters to the other constructor in this type. Then
+ // try to map any further parameters to existing fields. Finally, generate
+ // new fields if no such parameters exist.
+
+ // Find the names of the parameters that will follow the parameters we're
+ // delegating.
+ var remainingParameterNames = _service.GenerateParameterNames(
+ _document.SemanticModel, remainingArguments, delegatedConstructor.Parameters.Select(p => p.Name).ToList());
+
+ // Can't generate the constructor if the parameter names we're copying over forcibly
+ // conflict with any names we generated.
+ if (delegatedConstructor.Parameters.Select(p => p.Name).Intersect(remainingParameterNames).Any())
+ {
+ return null;
+ }
+
+ // Try to map those parameters to fields.
+ Dictionary<string, ISymbol> parameterToExistingFieldMap;
+ Dictionary<string, string> parameterToNewFieldMap;
+ List<IParameterSymbol> remainingParameters;
+ this.GetParameters(remainingArguments, remainingAttributeArguments, remainingParameterTypes, remainingParameterNames, out parameterToExistingFieldMap, out parameterToNewFieldMap, out remainingParameters);
+
+ var fields = syntaxFactory.CreateFieldsForParameters(remainingParameters, parameterToNewFieldMap);
+ var assignStatements = syntaxFactory.CreateAssignmentStatements(remainingParameters, parameterToExistingFieldMap, parameterToNewFieldMap);
+
+ var allParameters = delegatedConstructor.Parameters.Concat(remainingParameters).ToList();
+
+ var isThis = namedType.Equals(_state.TypeToGenerateIn);
+ var delegatingArguments = syntaxFactory.CreateArguments(delegatedConstructor.Parameters);
+ var baseConstructorArguments = isThis ? null : delegatingArguments;
+ var thisConstructorArguments = isThis ? delegatingArguments : null;
+
+ var constructor = CodeGenerationSymbolFactory.CreateConstructorSymbol(
+ attributes: null,
+ accessibility: Accessibility.Public,
+ modifiers: default(DeclarationModifiers),
+ typeName: _state.TypeToGenerateIn.Name,
+ parameters: allParameters,
+ statements: assignStatements.ToList(),
+ baseConstructorArguments: baseConstructorArguments,
+ thisConstructorArguments: thisConstructorArguments);
+
+ var members = new List<ISymbol>(fields) { constructor };
+ var result = await codeGenerationService.AddMembersAsync(
+ _document.Project.Solution,
+ _state.TypeToGenerateIn,
+ members,
+ new CodeGenerationOptions(_state.Token.GetLocation(), generateDefaultAccessibility: false),
+ _cancellationToken)
+ .ConfigureAwait(false);
+
+ return result;
+ }
+
+ private async Task<Document> GenerateFieldDelegatingConstructorAsync()
+ {
+ var arguments = _state.Arguments.ToList();
+ var parameterTypes = _state.ParameterTypes;
+
+ var typeParametersNames = _state.TypeToGenerateIn.GetAllTypeParameters().Select(t => t.Name).ToList();
+ var parameterNames = _state.AttributeArguments != null
+ ? _service.GenerateParameterNames(_document.SemanticModel, _state.AttributeArguments, typeParametersNames)
+ : _service.GenerateParameterNames(_document.SemanticModel, arguments, typeParametersNames);
+
+ Dictionary<string, ISymbol> parameterToExistingFieldMap;
+ Dictionary<string, string> parameterToNewFieldMap;
+ List<IParameterSymbol> parameters;
+ GetParameters(arguments, _state.AttributeArguments, parameterTypes, parameterNames, out parameterToExistingFieldMap, out parameterToNewFieldMap, out parameters);
+
+ var provider = _document.Project.Solution.Workspace.Services.GetLanguageServices(_state.TypeToGenerateIn.Language);
+ var syntaxFactory = provider.GetService<SyntaxGenerator>();
+ var codeGenerationService = new CSharpCodeGenerationService (_document.Project.Solution.Workspace);
+
+ var syntaxTree = _document.SyntaxTree;
+ var members = syntaxFactory.CreateFieldDelegatingConstructor(
+ _state.TypeToGenerateIn.Name, _state.TypeToGenerateIn, parameters,
+ parameterToExistingFieldMap, parameterToNewFieldMap, _cancellationToken);
+
+ var result = await codeGenerationService.AddMembersAsync(
+ _document.Project.Solution,
+ _state.TypeToGenerateIn,
+ members,
+ new CodeGenerationOptions(_state.Token.GetLocation(), generateDefaultAccessibility: false),
+ _cancellationToken)
+ .ConfigureAwait(false);
+
+ return result;
+ }
+
+ private void GetParameters(
+ IList<TArgumentSyntax> arguments,
+ IList<TAttributeArgumentSyntax> attributeArguments,
+ IList<ITypeSymbol> parameterTypes,
+ IList<string> parameterNames,
+ out Dictionary<string, ISymbol> parameterToExistingFieldMap,
+ out Dictionary<string, string> parameterToNewFieldMap,
+ out List<IParameterSymbol> parameters)
+ {
+ parameterToExistingFieldMap = new Dictionary<string, ISymbol>();
+ parameterToNewFieldMap = new Dictionary<string, string>();
+ parameters = new List<IParameterSymbol>();
+
+ for (var i = 0; i < parameterNames.Count; i++)
+ {
+ // See if there's a matching field we can use. First test in a case sensitive
+ // manner, then case insensitively.
+ if (!TryFindMatchingField(arguments, attributeArguments, parameterNames, parameterTypes, i, parameterToExistingFieldMap, parameterToNewFieldMap, caseSentitive: true))
+ {
+ if (!TryFindMatchingField(arguments, attributeArguments, parameterNames, parameterTypes, i, parameterToExistingFieldMap, parameterToNewFieldMap, caseSentitive: false))
+ {
+ parameterToNewFieldMap[parameterNames[i]] = parameterNames[i];
+ }
+ }
+
+ parameters.Add(CodeGenerationSymbolFactory.CreateParameterSymbol(
+ attributes: null,
+ refKind: _service.GetRefKind(arguments[i]),
+ isParams: false,
+ type: parameterTypes[i],
+ name: parameterNames[i]));
+ }
+ }
+
+ private bool TryFindMatchingField(
+ IList<TArgumentSyntax> arguments,
+ IList<TAttributeArgumentSyntax> attributeArguments,
+ IList<string> parameterNames,
+ IList<ITypeSymbol> parameterTypes,
+ int index,
+ Dictionary<string, ISymbol> parameterToExistingFieldMap,
+ Dictionary<string, string> parameterToNewFieldMap,
+ bool caseSentitive)
+ {
+ var parameterName = parameterNames[index];
+ var parameterType = parameterTypes[index];
+ var isFixed = _service.IsNamedArgument(arguments[index]);
+
+ // For non-out parameters, see if there's already a field there with the same name.
+ // If so, and it has a compatible type, then we can just assign to that field.
+ // Otherwise, we'll need to choose a different name for this member so that it
+ // doesn't conflict with something already in the type. First check the current type
+ // for a matching field. If so, defer to it.
+ var comparison = caseSentitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
+
+ foreach (var type in _state.TypeToGenerateIn.GetBaseTypesAndThis())
+ {
+ var ignoreAccessibility = type.Equals(_state.TypeToGenerateIn);
+ var symbol = type.GetMembers()
+ .FirstOrDefault(s => s.Name.Equals(parameterName, comparison));
+
+ if (symbol != null)
+ {
+ if (ignoreAccessibility || IsSymbolAccessible(symbol))
+ {
+ if (IsViableFieldOrProperty(parameterType, symbol))
+ {
+ // Ok! We can just the existing field.
+ parameterToExistingFieldMap[parameterName] = symbol;
+ }
+ else
+ {
+ // Uh-oh. Now we have a problem. We can't assign this parameter to
+ // this field. So we need to create a new field. Find a name not in
+ // use so we can assign to that.
+ var newFieldName = NameGenerator.EnsureUniqueness(
+ attributeArguments != null ?
+ _service.GenerateNameForArgument(_document.SemanticModel, attributeArguments[index]) :
+ _service.GenerateNameForArgument(_document.SemanticModel, arguments[index]),
+ GetUnavailableMemberNames().Concat(parameterToNewFieldMap.Values));
+
+ if (isFixed)
+ {
+ // Can't change the parameter name, so map the existing parameter
+ // name to the new field name.
+ parameterToNewFieldMap[parameterName] = newFieldName;
+ }
+ else
+ {
+ // Can change the parameter name, so do so.
+ parameterNames[index] = newFieldName;
+ parameterToNewFieldMap[newFieldName] = newFieldName;
+ }
+ }
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private IEnumerable<string> GetUnavailableMemberNames()
+ {
+ return _state.TypeToGenerateIn.MemberNames.Concat(
+ from type in _state.TypeToGenerateIn.GetBaseTypes()
+ from member in type.GetMembers()
+ select member.Name);
+ }
+
+ private bool IsViableFieldOrProperty(
+ ITypeSymbol parameterType,
+ ISymbol symbol)
+ {
+ if (parameterType.Language != symbol.Language)
+ {
+ return false;
+ }
+
+ if (symbol != null && !symbol.IsStatic)
+ {
+ if (symbol is IFieldSymbol)
+ {
+ var field = (IFieldSymbol)symbol;
+ return
+ !field.IsConst &&
+ _service.IsConversionImplicit(_document.SemanticModel.Compilation, parameterType, field.Type);
+ }
+ else if (symbol is IPropertySymbol)
+ {
+ var property = (IPropertySymbol)symbol;
+ return
+ property.Parameters.Length == 0 &&
+ property.SetMethod != null &&
+ _service.IsConversionImplicit(_document.SemanticModel.Compilation, parameterType, property.Type);
+ }
+ }
+
+ return false;
+ }
+
+ private bool IsSymbolAccessible(
+ ISymbol symbol)
+ {
+ if (symbol == null)
+ {
+ return false;
+ }
+
+ if (symbol.Kind == SymbolKind.Property)
+ {
+ if (!IsSymbolAccessible(((IPropertySymbol)symbol).SetMethod))
+ {
+ return false;
+ }
+ }
+
+ // Public and protected constructors are accessible. Internal constructors are
+ // accessible if we have friend access. We can't call the normal accessibility
+ // checkers since they will think that a protected constructor isn't accessible
+ // (since we don't have the destination type that would have access to them yet).
+ switch (symbol.DeclaredAccessibility)
+ {
+ case Accessibility.ProtectedOrInternal:
+ case Accessibility.Protected:
+ case Accessibility.Public:
+ return true;
+ case Accessibility.ProtectedAndInternal:
+ case Accessibility.Internal:
+ return _document.SemanticModel.Compilation.Assembly.IsSameAssemblyOrHasFriendAccessTo(
+ symbol.ContainingAssembly);
+
+ default:
+ return false;
+ }
+ }
+ }
+
+ protected internal class State
+ {
+ public IList<TArgumentSyntax> Arguments { get; private set; }
+
+ public IList<TAttributeArgumentSyntax> AttributeArguments { get; private set; }
+
+ // The type we're creating a constructor for. Will be a class or struct type.
+ public INamedTypeSymbol TypeToGenerateIn { get; private set; }
+
+ public IList<ITypeSymbol> ParameterTypes { get; private set; }
+ public IList<RefKind> ParameterRefKinds { get; private set; }
+
+ public SyntaxToken Token { get; private set; }
+ public bool IsConstructorInitializerGeneration { get; private set; }
+
+ private State()
+ {
+ this.IsConstructorInitializerGeneration = false;
+ }
+
+ public static async Task<State> GenerateAsync(
+ TService service,
+ SemanticDocument document,
+ SyntaxNode node,
+ CancellationToken cancellationToken)
+ {
+ var state = new State();
+ if (!await state.TryInitializeAsync(service, document, node, cancellationToken).ConfigureAwait(false))
+ {
+ return null;
+ }
+
+ return state;
+ }
+
+ private async Task<bool> TryInitializeAsync(
+ TService service,
+ SemanticDocument document,
+ SyntaxNode node,
+ CancellationToken cancellationToken)
+ {
+ if (service.IsConstructorInitializerGeneration(document, node, cancellationToken))
+ {
+ if (!await TryInitializeConstructorInitializerGenerationAsync(service, document, node, cancellationToken).ConfigureAwait(false))
+ {
+ return false;
+ }
+ }
+ else if (service.IsSimpleNameGeneration(document, node, cancellationToken))
+ {
+ if (!await TryInitializeSimpleNameGenerationAsync(service, document, node, cancellationToken).ConfigureAwait(false))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ if (!new CSharpCodeGenerationService (document.Project.Solution.Workspace).CanAddTo(this.TypeToGenerateIn, document.Project.Solution, cancellationToken))
+ {
+ return false;
+ }
+
+ this.ParameterTypes = this.ParameterTypes ?? GetParameterTypes(service, document, cancellationToken);
+ this.ParameterRefKinds = this.Arguments.Select(service.GetRefKind).ToList();
+
+ return !ClashesWithExistingConstructor(service, document, cancellationToken);
+ }
+
+ private bool ClashesWithExistingConstructor(TService service, SemanticDocument document, CancellationToken cancellationToken)
+ {
+ var parameters = this.ParameterTypes.Zip(this.ParameterRefKinds, (t, r) => CodeGenerationSymbolFactory.CreateParameterSymbol(
+ attributes: null,
+ refKind: r,
+ isParams: false,
+ type: t,
+ name: string.Empty)).ToList();
+
+ var destinationProvider = document.Project.Solution.Workspace.Services.GetLanguageServices(this.TypeToGenerateIn.Language);
+
+ return this.TypeToGenerateIn.InstanceConstructors.Any(c => SignatureComparer.HaveSameSignature(parameters, c.Parameters, compareParameterName: true, isCaseSensitive: true));
+ }
+
+ internal List<ITypeSymbol> GetParameterTypes(
+ TService service,
+ SemanticDocument document,
+ CancellationToken cancellationToken)
+ {
+ var allTypeParameters = this.TypeToGenerateIn.GetAllTypeParameters();
+ var semanticModel = document.SemanticModel;
+ var allTypes = this.AttributeArguments != null
+ ? this.AttributeArguments.Select(a => service.GetAttributeArgumentType(semanticModel, a, cancellationToken))
+ : this.Arguments.Select(a => service.GetArgumentType(semanticModel, a, cancellationToken));
+
+ return allTypes.Select(t => FixType(t, semanticModel, allTypeParameters)).ToList();
+ }
+
+ private ITypeSymbol FixType(ITypeSymbol typeSymbol, SemanticModel semanticModel, IEnumerable<ITypeParameterSymbol> allTypeParameters)
+ {
+ var compilation = semanticModel.Compilation;
+ return typeSymbol.RemoveAnonymousTypes(compilation)
+ .RemoveUnavailableTypeParameters(compilation, allTypeParameters)
+ .RemoveUnnamedErrorTypes(compilation);
+ }
+
+ private async Task<bool> TryInitializeConstructorInitializerGenerationAsync(
+ TService service,
+ SemanticDocument document,
+ SyntaxNode constructorInitializer,
+ CancellationToken cancellationToken)
+ {
+ SyntaxToken token;
+ IList<TArgumentSyntax> arguments;
+ INamedTypeSymbol typeToGenerateIn;
+ if (!service.TryInitializeConstructorInitializerGeneration(document, constructorInitializer, cancellationToken,
+ out token, out arguments, out typeToGenerateIn))
+ {
+ return false;
+ }
+
+ this.Token = token;
+ this.Arguments = arguments;
+ this.IsConstructorInitializerGeneration = true;
+
+ var semanticModel = document.SemanticModel;
+ var semanticInfo = semanticModel.GetSymbolInfo(constructorInitializer, cancellationToken);
+
+ cancellationToken.ThrowIfCancellationRequested();
+ if (semanticInfo.Symbol != null)
+ {
+ return false;
+ }
+
+ return await TryDetermineTypeToGenerateInAsync(document, typeToGenerateIn, cancellationToken).ConfigureAwait(false);
+ }
+
+ private async Task<bool> TryInitializeSimpleNameGenerationAsync(
+ TService service,
+ SemanticDocument document,
+ SyntaxNode simpleName,
+ CancellationToken cancellationToken)
+ {
+ SyntaxToken token;
+ IList<TArgumentSyntax> arguments;
+ IList<TAttributeArgumentSyntax> attributeArguments;
+ INamedTypeSymbol typeToGenerateIn;
+ if (service.TryInitializeSimpleNameGenerationState(document, simpleName, cancellationToken,
+ out token, out arguments, out typeToGenerateIn))
+ {
+ this.Token = token;
+ this.Arguments = arguments;
+ }
+ else if (service.TryInitializeSimpleAttributeNameGenerationState(document, simpleName, cancellationToken,
+ out token, out arguments, out attributeArguments, out typeToGenerateIn))
+ {
+ this.Token = token;
+ this.AttributeArguments = attributeArguments;
+ this.Arguments = arguments;
+
+ //// Attribute parameters are restricted to be constant values (simple types or string, etc).
+ if (this.AttributeArguments != null && GetParameterTypes(service, document, cancellationToken).Any(t => !IsValidAttributeParameterType(t)))
+ {
+ return false;
+ }
+ else if (GetParameterTypes(service, document, cancellationToken).Any(t => !IsValidAttributeParameterType(t)))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ cancellationToken.ThrowIfCancellationRequested();
+
+ return await TryDetermineTypeToGenerateInAsync(document, typeToGenerateIn, cancellationToken).ConfigureAwait(false);
+ }
+
+ private bool IsValidAttributeParameterType(ITypeSymbol type)
+ {
+ if (type.Kind == SymbolKind.ArrayType)
+ {
+ var arrayType = (IArrayTypeSymbol)type;
+ if (arrayType.Rank != 1)
+ {
+ return false;
+ }
+
+ type = arrayType.ElementType;
+ }
+
+ if (type.IsEnumType())
+ {
+ return true;
+ }
+
+ switch (type.SpecialType)
+ {
+ case SpecialType.System_Boolean:
+ case SpecialType.System_Byte:
+ case SpecialType.System_Char:
+ case SpecialType.System_Int16:
+ case SpecialType.System_Int32:
+ case SpecialType.System_Int64:
+ case SpecialType.System_Double:
+ case SpecialType.System_Single:
+ case SpecialType.System_String:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ private async Task<bool> TryDetermineTypeToGenerateInAsync(
+ SemanticDocument document,
+ INamedTypeSymbol original,
+ CancellationToken cancellationToken)
+ {
+ var definition = await SymbolFinder.FindSourceDefinitionAsync(original, document.Project.Solution, cancellationToken).ConfigureAwait(false);
+ this.TypeToGenerateIn = definition as INamedTypeSymbol;
+
+ return this.TypeToGenerateIn != null &&
+ (this.TypeToGenerateIn.TypeKind == TypeKind.Class ||
+ this.TypeToGenerateIn.TypeKind == TypeKind.Struct);
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateConstructor/CSharpGenerateConstructorService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateConstructor/CSharpGenerateConstructorService.cs
new file mode 100644
index 0000000000..04eeba23d7
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateConstructor/CSharpGenerateConstructorService.cs
@@ -0,0 +1,278 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Composition;
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateMember.GenerateConstructor
+{
+ public class CSharpGenerateConstructorService : AbstractGenerateConstructorService<CSharpGenerateConstructorService, ArgumentSyntax, AttributeArgumentSyntax>
+ {
+ private static readonly SyntaxAnnotation s_annotation = new SyntaxAnnotation();
+
+ protected override bool IsSimpleNameGeneration(SemanticDocument document, SyntaxNode node, CancellationToken cancellationToken)
+ {
+ return node is SimpleNameSyntax;
+ }
+
+ protected override bool IsConstructorInitializerGeneration(SemanticDocument document, SyntaxNode node, CancellationToken cancellationToken)
+ {
+ return node is ConstructorInitializerSyntax;
+ }
+
+ protected override bool TryInitializeConstructorInitializerGeneration(
+ SemanticDocument document, SyntaxNode node, CancellationToken cancellationToken,
+ out SyntaxToken token, out IList<ArgumentSyntax> arguments, out INamedTypeSymbol typeToGenerateIn)
+ {
+ var constructorInitializer = (ConstructorInitializerSyntax)node;
+
+ if (!constructorInitializer.ArgumentList.CloseParenToken.IsMissing)
+ {
+ token = constructorInitializer.ThisOrBaseKeyword;
+ arguments = constructorInitializer.ArgumentList.Arguments.ToList();
+
+ var semanticModel = document.SemanticModel;
+ var currentType = semanticModel.GetEnclosingNamedType(constructorInitializer.SpanStart, cancellationToken);
+ typeToGenerateIn = constructorInitializer.IsKind(SyntaxKind.ThisConstructorInitializer)
+ ? currentType
+ : currentType.BaseType.OriginalDefinition;
+ return typeToGenerateIn != null;
+ }
+
+ token = default(SyntaxToken);
+ arguments = null;
+ typeToGenerateIn = null;
+ return false;
+ }
+
+ protected override bool TryInitializeSimpleNameGenerationState(
+ SemanticDocument document,
+ SyntaxNode node,
+ CancellationToken cancellationToken,
+ out SyntaxToken token,
+ out IList<ArgumentSyntax> arguments,
+ out INamedTypeSymbol typeToGenerateIn)
+ {
+ var simpleName = (SimpleNameSyntax)node;
+ var fullName = simpleName.IsRightSideOfQualifiedName()
+ ? (NameSyntax)simpleName.Parent
+ : simpleName;
+
+ if (fullName.Parent is ObjectCreationExpressionSyntax)
+ {
+ var objectCreationExpression = (ObjectCreationExpressionSyntax)fullName.Parent;
+ if (objectCreationExpression.ArgumentList != null &&
+ !objectCreationExpression.ArgumentList.CloseParenToken.IsMissing)
+ {
+ var symbolInfo = document.SemanticModel.GetSymbolInfo(objectCreationExpression.Type, cancellationToken);
+ token = simpleName.Identifier;
+ arguments = objectCreationExpression.ArgumentList.Arguments.ToList();
+ typeToGenerateIn = symbolInfo.GetAnySymbol() as INamedTypeSymbol;
+ return typeToGenerateIn != null;
+ }
+ }
+
+ token = default(SyntaxToken);
+ arguments = null;
+ typeToGenerateIn = null;
+ return false;
+ }
+
+ protected override bool TryInitializeSimpleAttributeNameGenerationState(
+ SemanticDocument document,
+ SyntaxNode node,
+ CancellationToken cancellationToken,
+ out SyntaxToken token,
+ out IList<ArgumentSyntax> arguments,
+ out IList<AttributeArgumentSyntax> attributeArguments,
+ out INamedTypeSymbol typeToGenerateIn)
+ {
+ var simpleName = (SimpleNameSyntax)node;
+ var fullName = simpleName.IsRightSideOfQualifiedName()
+ ? (NameSyntax)simpleName.Parent
+ : simpleName;
+
+ if (fullName.Parent is AttributeSyntax)
+ {
+ var attribute = (AttributeSyntax)fullName.Parent;
+ if (attribute.ArgumentList != null &&
+ !attribute.ArgumentList.CloseParenToken.IsMissing)
+ {
+ var symbolInfo = document.SemanticModel.GetSymbolInfo(attribute, cancellationToken);
+ if (symbolInfo.CandidateReason == CandidateReason.OverloadResolutionFailure && !symbolInfo.CandidateSymbols.IsEmpty)
+ {
+ token = simpleName.Identifier;
+ attributeArguments = attribute.ArgumentList.Arguments.ToList();
+ arguments = attributeArguments.Select(x => SyntaxFactory.Argument(x.NameColon ?? ((x.NameEquals != null) ? SyntaxFactory.NameColon(x.NameEquals.Name) : null), default(SyntaxToken), x.Expression)).ToList();
+
+ typeToGenerateIn = symbolInfo.CandidateSymbols.FirstOrDefault().ContainingSymbol as INamedTypeSymbol;
+ return typeToGenerateIn != null;
+ }
+ }
+ }
+
+ token = default(SyntaxToken);
+ arguments = null;
+ attributeArguments = null;
+ typeToGenerateIn = null;
+ return false;
+ }
+
+ protected override IList<string> GenerateParameterNames(
+ SemanticModel semanticModel, IEnumerable<ArgumentSyntax> arguments, IList<string> reservedNames)
+ {
+ return semanticModel.GenerateParameterNames(arguments, reservedNames);
+ }
+
+ protected override IList<string> GenerateParameterNames(
+ SemanticModel semanticModel, IEnumerable<AttributeArgumentSyntax> arguments, IList<string> reservedNames)
+ {
+ return semanticModel.GenerateParameterNames(arguments, reservedNames);
+ }
+
+ protected override string GenerateNameForArgument(
+ SemanticModel semanticModel, ArgumentSyntax argument)
+ {
+ return semanticModel.GenerateNameForArgument(argument);
+ }
+
+ protected override string GenerateNameForArgument(
+ SemanticModel semanticModel, AttributeArgumentSyntax argument)
+ {
+ return semanticModel.GenerateNameForArgument(argument);
+ }
+
+ protected override RefKind GetRefKind(ArgumentSyntax argument)
+ {
+ return argument.RefOrOutKeyword.Kind() == SyntaxKind.RefKeyword ? RefKind.Ref :
+ argument.RefOrOutKeyword.Kind() == SyntaxKind.OutKeyword ? RefKind.Out : RefKind.None;
+ }
+
+ protected override bool IsNamedArgument(ArgumentSyntax argument)
+ {
+ return argument.NameColon != null;
+ }
+
+ protected override ITypeSymbol GetArgumentType(
+ SemanticModel semanticModel, ArgumentSyntax argument, CancellationToken cancellationToken)
+ {
+ return semanticModel.GetType(argument.Expression, cancellationToken);
+ }
+
+ protected override ITypeSymbol GetAttributeArgumentType(
+ SemanticModel semanticModel, AttributeArgumentSyntax argument, CancellationToken cancellationToken)
+ {
+ return semanticModel.GetType(argument.Expression, cancellationToken);
+ }
+
+ protected override bool IsConversionImplicit(Compilation compilation, ITypeSymbol sourceType, ITypeSymbol targetType)
+ {
+ return compilation.ClassifyConversion(sourceType, targetType).IsImplicit;
+ }
+
+ internal override IMethodSymbol GetDelegatingConstructor(State state, SemanticDocument document, int argumentCount, INamedTypeSymbol namedType, ISet<IMethodSymbol> candidates, CancellationToken cancellationToken)
+ {
+ var oldToken = state.Token;
+ var tokenKind = oldToken.Kind();
+
+ if (state.IsConstructorInitializerGeneration)
+ {
+ SyntaxToken thisOrBaseKeyword;
+ SyntaxKind newCtorInitializerKind;
+ if (tokenKind != SyntaxKind.BaseKeyword && state.TypeToGenerateIn == namedType)
+ {
+ thisOrBaseKeyword = SyntaxFactory.Token(SyntaxKind.ThisKeyword);
+ newCtorInitializerKind = SyntaxKind.ThisConstructorInitializer;
+ }
+ else
+ {
+ thisOrBaseKeyword = SyntaxFactory.Token(SyntaxKind.BaseKeyword);
+ newCtorInitializerKind = SyntaxKind.BaseConstructorInitializer;
+ }
+
+ var ctorInitializer = (ConstructorInitializerSyntax)oldToken.Parent;
+ var oldArgumentList = ctorInitializer.ArgumentList;
+ var newArgumentList = GetNewArgumentList(oldArgumentList, argumentCount);
+
+ var newCtorInitializer = SyntaxFactory.ConstructorInitializer(newCtorInitializerKind, ctorInitializer.ColonToken, thisOrBaseKeyword, newArgumentList);
+ SemanticModel speculativeModel;
+ if (document.SemanticModel.TryGetSpeculativeSemanticModel(ctorInitializer.Span.Start, newCtorInitializer, out speculativeModel))
+ {
+ var symbolInfo = speculativeModel.GetSymbolInfo(newCtorInitializer, cancellationToken);
+ return GenerateConstructorHelpers.GetDelegatingConstructor(symbolInfo, candidates, namedType);
+ }
+ }
+ else
+ {
+ var oldNode = oldToken.Parent
+ .AncestorsAndSelf(ascendOutOfTrivia: false)
+ .Where(node => SpeculationAnalyzer.CanSpeculateOnNode(node))
+ .LastOrDefault();
+
+ var typeNameToReplace = (TypeSyntax)oldToken.Parent;
+ TypeSyntax newTypeName;
+ if (namedType != state.TypeToGenerateIn)
+ {
+ while (true)
+ {
+ var parentType = typeNameToReplace.Parent as TypeSyntax;
+ if (parentType == null)
+ {
+ break;
+ }
+
+ typeNameToReplace = parentType;
+ }
+
+ newTypeName = namedType.GenerateTypeSyntax().WithAdditionalAnnotations(s_annotation);
+ }
+ else
+ {
+ newTypeName = typeNameToReplace.WithAdditionalAnnotations(s_annotation);
+ }
+
+ var newNode = oldNode.ReplaceNode(typeNameToReplace, newTypeName);
+ newTypeName = (TypeSyntax)newNode.GetAnnotatedNodes(s_annotation).Single();
+
+ var oldArgumentList = (ArgumentListSyntax)newTypeName.Parent.ChildNodes().FirstOrDefault(n => n is ArgumentListSyntax);
+ if (oldArgumentList != null)
+ {
+ var newArgumentList = GetNewArgumentList(oldArgumentList, argumentCount);
+ if (newArgumentList != oldArgumentList)
+ {
+ newNode = newNode.ReplaceNode(oldArgumentList, newArgumentList);
+ newTypeName = (TypeSyntax)newNode.GetAnnotatedNodes(s_annotation).Single();
+ }
+ }
+
+ var speculativeModel = SpeculationAnalyzer.CreateSpeculativeSemanticModelForNode(oldNode, newNode, document.SemanticModel);
+ if (speculativeModel != null)
+ {
+ var symbolInfo = speculativeModel.GetSymbolInfo(newTypeName.Parent, cancellationToken);
+ return GenerateConstructorHelpers.GetDelegatingConstructor(symbolInfo, candidates, namedType);
+ }
+ }
+
+ return null;
+ }
+
+ private static ArgumentListSyntax GetNewArgumentList(ArgumentListSyntax oldArgumentList, int argumentCount)
+ {
+ if (oldArgumentList.IsMissing || oldArgumentList.Arguments.Count == argumentCount)
+ {
+ return oldArgumentList;
+ }
+
+ var newArguments = oldArgumentList.Arguments.Take(argumentCount);
+ return SyntaxFactory.ArgumentList(new SeparatedSyntaxList<ArgumentSyntax>().AddRange(newArguments));
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateConstructor/GenerateConstructorHelpers.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateConstructor/GenerateConstructorHelpers.cs
new file mode 100644
index 0000000000..ce7fdf3c5d
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateConstructor/GenerateConstructorHelpers.cs
@@ -0,0 +1,35 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateMember.GenerateConstructor
+{
+ internal static class GenerateConstructorHelpers
+ {
+ public static IMethodSymbol GetDelegatingConstructor(SymbolInfo symbolInfo, ISet<IMethodSymbol> candidateInstanceConstructors, INamedTypeSymbol containingType)
+ {
+ var symbol = symbolInfo.Symbol as IMethodSymbol;
+ if (symbol == null && symbolInfo.CandidateSymbols.Count() == 1)
+ {
+ // Even though the symbol info has a non-viable candidate symbol, we are trying to speculate a base constructor
+ // invocation from a different position then where the invocation to it would be generated.
+ // Passed in candidateInstanceConstructors actually represent all accessible and invocable constructor symbols.
+ // So, we allow candidate symbol for inaccessible OR not creatable candidate reason if it is in the given candidateInstanceConstructors.
+ if (symbolInfo.CandidateReason == CandidateReason.Inaccessible ||
+ (symbolInfo.CandidateReason == CandidateReason.NotCreatable && containingType.IsAbstract))
+ {
+ symbol = symbolInfo.CandidateSymbols.Single() as IMethodSymbol;
+ }
+ }
+
+ if (symbol != null && candidateInstanceConstructors.Contains(symbol))
+ {
+ return symbol;
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateDefaultConstructors/AbstractGenerateDefaultConstructorsService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateDefaultConstructors/AbstractGenerateDefaultConstructorsService.cs
new file mode 100644
index 0000000000..94feddabdb
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateDefaultConstructors/AbstractGenerateDefaultConstructorsService.cs
@@ -0,0 +1,242 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.CodeRefactorings;
+using Microsoft.CodeAnalysis.Internal.Log;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.CodeAnalysis;
+using ICSharpCode.NRefactory6.CSharp.Refactoring;
+using Microsoft.CodeAnalysis.Editing;
+using System.Linq;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateMember.GenerateDefaultConstructors
+{
+ public abstract partial class AbstractGenerateDefaultConstructorsService<TService>
+ where TService : AbstractGenerateDefaultConstructorsService<TService>
+ {
+ protected AbstractGenerateDefaultConstructorsService()
+ {
+ }
+
+ protected abstract bool TryInitializeState(SemanticDocument document, TextSpan textSpan, CancellationToken cancellationToken, out SyntaxNode baseTypeNode, out INamedTypeSymbol classType);
+
+ public async Task<GenerateDefaultConstructorsResult> GenerateDefaultConstructorsAsync(
+ Document document,
+ TextSpan textSpan,
+ CancellationToken cancellationToken)
+ {
+ var semanticDocument = await SemanticDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false);
+
+ if (textSpan.IsEmpty)
+ {
+ var state = State.Generate((TService)this, semanticDocument, textSpan, cancellationToken);
+ if (state != null)
+ {
+ return new GenerateDefaultConstructorsResult(new CodeRefactoring(null, GetActions(document, state)));
+ }
+ }
+
+ return GenerateDefaultConstructorsResult.Failure;
+ }
+
+ private IEnumerable<CodeAction> GetActions(Document document, State state)
+ {
+ foreach (var constructor in state.UnimplementedConstructors)
+ {
+ yield return new GenerateDefaultConstructorCodeAction((TService)this, document, state, constructor);
+ }
+
+ if (state.UnimplementedConstructors.Count > 1)
+ {
+ yield return new CodeActionAll((TService)this, document, state, state.UnimplementedConstructors);
+ }
+ }
+
+ private abstract class AbstractCodeAction : CodeAction
+ {
+ private readonly IList<IMethodSymbol> _constructors;
+ private readonly Document _document;
+ private readonly State _state;
+ private readonly TService _service;
+ private readonly string _title;
+
+ protected AbstractCodeAction(
+ TService service,
+ Document document,
+ State state,
+ IList<IMethodSymbol> constructors,
+ string title)
+ {
+ _service = service;
+ _document = document;
+ _state = state;
+ _constructors = constructors;
+ _title = title;
+ }
+
+ public override string Title
+ {
+ get { return _title; }
+ }
+
+ protected override async Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken)
+ {
+ var result = await CodeGenerator.AddMemberDeclarationsAsync(
+ _document.Project.Solution,
+ _state.ClassType,
+ _constructors.Select(CreateConstructorDefinition),
+ cancellationToken: cancellationToken).ConfigureAwait(false);
+
+ return result;
+ }
+
+ private IMethodSymbol CreateConstructorDefinition(
+ IMethodSymbol constructor)
+ {
+ var syntaxFactory = _document.GetLanguageService<SyntaxGenerator>();
+ var baseConstructorArguments = constructor.Parameters.Length != 0
+ ? syntaxFactory.CreateArguments(constructor.Parameters)
+ : null;
+
+ return CodeGenerationSymbolFactory.CreateConstructorSymbol(
+ attributes: null,
+ accessibility: constructor.DeclaredAccessibility,
+ modifiers: new DeclarationModifiers(),
+ typeName: _state.ClassType.Name,
+ parameters: constructor.Parameters,
+ statements: null,
+ baseConstructorArguments: baseConstructorArguments);
+ }
+ }
+
+
+ private class GenerateDefaultConstructorCodeAction : AbstractCodeAction
+ {
+ public GenerateDefaultConstructorCodeAction(
+ TService service,
+ Document document,
+ State state,
+ IMethodSymbol constructor)
+ : base(service, document, state, new[] { constructor }, GetDisplayText(state, constructor))
+ {
+ }
+
+ private static string GetDisplayText(State state, IMethodSymbol constructor)
+ {
+ var parameters = constructor.Parameters.Select(p => p.Name);
+ var parameterString = string.Join(", ", parameters);
+
+ return string.Format(Resources.GenerateConstructor + ".",
+ state.ClassType.Name, parameterString);
+ }
+ }
+
+ private class CodeActionAll : AbstractCodeAction
+ {
+ public CodeActionAll(
+ TService service,
+ Document document,
+ State state,
+ IList<IMethodSymbol> constructors)
+ : base(service, document, state, GetConstructors(state, constructors), Resources.GenerateAll)
+ {
+ }
+
+ private static IList<IMethodSymbol> GetConstructors(State state, IList<IMethodSymbol> constructors)
+ {
+ return state.UnimplementedDefaultConstructor != null
+ ? new[] { state.UnimplementedDefaultConstructor }.Concat(constructors).ToList()
+ : constructors;
+ }
+ }
+ private class State
+ {
+ public INamedTypeSymbol ClassType { get; private set; }
+
+ public IList<IMethodSymbol> UnimplementedConstructors { get; private set; }
+ public IMethodSymbol UnimplementedDefaultConstructor { get; private set; }
+
+ public SyntaxNode BaseTypeNode { get; private set; }
+
+ private State()
+ {
+ }
+
+ public static State Generate(
+ TService service,
+ SemanticDocument document,
+ TextSpan textSpan,
+ CancellationToken cancellationToken)
+ {
+ var state = new State();
+ if (!state.TryInitialize(service, document, textSpan, cancellationToken))
+ {
+ return null;
+ }
+
+ return state;
+ }
+
+ private bool TryInitialize(
+ TService service,
+ SemanticDocument document,
+ TextSpan textSpan,
+ CancellationToken cancellationToken)
+ {
+ SyntaxNode baseTypeNode;
+ INamedTypeSymbol classType;
+ if (!service.TryInitializeState(document, textSpan, cancellationToken, out baseTypeNode, out classType))
+ {
+ return false;
+ }
+
+ if (!baseTypeNode.Span.IntersectsWith(textSpan.Start))
+ {
+ return false;
+ }
+
+ this.BaseTypeNode = baseTypeNode;
+ this.ClassType = classType;
+
+ var baseType = this.ClassType.BaseType;
+
+ if (this.ClassType.TypeKind != TypeKind.Class ||
+ this.ClassType.IsStatic ||
+ baseType == null ||
+ baseType.SpecialType == SpecialType.System_Object ||
+ baseType.TypeKind == TypeKind.Error)
+ {
+ return false;
+ }
+
+ var classConstructors = this.ClassType.InstanceConstructors;
+ var baseTypeConstructors =
+ baseType.InstanceConstructors
+ .Where(c => c.IsAccessibleWithin(this.ClassType));
+
+ var destinationProvider = document.Project.Solution.Workspace.Services.GetLanguageServices(this.ClassType.Language);
+
+ var missingConstructors =
+ baseTypeConstructors.Where(c1 => !classConstructors.Any(
+ c2 => SignatureComparer.HaveSameSignature(c1.Parameters, c2.Parameters, compareParameterName: true, isCaseSensitive: true))).ToList();
+
+ this.UnimplementedConstructors = missingConstructors;
+
+ this.UnimplementedDefaultConstructor = baseTypeConstructors.FirstOrDefault(c => c.Parameters.Length == 0);
+ if (this.UnimplementedDefaultConstructor != null)
+ {
+ if (classConstructors.Any(c => c.Parameters.Length == 0 && !c.IsImplicitlyDeclared))
+ {
+ this.UnimplementedDefaultConstructor = null;
+ }
+ }
+
+ return this.UnimplementedConstructors.Count > 0;
+ }
+ }
+
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateDefaultConstructors/CSharpGenerateDefaultConstructorsService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateDefaultConstructors/CSharpGenerateDefaultConstructorsService.cs
new file mode 100644
index 0000000000..b443a65e52
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateDefaultConstructors/CSharpGenerateDefaultConstructorsService.cs
@@ -0,0 +1,49 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Composition;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Host;
+using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateMember.GenerateDefaultConstructors
+{
+ public class CSharpGenerateDefaultConstructorsService : AbstractGenerateDefaultConstructorsService<CSharpGenerateDefaultConstructorsService>
+ {
+ protected override bool TryInitializeState(
+ SemanticDocument document, TextSpan textSpan, CancellationToken cancellationToken,
+ out SyntaxNode baseTypeNode, out INamedTypeSymbol classType)
+ {
+ if (!cancellationToken.IsCancellationRequested)
+ {
+ var syntaxTree = document.SyntaxTree;
+ var node = document.Root.FindToken(textSpan.Start).GetAncestor<TypeSyntax>();
+ if (node != null)
+ {
+ if (node.Parent is BaseTypeSyntax && node.Parent.IsParentKind(SyntaxKind.BaseList))
+ {
+ var baseList = (BaseListSyntax)node.Parent.Parent;
+ if (baseList.Types.Count > 0 &&
+ baseList.Types[0].Type == node &&
+ baseList.IsParentKind(SyntaxKind.ClassDeclaration))
+ {
+ var semanticModel = document.SemanticModel;
+ classType = semanticModel.GetDeclaredSymbol(baseList.Parent, cancellationToken) as INamedTypeSymbol;
+ baseTypeNode = node;
+ return classType != null;
+ }
+ }
+ }
+ }
+
+ baseTypeNode = null;
+ classType = null;
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateDefaultConstructors/GenerateDefaultConstructorsResult.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateDefaultConstructors/GenerateDefaultConstructorsResult.cs
new file mode 100644
index 0000000000..7dc5f0de2d
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateDefaultConstructors/GenerateDefaultConstructorsResult.cs
@@ -0,0 +1,17 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.CodeAnalysis.CodeRefactorings;
+using ICSharpCode.NRefactory6.CSharp.Refactoring;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateMember.GenerateDefaultConstructors
+{
+ public class GenerateDefaultConstructorsResult : AbstractCodeRefactoringResult
+ {
+ public static readonly GenerateDefaultConstructorsResult Failure = new GenerateDefaultConstructorsResult(null);
+
+ internal GenerateDefaultConstructorsResult(CodeRefactoring codeRefactoring)
+ : base(codeRefactoring)
+ {
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateEnumMember/AbstractGenerateEnumMemberService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateEnumMember/AbstractGenerateEnumMemberService.cs
new file mode 100644
index 0000000000..c13d3b7d85
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateEnumMember/AbstractGenerateEnumMemberService.cs
@@ -0,0 +1,241 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.Internal.Log;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+using ICSharpCode.NRefactory6.CSharp.CodeGeneration;
+using Microsoft.CodeAnalysis.Editing;
+using Microsoft.CodeAnalysis.FindSymbols;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateMember.GenerateEnumMember
+{
+ public abstract class AbstractGenerateEnumMemberService<TService, TSimpleNameSyntax, TExpressionSyntax> :
+ AbstractGenerateMemberService<TSimpleNameSyntax, TExpressionSyntax>
+ where TService : AbstractGenerateEnumMemberService<TService, TSimpleNameSyntax, TExpressionSyntax>
+ where TSimpleNameSyntax : TExpressionSyntax
+ where TExpressionSyntax : SyntaxNode
+ {
+ protected AbstractGenerateEnumMemberService()
+ {
+ }
+
+ protected abstract bool IsIdentifierNameGeneration(SyntaxNode node);
+ protected abstract bool TryInitializeIdentifierNameState(SemanticDocument document, TSimpleNameSyntax identifierName, CancellationToken cancellationToken, out SyntaxToken identifierToken, out TExpressionSyntax simpleNameOrMemberAccessExpression);
+
+ public async Task<IEnumerable<CodeAction>> GenerateEnumMemberAsync(Document document, SyntaxNode node, CancellationToken cancellationToken)
+ {
+ var semanticDocument = await SemanticDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false);
+ var state = await State.GenerateAsync((TService)this, semanticDocument, node, cancellationToken).ConfigureAwait(false);
+ if (state == null)
+ {
+ return SpecializedCollections.EmptyEnumerable<CodeAction>();
+ }
+
+ return GetActions(document, state);
+ }
+
+ private IEnumerable<CodeAction> GetActions(Document document, State state)
+ {
+ yield return new GenerateEnumMemberCodeAction((TService)this, document, state);
+ }
+
+ private partial class GenerateEnumMemberCodeAction : CodeAction
+ {
+ private readonly TService _service;
+ private readonly Document _document;
+ private readonly State _state;
+
+ public GenerateEnumMemberCodeAction(
+ TService service,
+ Document document,
+ State state)
+ {
+ _service = service;
+ _document = document;
+ _state = state;
+ }
+
+ protected override async Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken)
+ {
+ var languageServices = _document.Project.Solution.Workspace.Services.GetLanguageServices(_state.TypeToGenerateIn.Language);
+
+ var value = _state.TypeToGenerateIn.LastEnumValueHasInitializer()
+ ? EnumValueUtilities.GetNextEnumValue(_state.TypeToGenerateIn, cancellationToken)
+ : null;
+
+ var syntaxTree = await _document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
+ var result = await new CSharpCodeGenerationService (_document.Project.Solution.Workspace).AddFieldAsync(
+ _document.Project.Solution,
+ _state.TypeToGenerateIn,
+ CodeGenerationSymbolFactory.CreateFieldSymbol(
+ attributes: null,
+ accessibility: Accessibility.Public,
+ modifiers: default(DeclarationModifiers),
+ type: _state.TypeToGenerateIn,
+ name: _state.IdentifierToken.ValueText,
+ hasConstantValue: value != null,
+ constantValue: value),
+ new CodeGenerationOptions(contextLocation: _state.IdentifierToken.GetLocation(), generateDefaultAccessibility: false),
+ cancellationToken)
+ .ConfigureAwait(false);
+
+ return result;
+ }
+
+ public override string Title
+ {
+ get
+ {
+ var text = Resources.GenerateEnumMemberIn;
+
+ return string.Format(
+ text,
+ _state.IdentifierToken.ValueText,
+ _state.TypeToGenerateIn.Name);
+ }
+ }
+ }
+
+
+ private partial class State
+ {
+ // public TypeDeclarationSyntax ContainingTypeDeclaration { get; private set; }
+ public INamedTypeSymbol TypeToGenerateIn { get; private set; }
+
+ // Just the name of the method. i.e. "Foo" in "Foo" or "X.Foo"
+ public SyntaxToken IdentifierToken { get; private set; }
+ public TSimpleNameSyntax SimpleName { get; private set; }
+ public TExpressionSyntax SimpleNameOrMemberAccessExpression { get; private set; }
+
+ public static async Task<State> GenerateAsync(
+ TService service,
+ SemanticDocument document,
+ SyntaxNode node,
+ CancellationToken cancellationToken)
+ {
+ var state = new State();
+ if (!await state.TryInitializeAsync(service, document, node, cancellationToken).ConfigureAwait(false))
+ {
+ return null;
+ }
+
+ return state;
+ }
+
+ private async Task<bool> TryInitializeAsync(
+ TService service,
+ SemanticDocument document,
+ SyntaxNode node,
+ CancellationToken cancellationToken)
+ {
+ if (service.IsIdentifierNameGeneration(node))
+ {
+ if (!TryInitializeIdentifierName(service, document, (TSimpleNameSyntax)node, cancellationToken))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ // Ok. It either didn't bind to any symbols, or it bound to a symbol but with
+ // errors. In the former case we definitely want to offer to generate a field. In
+ // the latter case, we want to generate a field *unless* there's an existing member
+ // with the same name. Note: it's ok if there's an existing field with the same
+ // name.
+ var existingMembers = this.TypeToGenerateIn.GetMembers(this.IdentifierToken.ValueText);
+ if (existingMembers.Any())
+ {
+ // TODO: Code coverage There was an existing member that the new member would
+ // clash with.
+ return false;
+ }
+
+ cancellationToken.ThrowIfCancellationRequested();
+ this.TypeToGenerateIn = await SymbolFinder.FindSourceDefinitionAsync(this.TypeToGenerateIn, document.Project.Solution, cancellationToken).ConfigureAwait(false) as INamedTypeSymbol;
+ if (!service.ValidateTypeToGenerateIn(
+ document.Project.Solution, this.TypeToGenerateIn, true, EnumType, cancellationToken))
+ {
+ return false;
+ }
+
+ return CodeGenerator.CanAdd(document.Project.Solution, this.TypeToGenerateIn, cancellationToken);
+ }
+
+ private bool TryInitializeIdentifierName(
+ TService service,
+ SemanticDocument document,
+ TSimpleNameSyntax identifierName,
+ CancellationToken cancellationToken)
+ {
+ this.SimpleName = identifierName;
+
+ SyntaxToken identifierToken;
+ TExpressionSyntax simpleNameOrMemberAccessExpression;
+ if (!service.TryInitializeIdentifierNameState(document, identifierName, cancellationToken,
+ out identifierToken, out simpleNameOrMemberAccessExpression))
+ {
+ return false;
+ }
+
+ this.IdentifierToken = identifierToken;
+ this.SimpleNameOrMemberAccessExpression = simpleNameOrMemberAccessExpression;
+
+ var semanticModel = document.SemanticModel;
+ if ((simpleNameOrMemberAccessExpression as ExpressionSyntax).IsWrittenTo() ||
+ simpleNameOrMemberAccessExpression.IsInNamespaceOrTypeContext())
+ {
+ return false;
+ }
+
+ // Now, try to bind the invocation and see if it succeeds or not. if it succeeds and
+ // binds uniquely, then we don't need to offer this quick fix.
+ cancellationToken.ThrowIfCancellationRequested();
+ var containingType = semanticModel.GetEnclosingNamedType(identifierToken.SpanStart, cancellationToken);
+ if (containingType == null)
+ {
+ return false;
+ }
+
+ var semanticInfo = semanticModel.GetSymbolInfo(this.SimpleNameOrMemberAccessExpression, cancellationToken);
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return false;
+ }
+
+ if (semanticInfo.Symbol != null)
+ {
+ return false;
+ }
+
+ // Either we found no matches, or this was ambiguous. Either way, we might be able
+ // to generate a method here. Determine where the user wants to generate the method
+ // into, and if it's valid then proceed.
+ INamedTypeSymbol typeToGenerateIn;
+ bool isStatic;
+ if (!service.TryDetermineTypeToGenerateIn(
+ document, containingType, simpleNameOrMemberAccessExpression, cancellationToken,
+ out typeToGenerateIn, out isStatic))
+ {
+ return false;
+ }
+
+ if (!isStatic)
+ {
+ return false;
+ }
+
+ this.TypeToGenerateIn = typeToGenerateIn;
+ return true;
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateEnumMember/CSharpGenerateEnumMemberService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateEnumMember/CSharpGenerateEnumMemberService.cs
new file mode 100644
index 0000000000..5852b0d939
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateEnumMember/CSharpGenerateEnumMemberService.cs
@@ -0,0 +1,54 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Composition;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Host;
+using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateMember.GenerateEnumMember
+{
+ public partial class CSharpGenerateEnumMemberService :
+ AbstractGenerateEnumMemberService<CSharpGenerateEnumMemberService, SimpleNameSyntax, ExpressionSyntax>
+ {
+ protected override bool IsIdentifierNameGeneration(SyntaxNode node)
+ {
+ return node is IdentifierNameSyntax;
+ }
+
+ protected override bool TryInitializeIdentifierNameState(
+ SemanticDocument document, SimpleNameSyntax identifierName, CancellationToken cancellationToken,
+ out SyntaxToken identifierToken, out ExpressionSyntax simpleNameOrMemberAccessExpression)
+ {
+ identifierToken = identifierName.Identifier;
+ if (identifierToken.ValueText != string.Empty &&
+ !identifierName.IsVar)
+ {
+ var memberAccess = identifierName.Parent as MemberAccessExpressionSyntax;
+ simpleNameOrMemberAccessExpression = memberAccess != null && memberAccess.Name == identifierName
+ ? (ExpressionSyntax)memberAccess
+ : identifierName;
+
+ // If we're being invoked, then don't offer this, offer generate method instead.
+ // Note: we could offer to generate a field with a delegate type. However, that's
+ // very esoteric and probably not what most users want.
+ if (simpleNameOrMemberAccessExpression.IsParentKind(SyntaxKind.InvocationExpression) ||
+ simpleNameOrMemberAccessExpression.IsParentKind(SyntaxKind.ObjectCreationExpression) ||
+ simpleNameOrMemberAccessExpression.IsParentKind(SyntaxKind.GotoStatement) ||
+ simpleNameOrMemberAccessExpression.IsParentKind(SyntaxKind.AliasQualifiedName))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ identifierToken = default(SyntaxToken);
+ simpleNameOrMemberAccessExpression = null;
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/AbstractGenerateConversionService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/AbstractGenerateConversionService.cs
new file mode 100644
index 0000000000..dc664d9f14
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/AbstractGenerateConversionService.cs
@@ -0,0 +1,135 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.Internal.Log;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateMember.GenerateParameterizedMember
+{
+ public abstract partial class AbstractGenerateConversionService<TService, TSimpleNameSyntax, TExpressionSyntax, TInvocationExpressionSyntax> :
+ AbstractGenerateParameterizedMemberService<TService, TSimpleNameSyntax, TExpressionSyntax, TInvocationExpressionSyntax>
+ where TService : AbstractGenerateConversionService<TService, TSimpleNameSyntax, TExpressionSyntax, TInvocationExpressionSyntax>
+ where TSimpleNameSyntax : TExpressionSyntax
+ where TExpressionSyntax : SyntaxNode
+ where TInvocationExpressionSyntax : TExpressionSyntax
+ {
+ protected abstract bool IsImplicitConversionGeneration(SyntaxNode node);
+ protected abstract bool IsExplicitConversionGeneration(SyntaxNode node);
+ protected abstract bool TryInitializeImplicitConversionState(SemanticDocument document, SyntaxNode expression, ISet<TypeKind> classInterfaceModuleStructTypes, CancellationToken cancellationToken, out SyntaxToken identifierToken, out IMethodSymbol methodSymbol, out INamedTypeSymbol typeToGenerateIn);
+ protected abstract bool TryInitializeExplicitConversionState(SemanticDocument document, SyntaxNode expression, ISet<TypeKind> classInterfaceModuleStructTypes, CancellationToken cancellationToken, out SyntaxToken identifierToken, out IMethodSymbol methodSymbol, out INamedTypeSymbol typeToGenerateIn);
+
+ public async Task<IEnumerable<CodeAction>> GenerateConversionAsync(
+ Document document,
+ SyntaxNode node,
+ CancellationToken cancellationToken)
+ {
+ var semanticDocument = await SemanticDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false);
+ var state = await State.GenerateConversionStateAsync((TService)this, semanticDocument, node, cancellationToken).ConfigureAwait(false);
+ if (state == null)
+ {
+ return SpecializedCollections.EmptyEnumerable<CodeAction>();
+ }
+
+ return GetActions(document, state, cancellationToken);
+ }
+
+ protected new class State : AbstractGenerateParameterizedMemberService<TService, TSimpleNameSyntax, TExpressionSyntax, TInvocationExpressionSyntax>.State
+ {
+ public static async Task<State> GenerateConversionStateAsync(
+ TService service,
+ SemanticDocument document,
+ SyntaxNode interfaceNode,
+ CancellationToken cancellationToken)
+ {
+ var state = new State();
+ if (!await state.TryInitializeConversionAsync(service, document, interfaceNode, cancellationToken).ConfigureAwait(false))
+ {
+ return null;
+ }
+
+ return state;
+ }
+
+ private Task<bool> TryInitializeConversionAsync(
+ TService service,
+ SemanticDocument document,
+ SyntaxNode node,
+ CancellationToken cancellationToken)
+ {
+ if (service.IsImplicitConversionGeneration(node))
+ {
+ if (!TryInitializeImplicitConversion(service, document, node, cancellationToken))
+ {
+ return Task.FromResult (false);
+ }
+ }
+ else if (service.IsExplicitConversionGeneration(node))
+ {
+ if (!TryInitializeExplicitConversion(service, document, node, cancellationToken))
+ {
+ return Task.FromResult (false);
+ }
+ }
+
+ return TryFinishInitializingState(service, document, cancellationToken);
+ }
+
+ private bool TryInitializeExplicitConversion(TService service, SemanticDocument document, SyntaxNode node, CancellationToken cancellationToken)
+ {
+ MethodKind = MethodKind.Conversion;
+ SyntaxToken identifierToken;
+ IMethodSymbol methodSymbol;
+ INamedTypeSymbol typeToGenerateIn;
+ if (!service.TryInitializeExplicitConversionState(
+ document, node, ClassInterfaceModuleStructTypes, cancellationToken,
+ out identifierToken, out methodSymbol, out typeToGenerateIn))
+ {
+ return false;
+ }
+
+ this.ContainingType = document.SemanticModel.GetEnclosingNamedType(node.SpanStart, cancellationToken);
+ if (ContainingType == null)
+ {
+ return false;
+ }
+
+ this.IdentifierToken = identifierToken;
+ this.TypeToGenerateIn = typeToGenerateIn;
+ this.SignatureInfo = new MethodSignatureInfo(document, this, methodSymbol);
+ this.location = node.GetLocation();
+ this.MethodGenerationKind = MethodGenerationKind.ExplicitConversion;
+ return true;
+ }
+
+ private bool TryInitializeImplicitConversion(TService service, SemanticDocument document, SyntaxNode node, CancellationToken cancellationToken)
+ {
+ MethodKind = MethodKind.Conversion;
+ SyntaxToken identifierToken;
+ IMethodSymbol methodSymbol;
+ INamedTypeSymbol typeToGenerateIn;
+ if (!service.TryInitializeImplicitConversionState(
+ document, node, ClassInterfaceModuleStructTypes, cancellationToken,
+ out identifierToken, out methodSymbol, out typeToGenerateIn))
+ {
+ return false;
+ }
+
+ this.ContainingType = document.SemanticModel.GetEnclosingNamedType(node.SpanStart, cancellationToken);
+ if (ContainingType == null)
+ {
+ return false;
+ }
+
+ this.IdentifierToken = identifierToken;
+ this.TypeToGenerateIn = typeToGenerateIn;
+ this.SignatureInfo = new MethodSignatureInfo(document, this, methodSymbol);
+ this.MethodGenerationKind = MethodGenerationKind.ImplicitConversion;
+ return true;
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/AbstractGenerateMethodService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/AbstractGenerateMethodService.cs
new file mode 100644
index 0000000000..9d7e794ee5
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/AbstractGenerateMethodService.cs
@@ -0,0 +1,266 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Linq;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.Internal.Log;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Editing;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateMember.GenerateParameterizedMember
+{
+ public abstract partial class AbstractGenerateMethodService<TService, TSimpleNameSyntax, TExpressionSyntax, TInvocationExpressionSyntax> :
+ AbstractGenerateParameterizedMemberService<TService, TSimpleNameSyntax, TExpressionSyntax, TInvocationExpressionSyntax>
+ where TService : AbstractGenerateMethodService<TService, TSimpleNameSyntax, TExpressionSyntax, TInvocationExpressionSyntax>
+ where TSimpleNameSyntax : TExpressionSyntax
+ where TExpressionSyntax : SyntaxNode
+ where TInvocationExpressionSyntax : TExpressionSyntax
+ {
+ protected abstract bool IsSimpleNameGeneration(SyntaxNode node);
+ protected abstract bool IsExplicitInterfaceGeneration(SyntaxNode node);
+ protected abstract bool TryInitializeExplicitInterfaceState(SemanticDocument document, SyntaxNode node, CancellationToken cancellationToken, out SyntaxToken identifierToken, out IMethodSymbol methodSymbol, out INamedTypeSymbol typeToGenerateIn);
+ protected abstract bool TryInitializeSimpleNameState(SemanticDocument document, TSimpleNameSyntax simpleName, CancellationToken cancellationToken, out SyntaxToken identifierToken, out TExpressionSyntax simpleNameOrMemberAccessExpression, out TInvocationExpressionSyntax invocationExpressionOpt, out bool isInConditionalExpression);
+ protected abstract ITypeSymbol CanGenerateMethodForSimpleNameOrMemberAccessExpression(SemanticModel semanticModel, TExpressionSyntax expresion, CancellationToken cancellationToken);
+
+ public async Task<IEnumerable<CodeAction>> GenerateMethodAsync(
+ Document document,
+ SyntaxNode node,
+ CancellationToken cancellationToken)
+ {
+ var semanticDocument = await SemanticDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false);
+ var state = await State.GenerateMethodStateAsync((TService)this, semanticDocument, node, cancellationToken).ConfigureAwait(false);
+ if (state == null)
+ {
+ return SpecializedCollections.EmptyEnumerable<CodeAction>();
+ }
+
+ return GetActions(document, state, cancellationToken);
+ }
+
+ internal protected new class State : AbstractGenerateParameterizedMemberService<TService, TSimpleNameSyntax, TExpressionSyntax, TInvocationExpressionSyntax>.State
+ {
+ public static async Task<State> GenerateMethodStateAsync(
+ TService service,
+ SemanticDocument document,
+ SyntaxNode interfaceNode,
+ CancellationToken cancellationToken)
+ {
+ var state = new State();
+ if (!await state.TryInitializeMethodAsync(service, document, interfaceNode, cancellationToken).ConfigureAwait(false))
+ {
+ return null;
+ }
+
+ return state;
+ }
+
+ private Task<bool> TryInitializeMethodAsync(
+ TService service,
+ SemanticDocument document,
+ SyntaxNode node,
+ CancellationToken cancellationToken)
+ {
+ // Cases that we deal with currently:
+ //
+ // 1) expr.Foo
+ // 2) expr->Foo
+ // 3) Foo
+ // 4) expr.Foo()
+ // 5) expr->Foo()
+ // 6) Foo()
+ // 7) ReturnType Explicit.Interface.Foo()
+ //
+ // In the first 3 invocationExpressionOpt will be null and we'll have to infer a
+ // delegate type in order to figure out the right method signature to generate. In
+ // the next 3 invocationExpressionOpt will be non null and will be used to figure
+ // out the types/name of the parameters to generate. In the last one, we're going to
+ // generate into an interface.
+ if (service.IsExplicitInterfaceGeneration(node))
+ {
+ if (!TryInitializeExplicitInterface(service, document, node, cancellationToken))
+ {
+ return Task.FromResult (false);
+ }
+ }
+ else if (service.IsSimpleNameGeneration(node))
+ {
+ if (!TryInitializeSimpleName(service, document, (TSimpleNameSyntax)node, cancellationToken))
+ {
+ return Task.FromResult (false);
+ }
+ }
+
+ return TryFinishInitializingState(service, document, cancellationToken);
+ }
+
+ private bool TryInitializeExplicitInterface(
+ TService service,
+ SemanticDocument document,
+ SyntaxNode methodDeclaration,
+ CancellationToken cancellationToken)
+ {
+ MethodKind = MethodKind.Ordinary;
+ SyntaxToken identifierToken;
+ IMethodSymbol methodSymbol;
+ INamedTypeSymbol typeToGenerateIn;
+ if (!service.TryInitializeExplicitInterfaceState(
+ document, methodDeclaration, cancellationToken,
+ out identifierToken, out methodSymbol, out typeToGenerateIn))
+ {
+ return false;
+ }
+
+ if (methodSymbol.ExplicitInterfaceImplementations.Any())
+ {
+ return false;
+ }
+
+ this.IdentifierToken = identifierToken;
+ this.TypeToGenerateIn = typeToGenerateIn;
+
+ cancellationToken.ThrowIfCancellationRequested();
+ var semanticModel = document.SemanticModel;
+ this.ContainingType = semanticModel.GetEnclosingNamedType(methodDeclaration.SpanStart, cancellationToken);
+ if (this.ContainingType == null)
+ {
+ return false;
+ }
+
+ if (!this.ContainingType.Interfaces.Contains(this.TypeToGenerateIn))
+ {
+ return false;
+ }
+
+ this.SignatureInfo = new MethodSignatureInfo(document, this, methodSymbol);
+ return true;
+ }
+
+ private bool TryInitializeSimpleName(
+ TService service,
+ SemanticDocument document,
+ TSimpleNameSyntax simpleName,
+ CancellationToken cancellationToken)
+ {
+ MethodKind = MethodKind.Ordinary;
+ this.SimpleNameOpt = simpleName;
+
+ SyntaxToken identifierToken;
+ TExpressionSyntax simpleNameOrMemberAccessExpression;
+ TInvocationExpressionSyntax invocationExpressionOpt;
+ bool isInConditionalExpression;
+ if (!service.TryInitializeSimpleNameState(
+ document, simpleName, cancellationToken,
+ out identifierToken, out simpleNameOrMemberAccessExpression, out invocationExpressionOpt, out isInConditionalExpression))
+ {
+ return false;
+ }
+
+ this.IdentifierToken = identifierToken;
+ this.SimpleNameOrMemberAccessExpression = simpleNameOrMemberAccessExpression;
+ this.InvocationExpressionOpt = invocationExpressionOpt;
+ this.IsInConditionalAccessExpression = isInConditionalExpression;
+
+ if (string.IsNullOrWhiteSpace(this.IdentifierToken.ValueText))
+ {
+ return false;
+ }
+
+ // If we're not in a type, don't even bother. NOTE(cyrusn): We'll have to rethink this
+ // for C# Script.
+ cancellationToken.ThrowIfCancellationRequested();
+ var semanticModel = document.SemanticModel;
+ this.ContainingType = semanticModel.GetEnclosingNamedType(this.SimpleNameOpt.SpanStart, cancellationToken);
+ if (this.ContainingType == null)
+ {
+ return false;
+ }
+
+ if (this.InvocationExpressionOpt != null)
+ {
+ this.SignatureInfo = service.CreateInvocationMethodInfo(document, this);
+ }
+ else
+ {
+ var delegateType = TypeGuessing.typeInferenceService.InferDelegateType(semanticModel, this.SimpleNameOrMemberAccessExpression, cancellationToken);
+ if (delegateType != null && delegateType.DelegateInvokeMethod != null)
+ {
+ this.SignatureInfo = new MethodSignatureInfo(document, this, delegateType.DelegateInvokeMethod);
+ }
+ else
+ {
+ // We don't have and invocation expression or a delegate, but we may have a special expression without parenthesis. Lets see
+ // if the type inference service can directly infer the type for our expression.
+ var expressionType = service.CanGenerateMethodForSimpleNameOrMemberAccessExpression(semanticModel, this.SimpleNameOrMemberAccessExpression, cancellationToken);
+ if (expressionType == null)
+ {
+ return false;
+ }
+
+ this.SignatureInfo = new MethodSignatureInfo(document, this, CreateMethodSymbolWithReturnType(expressionType));
+ }
+ }
+
+ // Now, try to bind the invocation and see if it succeeds or not. if it succeeds and
+ // binds uniquely, then we don't need to offer this quick fix.
+ cancellationToken.ThrowIfCancellationRequested();
+
+ // If the name bound with errors, then this is a candidate for generate method.
+ var semanticInfo = semanticModel.GetSymbolInfo(this.SimpleNameOrMemberAccessExpression, cancellationToken);
+ if (semanticInfo.GetAllSymbols().Any(s => s.Kind == SymbolKind.Local || s.Kind == SymbolKind.Parameter) &&
+ !service.AreSpecialOptionsActive(semanticModel))
+ {
+ // if the name bound to something in scope then we don't want to generate the
+ // method because it will be shadowed by what's in scope. Unless we are in a
+ // special state such as Option Strict On where we want to generate fixes even
+ // if we shadow types.
+ return false;
+ }
+
+ // Check if the symbol is on the list of valid symbols for this language.
+ cancellationToken.ThrowIfCancellationRequested();
+ if (semanticInfo.Symbol != null && !service.IsValidSymbol(semanticInfo.Symbol, semanticModel))
+ {
+ return false;
+ }
+
+ // Either we found no matches, or this was ambiguous. Either way, we might be able
+ // to generate a method here. Determine where the user wants to generate the method
+ // into, and if it's valid then proceed.
+ cancellationToken.ThrowIfCancellationRequested();
+ INamedTypeSymbol typeToGenerateIn;
+ bool isStatic;
+ if (!service.TryDetermineTypeToGenerateIn(
+ document, this.ContainingType, this.SimpleNameOrMemberAccessExpression, cancellationToken,
+ out typeToGenerateIn, out isStatic))
+ {
+ return false;
+ }
+
+ var expressionSyntax = (this.InvocationExpressionOpt ?? this.SimpleNameOrMemberAccessExpression) as ExpressionSyntax;
+ this.IsWrittenTo = expressionSyntax.IsWrittenTo();
+ this.TypeToGenerateIn = typeToGenerateIn;
+ this.IsStatic = isStatic;
+ this.MethodGenerationKind = MethodGenerationKind.Member;
+ return true;
+ }
+
+ private static IMethodSymbol CreateMethodSymbolWithReturnType(ITypeSymbol expressionType)
+ {
+ return CodeGenerationSymbolFactory.CreateMethodSymbol(
+ attributes: SpecializedCollections.EmptyList<AttributeData>(),
+ accessibility: default(Accessibility),
+ modifiers: default(DeclarationModifiers),
+ returnType: expressionType,
+ explicitInterfaceSymbol: null,
+ name: null,
+ typeParameters: SpecializedCollections.EmptyList<ITypeParameterSymbol>(),
+ parameters: SpecializedCollections.EmptyList<IParameterSymbol>());
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.cs
new file mode 100644
index 0000000000..f70958dcd8
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.cs
@@ -0,0 +1,579 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis;
+using System.Linq;
+using System;
+using System.Threading.Tasks;
+using ICSharpCode.NRefactory6.CSharp.CodeGeneration;
+using Microsoft.CodeAnalysis.Editing;
+using System.Collections.Immutable;
+using Microsoft.CodeAnalysis.FindSymbols;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateMember.GenerateParameterizedMember
+{
+ public abstract partial class AbstractGenerateParameterizedMemberService<TService, TSimpleNameSyntax, TExpressionSyntax, TInvocationExpressionSyntax> :
+ AbstractGenerateMemberService<TSimpleNameSyntax, TExpressionSyntax>
+ where TService : AbstractGenerateParameterizedMemberService<TService, TSimpleNameSyntax, TExpressionSyntax, TInvocationExpressionSyntax>
+ where TSimpleNameSyntax : TExpressionSyntax
+ where TExpressionSyntax : SyntaxNode
+ where TInvocationExpressionSyntax : TExpressionSyntax
+ {
+ protected AbstractGenerateParameterizedMemberService()
+ {
+ }
+
+ protected abstract AbstractInvocationInfo CreateInvocationMethodInfo(SemanticDocument document, State abstractState);
+
+ protected abstract bool IsValidSymbol(ISymbol symbol, SemanticModel semanticModel);
+ protected abstract bool AreSpecialOptionsActive(SemanticModel semanticModel);
+
+ protected virtual bool ContainingTypesOrSelfHasUnsafeKeyword(INamedTypeSymbol containingType)
+ {
+ return false;
+ }
+
+ protected virtual string GetImplicitConversionDisplayText(State state)
+ {
+ return string.Empty;
+ }
+
+ protected virtual string GetExplicitConversionDisplayText(State state)
+ {
+ return string.Empty;
+ }
+
+ protected IEnumerable<CodeAction> GetActions(Document document, State state, CancellationToken cancellationToken)
+ {
+ yield return new GenerateParameterizedMemberCodeAction((TService)this, document, state, isAbstract: false, generateProperty: false);
+
+ // If we're trying to generate an instance method into an abstract class (but not a
+ // static class or an interface), then offer to generate it abstractly.
+ var canGenerateAbstractly = state.TypeToGenerateIn.IsAbstract &&
+ !state.TypeToGenerateIn.IsStatic &&
+ state.TypeToGenerateIn.TypeKind != TypeKind.Interface &&
+ !state.IsStatic;
+
+ if (canGenerateAbstractly)
+ {
+ yield return new GenerateParameterizedMemberCodeAction((TService)this, document, state, isAbstract: true, generateProperty: false);
+ }
+
+ if (true/*semanticFacts.SupportsParameterizedProperties*/ &&
+ state.InvocationExpressionOpt != null)
+ {
+ var typeParameters = state.SignatureInfo.DetermineTypeParameters(cancellationToken);
+ var returnType = state.SignatureInfo.DetermineReturnType(cancellationToken);
+
+ if (typeParameters.Count == 0 && returnType.SpecialType != SpecialType.System_Void)
+ {
+ yield return new GenerateParameterizedMemberCodeAction((TService)this, document, state, isAbstract: false, generateProperty: true);
+
+ if (canGenerateAbstractly)
+ {
+ yield return new GenerateParameterizedMemberCodeAction((TService)this, document, state, isAbstract: true, generateProperty: true);
+ }
+ }
+ }
+ }
+ internal protected abstract class AbstractInvocationInfo : SignatureInfo
+ {
+ protected abstract bool IsIdentifierName();
+
+ protected abstract IList<ITypeParameterSymbol> GetCapturedTypeParameters(CancellationToken cancellationToken);
+ protected abstract IList<ITypeParameterSymbol> GenerateTypeParameters(CancellationToken cancellationToken);
+
+ protected AbstractInvocationInfo(SemanticDocument document, State state)
+ : base(document, state)
+ {
+ }
+
+ public override IList<ITypeParameterSymbol> DetermineTypeParameters(CancellationToken cancellationToken)
+ {
+ var typeParameters = DetermineTypeParametersWorker(cancellationToken);
+ return typeParameters.Select(tp => MassageTypeParameter(tp, cancellationToken)).ToList();
+ }
+
+ private IList<ITypeParameterSymbol> DetermineTypeParametersWorker(
+ CancellationToken cancellationToken)
+ {
+ if (IsIdentifierName())
+ {
+ // If the user wrote something like Foo(x) then we still might want to generate
+ // a generic method if the expression 'x' captured any method type variables.
+ var capturedTypeParameters = GetCapturedTypeParameters(cancellationToken);
+ var availableTypeParameters = this.State.TypeToGenerateIn.GetAllTypeParameters();
+ var result = capturedTypeParameters.Except(availableTypeParameters).ToList();
+ return result;
+ }
+ else
+ {
+ return GenerateTypeParameters(cancellationToken);
+ }
+ }
+
+ private ITypeParameterSymbol MassageTypeParameter(
+ ITypeParameterSymbol typeParameter,
+ CancellationToken cancellationToken)
+ {
+ var constraints = typeParameter.ConstraintTypes.Where(ts => !ts.IsUnexpressableTypeParameterConstraint()).ToList();
+ var classTypes = constraints.Where(ts => ts.TypeKind == TypeKind.Class).ToList();
+ var nonClassTypes = constraints.Where(ts => ts.TypeKind != TypeKind.Class).ToList();
+
+ classTypes = MergeClassTypes(classTypes, cancellationToken);
+ constraints = classTypes.Concat(nonClassTypes).ToList();
+ if (constraints.SequenceEqual(typeParameter.ConstraintTypes))
+ {
+ return typeParameter;
+ }
+
+ return CodeGenerationSymbolFactory.CreateTypeParameter(
+ attributes: null,
+ varianceKind: typeParameter.Variance,
+ name: typeParameter.Name,
+ constraintTypes: ImmutableArray.CreateRange<ITypeSymbol>(constraints),
+ hasConstructorConstraint: typeParameter.HasConstructorConstraint,
+ hasReferenceConstraint: typeParameter.HasReferenceTypeConstraint,
+ hasValueConstraint: typeParameter.HasValueTypeConstraint);
+ }
+
+ private List<ITypeSymbol> MergeClassTypes(List<ITypeSymbol> classTypes, CancellationToken cancellationToken)
+ {
+ var compilation = this.Document.SemanticModel.Compilation;
+ for (int i = classTypes.Count - 1; i >= 0; i--)
+ {
+ // For example, 'Attribute'.
+ var type1 = classTypes[i];
+
+ for (int j = 0; j < classTypes.Count; j++)
+ {
+ if (j != i)
+ {
+ // For example 'FooAttribute'.
+ var type2 = classTypes[j];
+
+ if (IsImplicitReferenceConversion(compilation, type2, type1))
+ {
+ // If there's an implicit reference conversion (i.e. from
+ // FooAttribute to Attribute), then we don't need Attribute as it's
+ // implied by the second attribute;
+ classTypes.RemoveAt(i);
+ break;
+ }
+ }
+ }
+ }
+
+ return classTypes;
+ }
+
+ protected abstract bool IsImplicitReferenceConversion(Compilation compilation, ITypeSymbol sourceType, ITypeSymbol targetType);
+ }
+ private partial class GenerateParameterizedMemberCodeAction : CodeAction
+ {
+ private readonly TService _service;
+ private readonly Document _document;
+ private readonly State _state;
+ private readonly bool _isAbstract;
+ private readonly bool _generateProperty;
+
+ public GenerateParameterizedMemberCodeAction(
+ TService service,
+ Document document,
+ State state,
+ bool isAbstract,
+ bool generateProperty)
+ {
+ _service = service;
+ _document = document;
+ _state = state;
+ _isAbstract = isAbstract;
+ _generateProperty = generateProperty;
+ }
+
+ private string GetDisplayText(
+ State state,
+ bool isAbstract,
+ bool generateProperty)
+ {
+ switch (state.MethodGenerationKind)
+ {
+ case MethodGenerationKind.Member:
+ var text = generateProperty ?
+ isAbstract ? Resources.GenerateAbstractProperty : Resources.GeneratePropertyIn :
+ isAbstract ? Resources.GenerateAbstractMethod : Resources.GenerateMethodIn;
+
+ var name = state.IdentifierToken.ValueText;
+ var destination = state.TypeToGenerateIn.Name;
+ return string.Format(text, name, destination);
+ case MethodGenerationKind.ImplicitConversion:
+ return _service.GetImplicitConversionDisplayText(_state);
+ case MethodGenerationKind.ExplicitConversion:
+ return _service.GetExplicitConversionDisplayText(_state);
+ default:
+ throw new InvalidOperationException();
+ }
+ }
+
+ protected override async Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken)
+ {
+ var syntaxTree = await _document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
+ var syntaxFactory = _document.Project.Solution.Workspace.Services.GetLanguageServices(_state.TypeToGenerateIn.Language).GetService<SyntaxGenerator>();
+
+ if (_generateProperty)
+ {
+ var property = _state.SignatureInfo.GenerateProperty(syntaxFactory, _isAbstract, _state.IsWrittenTo, cancellationToken);
+
+ var result = await CodeGenerator.AddPropertyDeclarationAsync(
+ _document.Project.Solution,
+ _state.TypeToGenerateIn,
+ property,
+ new CodeGenerationOptions(afterThisLocation: _state.IdentifierToken.GetLocation(), generateDefaultAccessibility: false),
+ cancellationToken)
+ .ConfigureAwait(false);
+
+ return result;
+ }
+ else
+ {
+ var method = _state.SignatureInfo.GenerateMethod(syntaxFactory, _isAbstract, cancellationToken);
+
+ var result = await CodeGenerator.AddMethodDeclarationAsync(
+ _document.Project.Solution,
+ _state.TypeToGenerateIn,
+ method,
+ new CodeGenerationOptions(afterThisLocation: _state.Location, generateDefaultAccessibility: false),
+ cancellationToken)
+ .ConfigureAwait(false);
+
+ return result;
+ }
+ }
+
+ public override string Title
+ {
+ get
+ {
+ return GetDisplayText(_state, _isAbstract, _generateProperty);
+ }
+ }
+ }
+
+ protected class MethodSignatureInfo : SignatureInfo
+ {
+ private readonly IMethodSymbol _methodSymbol;
+
+ public MethodSignatureInfo(
+ SemanticDocument document,
+ State state,
+ IMethodSymbol methodSymbol)
+ : base(document, state)
+ {
+ _methodSymbol = methodSymbol;
+ }
+
+ protected override ITypeSymbol DetermineReturnTypeWorker(CancellationToken cancellationToken)
+ {
+ if (State.IsInConditionalAccessExpression)
+ {
+ return _methodSymbol.ReturnType.RemoveNullableIfPresent();
+ }
+
+ return _methodSymbol.ReturnType;
+ }
+
+ public override IList<ITypeParameterSymbol> DetermineTypeParameters(CancellationToken cancellationToken)
+ {
+ return _methodSymbol.TypeParameters;
+ }
+
+ protected override IList<RefKind> DetermineParameterModifiers(CancellationToken cancellationToken)
+ {
+ return _methodSymbol.Parameters.Select(p => p.RefKind).ToList();
+ }
+
+ protected override IList<bool> DetermineParameterOptionality(CancellationToken cancellationToken)
+ {
+ return _methodSymbol.Parameters.Select(p => p.IsOptional).ToList();
+ }
+
+ protected override IList<ITypeSymbol> DetermineParameterTypes(CancellationToken cancellationToken)
+ {
+ return _methodSymbol.Parameters.Select(p => p.Type).ToList();
+ }
+
+ protected override IList<string> DetermineParameterNames(CancellationToken cancellationToken)
+ {
+ return _methodSymbol.Parameters.Select(p => p.Name).ToList();
+ }
+ }
+
+ internal protected abstract class SignatureInfo
+ {
+ protected readonly SemanticDocument Document;
+ protected readonly State State;
+
+ public SignatureInfo(
+ SemanticDocument document,
+ State state)
+ {
+ this.Document = document;
+ this.State = state;
+ }
+
+ public abstract IList<ITypeParameterSymbol> DetermineTypeParameters(CancellationToken cancellationToken);
+ public ITypeSymbol DetermineReturnType(CancellationToken cancellationToken)
+ {
+ return FixType(DetermineReturnTypeWorker(cancellationToken), cancellationToken);
+ }
+
+ protected abstract ITypeSymbol DetermineReturnTypeWorker(CancellationToken cancellationToken);
+ protected abstract IList<RefKind> DetermineParameterModifiers(CancellationToken cancellationToken);
+ protected abstract IList<ITypeSymbol> DetermineParameterTypes(CancellationToken cancellationToken);
+ protected abstract IList<bool> DetermineParameterOptionality(CancellationToken cancellationToken);
+ protected abstract IList<string> DetermineParameterNames(CancellationToken cancellationToken);
+
+ internal IPropertySymbol GenerateProperty(
+ SyntaxGenerator factory,
+ bool isAbstract, bool includeSetter,
+ CancellationToken cancellationToken)
+ {
+ var accessibility = DetermineAccessibility(isAbstract);
+ var getMethod = CodeGenerationSymbolFactory.CreateAccessorSymbol(
+ attributes: null,
+ accessibility: accessibility,
+ statements: GenerateStatements(factory, isAbstract, cancellationToken));
+
+ var setMethod = includeSetter ? getMethod : null;
+
+ return CodeGenerationSymbolFactory.CreatePropertySymbol(
+ attributes: null,
+ accessibility: accessibility,
+ modifiers: DeclarationModifiers.None.WithIsStatic(State.IsStatic).WithIsAbstract (isAbstract),
+ type: DetermineReturnType(cancellationToken),
+ explicitInterfaceSymbol: null,
+ name: this.State.IdentifierToken.ValueText,
+ parameters: DetermineParameters(cancellationToken),
+ getMethod: getMethod,
+ setMethod: setMethod);
+ }
+
+ public IMethodSymbol GenerateMethod(
+ SyntaxGenerator factory,
+ bool isAbstract,
+ CancellationToken cancellationToken)
+ {
+ var parameters = DetermineParameters(cancellationToken);
+ var returnType = DetermineReturnType(cancellationToken);
+ var isUnsafe = (parameters
+ .Any(p => p.Type.IsUnsafe()) || returnType.IsUnsafe()) &&
+ !State.IsContainedInUnsafeType;
+ var method = CodeGenerationSymbolFactory.CreateMethodSymbol(
+ attributes: null,
+ accessibility: DetermineAccessibility(isAbstract),
+ modifiers: DeclarationModifiers.None.WithIsStatic(State.IsStatic).WithIsAbstract (isAbstract).WithIsUnsafe (isUnsafe),
+ returnType: returnType,
+ explicitInterfaceSymbol: null,
+ name: this.State.IdentifierToken.ValueText,
+ typeParameters: DetermineTypeParameters(cancellationToken),
+ parameters: parameters,
+ statements: GenerateStatements(factory, isAbstract, cancellationToken),
+ handlesExpressions: null,
+ returnTypeAttributes: null,
+ methodKind: State.MethodKind);
+
+ // Ensure no conflicts between type parameter names and parameter names.
+ var languageServiceProvider = this.Document.Project.Solution.Workspace.Services.GetLanguageServices(this.State.TypeToGenerateIn.Language);
+
+ var equalityComparer = true ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase;
+ var reservedParameterNames = this.DetermineParameterNames(cancellationToken).ToSet(equalityComparer);
+ var newTypeParameterNames = NameGenerator.EnsureUniqueness(
+ method.TypeParameters.Select(t => t.Name).ToList(), n => !reservedParameterNames.Contains(n));
+
+ return method.RenameTypeParameters(newTypeParameterNames);
+ }
+
+ private ITypeSymbol FixType(
+ ITypeSymbol typeSymbol,
+ CancellationToken cancellationToken)
+ {
+ // A type can't refer to a type parameter that isn't available in the type we're
+ // eventually generating into.
+ var availableMethodTypeParameters = this.DetermineTypeParameters(cancellationToken);
+ var availableTypeParameters = this.State.TypeToGenerateIn.GetAllTypeParameters();
+
+ var compilation = this.Document.SemanticModel.Compilation;
+ var allTypeParameters = availableMethodTypeParameters.Concat(availableTypeParameters);
+
+ return typeSymbol.RemoveAnonymousTypes(compilation)
+ .ReplaceTypeParametersBasedOnTypeConstraints(compilation, allTypeParameters, this.Document.Document.Project.Solution, cancellationToken)
+ .RemoveUnavailableTypeParameters(compilation, allTypeParameters)
+ .RemoveUnnamedErrorTypes(compilation);
+ }
+
+ private IList<SyntaxNode> GenerateStatements(
+ SyntaxGenerator factory,
+ bool isAbstract,
+ CancellationToken cancellationToken)
+ {
+ var throwStatement = CodeGenerationHelpers.GenerateThrowStatement(factory, this.Document, "System.NotImplementedException", cancellationToken);
+
+ return isAbstract || State.TypeToGenerateIn.TypeKind == TypeKind.Interface || throwStatement == null
+ ? null
+ : new[] { throwStatement };
+ }
+
+ private IList<IParameterSymbol> DetermineParameters(CancellationToken cancellationToken)
+ {
+ var modifiers = DetermineParameterModifiers(cancellationToken);
+ var types = DetermineParameterTypes(cancellationToken).Select(t => FixType(t, cancellationToken)).ToList();
+ var optionality = DetermineParameterOptionality(cancellationToken);
+ var names = DetermineParameterNames(cancellationToken);
+
+ var result = new List<IParameterSymbol>();
+ for (var i = 0; i < modifiers.Count; i++)
+ {
+ result.Add(CodeGenerationSymbolFactory.CreateParameterSymbol(
+ attributes: null,
+ refKind: modifiers[i],
+ isParams: false,
+ isOptional: optionality[i],
+ type: types[i],
+ name: names[i]));
+ }
+
+ return result;
+ }
+
+ private Accessibility DetermineAccessibility(bool isAbstract)
+ {
+ var containingType = this.State.ContainingType;
+
+ // If we're generating into an interface, then we don't use any modifiers.
+ if (State.TypeToGenerateIn.TypeKind != TypeKind.Interface)
+ {
+ // Otherwise, figure out what accessibility modifier to use and optionally
+ // mark it as static.
+ if (containingType.IsContainedWithin(State.TypeToGenerateIn) && !isAbstract)
+ {
+ return Accessibility.Private;
+ }
+ else if (DerivesFrom(containingType) && State.IsStatic)
+ {
+ // NOTE(cyrusn): We only generate protected in the case of statics. Consider
+ // the case where we're generating into one of our base types. i.e.:
+ //
+ // class B : A { void Foo() { A a; a.Foo(); }
+ //
+ // In this case we can *not* mark the method as protected. 'B' can only
+ // access protected members of 'A' through an instance of 'B' (or a subclass
+ // of B). It can not access protected members through an instance of the
+ // superclass. In this case we need to make the method public or internal.
+ //
+ // However, this does not apply if the method will be static. i.e.
+ //
+ // class B : A { void Foo() { A.Foo(); }
+ //
+ // B can access the protected statics of A, and so we generate 'Foo' as
+ // protected.
+
+ // TODO: Code coverage
+ return Accessibility.Protected;
+ }
+ else if (containingType.ContainingAssembly.IsSameAssemblyOrHasFriendAccessTo(State.TypeToGenerateIn.ContainingAssembly))
+ {
+ return Accessibility.Internal;
+ }
+ else
+ {
+ // TODO: Code coverage
+ return Accessibility.Public;
+ }
+ }
+
+ return Accessibility.NotApplicable;
+ }
+
+ private bool DerivesFrom(INamedTypeSymbol containingType)
+ {
+ return containingType.GetBaseTypes().Select(t => t.OriginalDefinition)
+ .OfType<INamedTypeSymbol>()
+ .Contains(State.TypeToGenerateIn);
+ }
+ }
+
+ internal protected abstract class State
+ {
+ public INamedTypeSymbol ContainingType { get; protected set; }
+ public INamedTypeSymbol TypeToGenerateIn { get; protected set; }
+ public bool IsStatic { get; protected set; }
+ public bool IsContainedInUnsafeType { get; protected set; }
+
+ // Just the name of the method. i.e. "Foo" in "X.Foo" or "X.Foo()"
+ public SyntaxToken IdentifierToken { get; protected set; }
+ public TSimpleNameSyntax SimpleNameOpt { get; protected set; }
+
+ // The entire expression containing the name, not including the invocation. i.e. "X.Foo"
+ // in "X.Foo()".
+ public TExpressionSyntax SimpleNameOrMemberAccessExpression { get; protected set; }
+ public TInvocationExpressionSyntax InvocationExpressionOpt { get; protected set; }
+ public bool IsInConditionalAccessExpression { get; protected set; }
+
+ public bool IsWrittenTo { get; protected set; }
+
+ public SignatureInfo SignatureInfo { get; protected set; }
+ public MethodKind MethodKind { get; internal set; }
+ public MethodGenerationKind MethodGenerationKind { get; protected set; }
+ protected Location location = null;
+ public Location Location
+ {
+ get
+ {
+ if (IdentifierToken.SyntaxTree != null)
+ {
+ return IdentifierToken.GetLocation();
+ }
+
+ return location;
+ }
+ }
+
+ protected async Task<bool> TryFinishInitializingState(TService service, SemanticDocument document, CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ this.TypeToGenerateIn = await SymbolFinder.FindSourceDefinitionAsync(this.TypeToGenerateIn, document.Project.Solution, cancellationToken).ConfigureAwait(false) as INamedTypeSymbol;
+ if (this.TypeToGenerateIn.IsErrorType())
+ {
+ return false;
+ }
+
+ if (!service.ValidateTypeToGenerateIn(document.Project.Solution, this.TypeToGenerateIn,
+ this.IsStatic, ClassInterfaceModuleStructTypes, cancellationToken))
+ {
+ return false;
+ }
+
+ if (!new CSharpCodeGenerationService(document.Project.Solution.Workspace).CanAddTo(this.TypeToGenerateIn, document.Project.Solution, cancellationToken))
+ {
+ return false;
+ }
+
+ // Ok. It either didn't bind to any symbols, or it bound to a symbol but with
+ // errors. In the former case we definitely want to offer to generate a method. In
+ // the latter case, we want to generate a method *unless* there's an existing method
+ // with the same signature.
+ var existingMethods = this.TypeToGenerateIn.GetMembers(this.IdentifierToken.ValueText)
+ .OfType<IMethodSymbol>();
+
+ var destinationProvider = document.Project.Solution.Workspace.Services.GetLanguageServices(this.TypeToGenerateIn.Language);
+ var syntaxFactory = destinationProvider.GetService<SyntaxGenerator>();
+ this.IsContainedInUnsafeType = service.ContainingTypesOrSelfHasUnsafeKeyword(this.TypeToGenerateIn);
+ var generatedMethod = this.SignatureInfo.GenerateMethod(syntaxFactory, false, cancellationToken);
+ return !existingMethods.Any(m => SignatureComparer.HaveSameSignature(m, generatedMethod, caseSensitive: true, compareParameterName: true, isParameterCaseSensitive: true));
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/CSharpCommonGenerationServiceMethods.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/CSharpCommonGenerationServiceMethods.cs
new file mode 100644
index 0000000000..1f9a4ae970
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/CSharpCommonGenerationServiceMethods.cs
@@ -0,0 +1,36 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Composition;
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeGeneration;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Symbols;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.FindSymbols;
+using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Shared.Utilities;
+using Roslyn.Utilities;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateMember.GenerateParameterizedMember
+{
+ internal static class CSharpCommonGenerationServiceMethods
+ {
+ public static bool AreSpecialOptionsActive(SemanticModel semanticModel)
+ {
+ return false;
+ }
+
+ public static bool IsValidSymbol(ISymbol symbol, SemanticModel semanticModel)
+ {
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/CSharpGenerateConversionService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/CSharpGenerateConversionService.cs
new file mode 100644
index 0000000000..a1ba0631c4
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/CSharpGenerateConversionService.cs
@@ -0,0 +1,233 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Composition;
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeGeneration;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Symbols;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Editing;
+using Microsoft.CodeAnalysis.FindSymbols;
+using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateMember.GenerateParameterizedMember
+{
+ public partial class CSharpGenerateConversionService :
+ AbstractGenerateConversionService<CSharpGenerateConversionService, SimpleNameSyntax, ExpressionSyntax, InvocationExpressionSyntax>
+ {
+ protected override bool IsImplicitConversionGeneration(SyntaxNode node)
+ {
+ return node is ExpressionSyntax &&
+ (node.Parent is AssignmentExpressionSyntax || node.Parent is EqualsValueClauseSyntax) &&
+ !(node is CastExpressionSyntax) &&
+ !(node is MemberAccessExpressionSyntax);
+ }
+
+ protected override bool IsExplicitConversionGeneration(SyntaxNode node)
+ {
+ return node is CastExpressionSyntax;
+ }
+
+ protected override bool ContainingTypesOrSelfHasUnsafeKeyword(INamedTypeSymbol containingType)
+ {
+ return containingType.ContainingTypesOrSelfHasUnsafeKeyword();
+ }
+
+ protected override AbstractInvocationInfo CreateInvocationMethodInfo(SemanticDocument document, AbstractGenerateParameterizedMemberService<CSharpGenerateConversionService, SimpleNameSyntax, ExpressionSyntax, InvocationExpressionSyntax>.State state)
+ {
+ return new CSharpGenerateParameterizedMemberService<CSharpGenerateConversionService>.InvocationExpressionInfo(document, state);
+ }
+
+ protected override bool AreSpecialOptionsActive(SemanticModel semanticModel)
+ {
+ return CSharpCommonGenerationServiceMethods.AreSpecialOptionsActive(semanticModel);
+ }
+
+ protected override bool IsValidSymbol(ISymbol symbol, SemanticModel semanticModel)
+ {
+ return CSharpCommonGenerationServiceMethods.IsValidSymbol(symbol, semanticModel);
+ }
+
+ protected override bool TryInitializeImplicitConversionState(
+ SemanticDocument document,
+ SyntaxNode expression,
+ ISet<TypeKind> classInterfaceModuleStructTypes,
+ CancellationToken cancellationToken,
+ out SyntaxToken identifierToken,
+ out IMethodSymbol methodSymbol,
+ out INamedTypeSymbol typeToGenerateIn)
+ {
+ if (TryGetConversionMethodAndTypeToGenerateIn(document, expression, classInterfaceModuleStructTypes, cancellationToken, out methodSymbol, out typeToGenerateIn))
+ {
+ identifierToken = SyntaxFactory.Token(
+ default(SyntaxTriviaList),
+ SyntaxKind.ImplicitKeyword,
+ WellKnownMemberNames.ImplicitConversionName,
+ WellKnownMemberNames.ImplicitConversionName,
+ default(SyntaxTriviaList));
+ return true;
+ }
+
+ identifierToken = default(SyntaxToken);
+ methodSymbol = null;
+ typeToGenerateIn = null;
+ return false;
+ }
+
+ protected override bool TryInitializeExplicitConversionState(
+ SemanticDocument document,
+ SyntaxNode expression,
+ ISet<TypeKind> classInterfaceModuleStructTypes,
+ CancellationToken cancellationToken,
+ out SyntaxToken identifierToken,
+ out IMethodSymbol methodSymbol,
+ out INamedTypeSymbol typeToGenerateIn)
+ {
+ if (TryGetConversionMethodAndTypeToGenerateIn(document, expression, classInterfaceModuleStructTypes, cancellationToken, out methodSymbol, out typeToGenerateIn))
+ {
+ identifierToken = SyntaxFactory.Token(
+ default(SyntaxTriviaList),
+ SyntaxKind.ImplicitKeyword,
+ WellKnownMemberNames.ExplicitConversionName,
+ WellKnownMemberNames.ExplicitConversionName,
+ default(SyntaxTriviaList));
+ return true;
+ }
+
+ identifierToken = default(SyntaxToken);
+ methodSymbol = null;
+ typeToGenerateIn = null;
+ return false;
+ }
+
+ private bool TryGetConversionMethodAndTypeToGenerateIn(
+ SemanticDocument document,
+ SyntaxNode expression,
+ ISet<TypeKind> classInterfaceModuleStructTypes,
+ CancellationToken cancellationToken,
+ out IMethodSymbol methodSymbol,
+ out INamedTypeSymbol typeToGenerateIn)
+ {
+ var castExpression = expression as CastExpressionSyntax;
+ if (castExpression != null)
+ {
+ return TryGetExplicitConversionMethodAndTypeToGenerateIn(
+ document,
+ castExpression,
+ classInterfaceModuleStructTypes,
+ cancellationToken,
+ out methodSymbol,
+ out typeToGenerateIn);
+ }
+
+ return TryGetImplicitConversionMethodAndTypeToGenerateIn(
+ document,
+ expression,
+ classInterfaceModuleStructTypes,
+ cancellationToken,
+ out methodSymbol,
+ out typeToGenerateIn);
+ }
+
+ private bool TryGetExplicitConversionMethodAndTypeToGenerateIn(
+ SemanticDocument document,
+ CastExpressionSyntax castExpression,
+ ISet<TypeKind> classInterfaceModuleStructTypes,
+ CancellationToken cancellationToken,
+ out IMethodSymbol methodSymbol,
+ out INamedTypeSymbol typeToGenerateIn)
+ {
+ methodSymbol = null;
+ typeToGenerateIn = document.SemanticModel.GetTypeInfo(castExpression.Type, cancellationToken).Type as INamedTypeSymbol;
+ var parameterSymbol = document.SemanticModel.GetTypeInfo(castExpression.Expression, cancellationToken).Type as INamedTypeSymbol;
+ if (typeToGenerateIn == null || parameterSymbol == null || typeToGenerateIn.IsErrorType() || parameterSymbol.IsErrorType())
+ {
+ return false;
+ }
+
+ methodSymbol = GenerateMethodSymbol(typeToGenerateIn, parameterSymbol);
+
+ if (!ValidateTypeToGenerateIn(
+ document.Project.Solution,
+ typeToGenerateIn,
+ true,
+ classInterfaceModuleStructTypes,
+ cancellationToken))
+ {
+ typeToGenerateIn = parameterSymbol;
+ }
+
+ return true;
+ }
+
+ private bool TryGetImplicitConversionMethodAndTypeToGenerateIn(
+ SemanticDocument document,
+ SyntaxNode expression,
+ ISet<TypeKind> classInterfaceModuleStructTypes,
+ CancellationToken cancellationToken,
+ out IMethodSymbol methodSymbol,
+ out INamedTypeSymbol typeToGenerateIn)
+ {
+ methodSymbol = null;
+ typeToGenerateIn = document.SemanticModel.GetTypeInfo(expression, cancellationToken).ConvertedType as INamedTypeSymbol;
+ var parameterSymbol = document.SemanticModel.GetTypeInfo(expression, cancellationToken).Type as INamedTypeSymbol;
+ if (typeToGenerateIn == null || parameterSymbol == null || typeToGenerateIn.IsErrorType() || parameterSymbol.IsErrorType())
+ {
+ return false;
+ }
+
+ methodSymbol = GenerateMethodSymbol(typeToGenerateIn, parameterSymbol);
+
+ if (!ValidateTypeToGenerateIn(
+ document.Project.Solution,
+ typeToGenerateIn,
+ true,
+ classInterfaceModuleStructTypes,
+ cancellationToken))
+ {
+ typeToGenerateIn = parameterSymbol;
+ }
+
+ return true;
+ }
+
+ private static IMethodSymbol GenerateMethodSymbol(INamedTypeSymbol typeToGenerateIn, INamedTypeSymbol parameterSymbol)
+ {
+ // Remove any generic parameters
+ if (typeToGenerateIn.IsGenericType)
+ {
+ typeToGenerateIn = typeToGenerateIn.ConstructUnboundGenericType().ConstructedFrom;
+ }
+
+ return CodeGenerationSymbolFactory.CreateMethodSymbol(
+ attributes: SpecializedCollections.EmptyList<AttributeData>(),
+ accessibility: default(Accessibility),
+ modifiers: default(DeclarationModifiers),
+ returnType: typeToGenerateIn,
+ explicitInterfaceSymbol: null,
+ name: null,
+ typeParameters: SpecializedCollections.EmptyList<ITypeParameterSymbol>(),
+ parameters: new[] { CodeGenerationSymbolFactory.CreateParameterSymbol(parameterSymbol, "v") },
+ methodKind: MethodKind.Conversion);
+ }
+
+ protected override string GetImplicitConversionDisplayText(AbstractGenerateParameterizedMemberService<CSharpGenerateConversionService, SimpleNameSyntax, ExpressionSyntax, InvocationExpressionSyntax>.State state)
+ {
+ return string.Format(Resources.ImplicitConversionDisplayText, state.TypeToGenerateIn.Name);
+ }
+
+ protected override string GetExplicitConversionDisplayText(AbstractGenerateParameterizedMemberService<CSharpGenerateConversionService, SimpleNameSyntax, ExpressionSyntax, InvocationExpressionSyntax>.State state)
+ {
+ return string.Format(Resources.ExplicitConversionDisplayText, state.TypeToGenerateIn.Name);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/CSharpGenerateMethodService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/CSharpGenerateMethodService.cs
new file mode 100644
index 0000000000..41155db16b
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/CSharpGenerateMethodService.cs
@@ -0,0 +1,173 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Composition;
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeGeneration;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Symbols;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.FindSymbols;
+using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateMember.GenerateParameterizedMember
+{
+ public partial class CSharpGenerateMethodService :
+ AbstractGenerateMethodService<CSharpGenerateMethodService, SimpleNameSyntax, ExpressionSyntax, InvocationExpressionSyntax>
+ {
+ protected override bool IsExplicitInterfaceGeneration(SyntaxNode node)
+ {
+ return node is MethodDeclarationSyntax;
+ }
+
+ protected override bool IsSimpleNameGeneration(SyntaxNode node)
+ {
+ return node is SimpleNameSyntax;
+ }
+
+ protected override bool ContainingTypesOrSelfHasUnsafeKeyword(INamedTypeSymbol containingType)
+ {
+ return containingType.ContainingTypesOrSelfHasUnsafeKeyword();
+ }
+
+ protected override AbstractInvocationInfo CreateInvocationMethodInfo(SemanticDocument document, AbstractGenerateParameterizedMemberService<CSharpGenerateMethodService, SimpleNameSyntax, ExpressionSyntax, InvocationExpressionSyntax>.State state)
+ {
+ return new CSharpGenerateParameterizedMemberService<CSharpGenerateMethodService>.InvocationExpressionInfo(document, state);
+ }
+
+ protected override bool AreSpecialOptionsActive(SemanticModel semanticModel)
+ {
+ return CSharpCommonGenerationServiceMethods.AreSpecialOptionsActive(semanticModel);
+ }
+
+ protected override bool IsValidSymbol(ISymbol symbol, SemanticModel semanticModel)
+ {
+ return CSharpCommonGenerationServiceMethods.IsValidSymbol(symbol, semanticModel);
+ }
+
+ protected override bool TryInitializeExplicitInterfaceState(
+ SemanticDocument document,
+ SyntaxNode node,
+ CancellationToken cancellationToken,
+ out SyntaxToken identifierToken,
+ out IMethodSymbol methodSymbol,
+ out INamedTypeSymbol typeToGenerateIn)
+ {
+ var methodDeclaration = (MethodDeclarationSyntax)node;
+ identifierToken = methodDeclaration.Identifier;
+
+ if (methodDeclaration.ExplicitInterfaceSpecifier != null &&
+ !methodDeclaration.ParameterList.OpenParenToken.IsMissing &&
+ !methodDeclaration.ParameterList.CloseParenToken.IsMissing)
+ {
+ var semanticModel = document.SemanticModel;
+ methodSymbol = semanticModel.GetDeclaredSymbol(methodDeclaration, cancellationToken) as IMethodSymbol;
+ if (methodSymbol != null && !methodSymbol.ExplicitInterfaceImplementations.Any())
+ {
+ var semanticInfo = semanticModel.GetTypeInfo(methodDeclaration.ExplicitInterfaceSpecifier.Name, cancellationToken);
+ typeToGenerateIn = semanticInfo.Type as INamedTypeSymbol;
+ return typeToGenerateIn != null;
+ }
+ }
+
+ identifierToken = default(SyntaxToken);
+ methodSymbol = null;
+ typeToGenerateIn = null;
+ return false;
+ }
+
+ protected override bool TryInitializeSimpleNameState(
+ SemanticDocument document,
+ SimpleNameSyntax simpleName,
+ CancellationToken cancellationToken,
+ out SyntaxToken identifierToken,
+ out ExpressionSyntax simpleNameOrMemberAccessExpression,
+ out InvocationExpressionSyntax invocationExpressionOpt,
+ out bool isInConditionalAccessExpression)
+ {
+ identifierToken = simpleName.Identifier;
+
+ var memberAccess = simpleName?.Parent as MemberAccessExpressionSyntax;
+ var conditionalMemberAccess = simpleName?.Parent?.Parent?.Parent as ConditionalAccessExpressionSyntax;
+ var inConditionalMemberAccess = conditionalMemberAccess != null;
+ if (memberAccess != null)
+ {
+ simpleNameOrMemberAccessExpression = (ExpressionSyntax)memberAccess;
+ }
+ else if (inConditionalMemberAccess)
+ {
+ simpleNameOrMemberAccessExpression = (ExpressionSyntax)conditionalMemberAccess;
+ }
+ else
+ {
+ simpleNameOrMemberAccessExpression = simpleName;
+ }
+
+ if (memberAccess == null || memberAccess.Name == simpleName)
+ {
+ if (simpleNameOrMemberAccessExpression.IsParentKind(SyntaxKind.InvocationExpression))
+ {
+ invocationExpressionOpt = (InvocationExpressionSyntax)simpleNameOrMemberAccessExpression.Parent;
+ isInConditionalAccessExpression = inConditionalMemberAccess;
+ return !invocationExpressionOpt.ArgumentList.CloseParenToken.IsMissing;
+ }
+ // We need to check that the tree is structured like so:
+ // ConditionalAccessExpressionSyntax
+ // -> InvocationExpressionSyntax
+ // -> MemberBindingExpressionSyntax
+ // and that the name at the end of this expression matches the simple name we were given
+ else if ((((simpleNameOrMemberAccessExpression as ConditionalAccessExpressionSyntax)
+ ?.WhenNotNull as InvocationExpressionSyntax)
+ ?.Expression as MemberBindingExpressionSyntax)
+ ?.Name == simpleName)
+ {
+ invocationExpressionOpt = (InvocationExpressionSyntax)((ConditionalAccessExpressionSyntax)simpleNameOrMemberAccessExpression).WhenNotNull;
+ isInConditionalAccessExpression = inConditionalMemberAccess;
+ return !invocationExpressionOpt.ArgumentList.CloseParenToken.IsMissing;
+ }
+ else if (simpleName.IsKind(SyntaxKind.IdentifierName))
+ {
+ // If we don't have an invocation node, then see if we can infer a delegate in
+ // this location. Check if this is a place where a delegate can go. Only do this
+ // for identifier names. for now. It gets really funky if you have to deal with
+ // a generic name here.
+
+ // Can't assign into a method.
+ if (!simpleNameOrMemberAccessExpression.IsLeftSideOfAnyAssignExpression())
+ {
+ invocationExpressionOpt = null;
+ isInConditionalAccessExpression = inConditionalMemberAccess;
+ return true;
+ }
+ }
+ }
+
+ identifierToken = default(SyntaxToken);
+ simpleNameOrMemberAccessExpression = null;
+ invocationExpressionOpt = null;
+ isInConditionalAccessExpression = false;
+ return false;
+ }
+
+ protected override ITypeSymbol CanGenerateMethodForSimpleNameOrMemberAccessExpression(
+ SemanticModel semanticModel,
+ ExpressionSyntax expresion,
+ CancellationToken cancellationToken)
+ {
+ if (semanticModel.SyntaxTree.IsNameOfContext(expresion.SpanStart, semanticModel, cancellationToken))
+ {
+ return TypeGuessing.typeInferenceService.InferType(semanticModel, expresion, true, cancellationToken);
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/CSharpGenerateParameterizedMemberService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/CSharpGenerateParameterizedMemberService.cs
new file mode 100644
index 0000000000..f6784ece1a
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/CSharpGenerateParameterizedMemberService.cs
@@ -0,0 +1,172 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeGeneration;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Symbols;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Shared.Utilities;
+using Roslyn.Utilities;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateMember.GenerateParameterizedMember
+{
+ internal abstract class CSharpGenerateParameterizedMemberService<TService> : AbstractGenerateParameterizedMemberService<TService, SimpleNameSyntax, ExpressionSyntax, InvocationExpressionSyntax>
+ where TService : AbstractGenerateParameterizedMemberService<TService, SimpleNameSyntax, ExpressionSyntax, InvocationExpressionSyntax>
+ {
+ internal protected partial class InvocationExpressionInfo : AbstractInvocationInfo
+ {
+ private readonly InvocationExpressionSyntax _invocationExpression;
+
+ public InvocationExpressionInfo(
+ SemanticDocument document,
+ AbstractGenerateParameterizedMemberService<TService, SimpleNameSyntax, ExpressionSyntax, InvocationExpressionSyntax>.State state)
+ : base(document, state)
+ {
+ _invocationExpression = state.InvocationExpressionOpt;
+ }
+
+ protected override IList<string> DetermineParameterNames(CancellationToken cancellationToken)
+ {
+ return this.Document.SemanticModel.GenerateParameterNames(
+ _invocationExpression.ArgumentList);
+ }
+
+ protected override ITypeSymbol DetermineReturnTypeWorker(CancellationToken cancellationToken)
+ {
+ // Defer to the type inferrer to figure out what the return type of this new method
+ // should be.
+ var inferredType = TypeGuessing.typeInferenceService.InferType(this.Document.SemanticModel,
+ _invocationExpression, objectAsDefault: true, cancellationToken: cancellationToken);
+ if (State.IsInConditionalAccessExpression)
+ {
+ return inferredType.RemoveNullableIfPresent();
+ }
+
+ return inferredType;
+ }
+
+ protected override IList<ITypeParameterSymbol> GetCapturedTypeParameters(CancellationToken cancellationToken)
+ {
+ var result = new List<ITypeParameterSymbol>();
+ var semanticModel = this.Document.SemanticModel;
+ foreach (var argument in _invocationExpression.ArgumentList.Arguments)
+ {
+ var type = semanticModel.GetType(argument.Expression, cancellationToken);
+ type.GetReferencedTypeParameters(result);
+ }
+
+ return result;
+ }
+
+ protected override IList<ITypeParameterSymbol> GenerateTypeParameters(CancellationToken cancellationToken)
+ {
+ // Generate dummy type parameter names for a generic method. If the user is inside a
+ // generic method, and calls a generic method with type arguments from the outer
+ // method, then use those same names for the generated type parameters.
+ //
+ // TODO(cyrusn): If we do capture method type variables, then we should probably
+ // capture their constraints as well.
+ var genericName = (GenericNameSyntax)this.State.SimpleNameOpt;
+ var semanticModel = this.Document.SemanticModel;
+
+ if (genericName.TypeArgumentList.Arguments.Count == 1)
+ {
+ var typeParameter = GetUniqueTypeParameter(
+ genericName.TypeArgumentList.Arguments.First(),
+ s => !State.TypeToGenerateIn.GetAllTypeParameters().Any(t => t.Name == s),
+ cancellationToken);
+
+ return new List<ITypeParameterSymbol> { typeParameter };
+ }
+ else
+ {
+ var list = new List<ITypeParameterSymbol>();
+
+ var usedIdentifiers = new HashSet<string> { "T" };
+ foreach (var type in genericName.TypeArgumentList.Arguments)
+ {
+ var typeParameter = GetUniqueTypeParameter(
+ type,
+ s => !usedIdentifiers.Contains(s) && !State.TypeToGenerateIn.GetAllTypeParameters().Any(t => t.Name == s),
+ cancellationToken);
+
+ usedIdentifiers.Add(typeParameter.Name);
+
+ list.Add(typeParameter);
+ }
+
+ return list;
+ }
+ }
+
+ private ITypeParameterSymbol GetUniqueTypeParameter(
+ TypeSyntax type,
+ Func<string, bool> isUnique,
+ CancellationToken cancellationToken)
+ {
+ var methodTypeParameter = GetMethodTypeParameter(type, cancellationToken);
+ return methodTypeParameter != null
+ ? methodTypeParameter
+ : CodeGenerationSymbolFactory.CreateTypeParameterSymbol(NameGenerator.GenerateUniqueName("T", isUnique));
+ }
+
+ private ITypeParameterSymbol GetMethodTypeParameter(TypeSyntax type, CancellationToken cancellationToken)
+ {
+ if (type is IdentifierNameSyntax)
+ {
+ var info = this.Document.SemanticModel.GetTypeInfo(type, cancellationToken);
+ if (info.Type is ITypeParameterSymbol &&
+ ((ITypeParameterSymbol)info.Type).TypeParameterKind == TypeParameterKind.Method)
+ {
+ return (ITypeParameterSymbol)info.Type;
+ }
+ }
+
+ return null;
+ }
+
+ protected override IList<RefKind> DetermineParameterModifiers(CancellationToken cancellationToken)
+ {
+ return
+ _invocationExpression.ArgumentList.Arguments.Select(
+ a => a.RefOrOutKeyword.Kind() == SyntaxKind.RefKeyword ? RefKind.Ref :
+ a.RefOrOutKeyword.Kind() == SyntaxKind.OutKeyword ? RefKind.Out : RefKind.None).ToList();
+ }
+
+ protected override IList<ITypeSymbol> DetermineParameterTypes(CancellationToken cancellationToken)
+ {
+ return _invocationExpression.ArgumentList.Arguments.Select(a => DetermineParameterType(a, cancellationToken)).ToList();
+ }
+
+ private ITypeSymbol DetermineParameterType(
+ ArgumentSyntax argument,
+ CancellationToken cancellationToken)
+ {
+ return argument.DetermineParameterType(this.Document.SemanticModel, cancellationToken);
+ }
+
+ protected override IList<bool> DetermineParameterOptionality(CancellationToken cancellationToken)
+ {
+ return _invocationExpression.ArgumentList.Arguments.Select(a => false).ToList();
+ }
+
+ protected override bool IsIdentifierName()
+ {
+ return this.State.SimpleNameOpt.Kind() == SyntaxKind.IdentifierName;
+ }
+
+ protected override bool IsImplicitReferenceConversion(Compilation compilation, ITypeSymbol sourceType, ITypeSymbol targetType)
+ {
+ var conversion = compilation.ClassifyConversion(sourceType, targetType);
+ return conversion.IsImplicit && conversion.IsReference;
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/MethodGenerationKind.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/MethodGenerationKind.cs
new file mode 100644
index 0000000000..cbafecdebb
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateParameterizedMember/MethodGenerationKind.cs
@@ -0,0 +1,11 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateMember.GenerateParameterizedMember
+{
+ public enum MethodGenerationKind
+ {
+ Member,
+ ImplicitConversion,
+ ExplicitConversion
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateVariable/AbstractGenerateVariableService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateVariable/AbstractGenerateVariableService.cs
new file mode 100644
index 0000000000..e4bc88e3cc
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateVariable/AbstractGenerateVariableService.cs
@@ -0,0 +1,696 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.CodeGeneration;
+using Microsoft.CodeAnalysis.Internal.Log;
+using Microsoft.CodeAnalysis.Options;
+using Roslyn.Utilities;
+using ICSharpCode.NRefactory6.CSharp.GenerateMember;
+using ICSharpCode.NRefactory6.CSharp;
+using Microsoft.CodeAnalysis.Editing;
+using ICSharpCode.NRefactory6.CSharp.CodeGeneration;
+using Microsoft.CodeAnalysis.FindSymbols;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace Microsoft.CodeAnalysis.GenerateMember.GenerateVariable
+{
+ public abstract partial class AbstractGenerateVariableService<TService, TSimpleNameSyntax, TExpressionSyntax> :
+ AbstractGenerateMemberService<TSimpleNameSyntax, TExpressionSyntax>
+ where TService : AbstractGenerateVariableService<TService, TSimpleNameSyntax, TExpressionSyntax>
+ where TSimpleNameSyntax : TExpressionSyntax
+ where TExpressionSyntax : SyntaxNode
+ {
+ protected AbstractGenerateVariableService()
+ {
+ }
+
+ protected abstract bool IsExplicitInterfaceGeneration(SyntaxNode node);
+ protected abstract bool IsIdentifierNameGeneration(SyntaxNode node);
+
+ protected abstract bool TryInitializeExplicitInterfaceState(SemanticDocument document, SyntaxNode node, CancellationToken cancellationToken, out SyntaxToken identifierToken, out IPropertySymbol propertySymbol, out INamedTypeSymbol typeToGenerateIn);
+ protected abstract bool TryInitializeIdentifierNameState(SemanticDocument document, TSimpleNameSyntax identifierName, CancellationToken cancellationToken, out SyntaxToken identifierToken, out TExpressionSyntax simpleNameOrMemberAccessExpression, out bool isInExecutableBlock, out bool isinConditionalAccessExpression);
+
+ protected abstract bool TryConvertToLocalDeclaration(ITypeSymbol type, SyntaxToken identifierToken, OptionSet options, out SyntaxNode newRoot);
+
+ public async Task<IEnumerable<CodeAction>> GenerateVariableAsync(
+ Document document,
+ SyntaxNode node,
+ CancellationToken cancellationToken)
+ {
+ var semanticDocument = await SemanticDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false);
+
+ var state = await State.GenerateAsync((TService)this, semanticDocument, node, cancellationToken).ConfigureAwait(false);
+ if (state == null)
+ {
+ return SpecializedCollections.EmptyEnumerable<CodeAction>();
+ }
+
+ var result = new List<CodeAction>();
+
+ var canGenerateMember = CodeGenerator.CanAdd(document.Project.Solution, state.TypeToGenerateIn, cancellationToken);
+
+ // prefer fields over properties (and vice versa) depending on the casing of the member.
+ // lowercase -> fields. title case -> properties.
+ var name = state.IdentifierToken.ValueText;
+ if (char.IsUpper(name.FirstOrDefault()))
+ {
+ if (canGenerateMember)
+ {
+ AddPropertyCodeActions(result, document, state);
+ AddFieldCodeActions(result, document, state);
+ }
+
+ AddLocalCodeActions(result, document, state);
+ }
+ else
+ {
+ if (canGenerateMember)
+ {
+ AddFieldCodeActions(result, document, state);
+ AddPropertyCodeActions(result, document, state);
+ }
+
+ AddLocalCodeActions(result, document, state);
+ }
+
+ return result;
+ }
+
+ protected virtual bool ContainingTypesOrSelfHasUnsafeKeyword(INamedTypeSymbol containingType)
+ {
+ return false;
+ }
+
+ private void AddPropertyCodeActions(List<CodeAction> result, Document document, State state)
+ {
+ if (state.IsInRefContext || state.IsInOutContext)
+ {
+ return;
+ }
+
+ if (state.IsConstant)
+ {
+ return;
+ }
+
+ if (state.TypeToGenerateIn.TypeKind == TypeKind.Interface && state.IsStatic)
+ {
+ return;
+ }
+
+ result.Add(new GenerateVariableCodeAction((TService)this, document, state, generateProperty: true, isReadonly: false, isConstant: false));
+
+ if (state.TypeToGenerateIn.TypeKind == TypeKind.Interface && !state.IsWrittenTo)
+ {
+ result.Add(new GenerateVariableCodeAction((TService)this, document, state, generateProperty: true, isReadonly: true, isConstant: false));
+ }
+ }
+
+ private void AddFieldCodeActions(List<CodeAction> result, Document document, State state)
+ {
+ if (state.TypeToGenerateIn.TypeKind != TypeKind.Interface)
+ {
+ if (state.IsConstant)
+ {
+ result.Add(new GenerateVariableCodeAction((TService)this, document, state, generateProperty: false, isReadonly: false, isConstant: true));
+ }
+ else
+ {
+ result.Add(new GenerateVariableCodeAction((TService)this, document, state, generateProperty: false, isReadonly: false, isConstant: false));
+
+ // If we haven't written to the field, or we're in the constructor for the type
+ // we're writing into, then we can generate this field read-only.
+ if (!state.IsWrittenTo || state.IsInConstructor)
+ {
+ result.Add(new GenerateVariableCodeAction((TService)this, document, state, generateProperty: false, isReadonly: true, isConstant: false));
+ }
+ }
+ }
+ }
+
+ private void AddLocalCodeActions(List<CodeAction> result, Document document, State state)
+ {
+ if (state.CanGenerateLocal())
+ {
+ result.Add(new GenerateLocalCodeAction((TService)this, document, state));
+ }
+ }
+
+ private partial class GenerateVariableCodeAction : CodeAction
+ {
+ private readonly TService _service;
+ private readonly State _state;
+ private readonly bool _generateProperty;
+ private readonly bool _isReadonly;
+ private readonly bool _isConstant;
+ private readonly Document _document;
+ private readonly string _equivalenceKey;
+
+ public GenerateVariableCodeAction(
+ TService service,
+ Document document,
+ State state,
+ bool generateProperty,
+ bool isReadonly,
+ bool isConstant)
+ {
+ _service = service;
+ _document = document;
+ _state = state;
+ _generateProperty = generateProperty;
+ _isReadonly = isReadonly;
+ _isConstant = isConstant;
+ _equivalenceKey = Title;
+ }
+
+ protected override async Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken)
+ {
+ var syntaxTree = await _document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
+ var generateUnsafe = _state.TypeMemberType.IsUnsafe() &&
+ !_state.IsContainedInUnsafeType;
+
+ if (_generateProperty)
+ {
+ var getAccessor = CodeGenerationSymbolFactory.CreateAccessorSymbol(
+ attributes: null,
+ accessibility: DetermineMaximalAccessibility(_state),
+ statements: null);
+ var setAccessor = _isReadonly ? null : CodeGenerationSymbolFactory.CreateAccessorSymbol(
+ attributes: null,
+ accessibility: DetermineMinimalAccessibility(_state),
+ statements: null);
+
+ var result = await CodeGenerator.AddPropertyDeclarationAsync(
+ _document.Project.Solution,
+ _state.TypeToGenerateIn,
+ CodeGenerationSymbolFactory.CreatePropertySymbol(
+ attributes: null,
+ accessibility: DetermineMaximalAccessibility(_state),
+ modifiers: DeclarationModifiers.None.WithIsStatic(_state.IsStatic).WithIsUnsafe (generateUnsafe),
+ type: _state.TypeMemberType,
+ explicitInterfaceSymbol: null,
+ name: _state.IdentifierToken.ValueText,
+ isIndexer: _state.IsIndexer,
+ parameters: _state.Parameters,
+ getMethod: getAccessor,
+ setMethod: setAccessor),
+ new CodeGenerationOptions(contextLocation: _state.IdentifierToken.GetLocation(), generateDefaultAccessibility: false),
+ cancellationToken: cancellationToken)
+ .ConfigureAwait(false);
+
+ return result;
+ }
+ else
+ {
+ var result = await CodeGenerator.AddFieldDeclarationAsync(
+ _document.Project.Solution,
+ _state.TypeToGenerateIn,
+ CodeGenerationSymbolFactory.CreateFieldSymbol(
+ attributes: null,
+ accessibility: DetermineMinimalAccessibility(_state),
+ modifiers: _isConstant ?
+ DeclarationModifiers.None.WithIsConst(true).WithIsUnsafe(generateUnsafe) :
+ DeclarationModifiers.None.WithIsStatic(_state.IsStatic).WithIsReadOnly (_isReadonly).WithIsUnsafe(generateUnsafe),
+ type: _state.TypeMemberType,
+ name: _state.IdentifierToken.ValueText),
+ new CodeGenerationOptions(contextLocation: _state.IdentifierToken.GetLocation(), generateDefaultAccessibility: false),
+ cancellationToken: cancellationToken)
+ .ConfigureAwait(false);
+
+ return result;
+ }
+ }
+
+ private Accessibility DetermineMaximalAccessibility(State state)
+ {
+ if (state.TypeToGenerateIn.TypeKind == TypeKind.Interface)
+ {
+ return Accessibility.NotApplicable;
+ }
+
+ var accessibility = Accessibility.Public;
+
+ // Ensure that we're not overly exposing a type.
+ var containingTypeAccessibility = state.TypeToGenerateIn.DetermineMinimalAccessibility();
+ var effectiveAccessibility = CommonAccessibilityUtilities.Minimum(
+ containingTypeAccessibility, accessibility);
+
+ var returnTypeAccessibility = state.TypeMemberType.DetermineMinimalAccessibility();
+
+ if (CommonAccessibilityUtilities.Minimum(effectiveAccessibility, returnTypeAccessibility) !=
+ effectiveAccessibility)
+ {
+ return returnTypeAccessibility;
+ }
+
+ return accessibility;
+ }
+
+ private Accessibility DetermineMinimalAccessibility(State state)
+ {
+ if (state.TypeToGenerateIn.TypeKind == TypeKind.Interface)
+ {
+ return Accessibility.NotApplicable;
+ }
+
+ // Otherwise, figure out what accessibility modifier to use and optionally mark
+ // it as static.
+ if (state.SimpleNameOrMemberAccessExpressionOpt.IsAttributeNamedArgumentIdentifier())
+ {
+ return Accessibility.Public;
+ }
+ else if (state.ContainingType.IsContainedWithin(state.TypeToGenerateIn))
+ {
+ return Accessibility.Private;
+ }
+ else if (DerivesFrom(state, state.ContainingType) && state.IsStatic)
+ {
+ // NOTE(cyrusn): We only generate protected in the case of statics. Consider
+ // the case where we're generating into one of our base types. i.e.:
+ //
+ // class B : A { void Foo() { A a; a.Foo(); }
+ //
+ // In this case we can *not* mark the method as protected. 'B' can only
+ // access protected members of 'A' through an instance of 'B' (or a subclass
+ // of B). It can not access protected members through an instance of the
+ // superclass. In this case we need to make the method public or internal.
+ //
+ // However, this does not apply if the method will be static. i.e.
+ //
+ // class B : A { void Foo() { A.Foo(); }
+ //
+ // B can access the protected statics of A, and so we generate 'Foo' as
+ // protected.
+ return Accessibility.Protected;
+ }
+ else if (state.ContainingType.ContainingAssembly.IsSameAssemblyOrHasFriendAccessTo(state.TypeToGenerateIn.ContainingAssembly))
+ {
+ return Accessibility.Internal;
+ }
+ else
+ {
+ // TODO: Code coverage - we need a unit-test that generates across projects
+ return Accessibility.Public;
+ }
+ }
+
+ private bool DerivesFrom(State state, INamedTypeSymbol containingType)
+ {
+ return containingType.GetBaseTypes().Select(t => t.OriginalDefinition)
+ .Contains(state.TypeToGenerateIn);
+ }
+
+ public override string Title
+ {
+ get
+ {
+ var text = _isConstant
+ ? Resources.GenerateConstantIn
+ : _generateProperty
+ ? _isReadonly ? Resources.GenerateReadonlyProperty : Resources.GeneratePropertyIn
+ : _isReadonly ? Resources.GenerateReadonlyField : Resources.GenerateFieldIn;
+
+ return string.Format(
+ text,
+ _state.IdentifierToken.ValueText,
+ _state.TypeToGenerateIn.Name);
+ }
+ }
+
+ public override string EquivalenceKey
+ {
+ get
+ {
+ return _equivalenceKey;
+ }
+ }
+ }
+
+ private class GenerateLocalCodeAction : CodeAction
+ {
+ private readonly TService _service;
+ private readonly Document _document;
+ private readonly State _state;
+
+ public GenerateLocalCodeAction(TService service, Document document, State state)
+ {
+ _service = service;
+ _document = document;
+ _state = state;
+ }
+
+ public override string Title
+ {
+ get
+ {
+ var text = Resources.GenerateLocal;
+
+ return string.Format(
+ text,
+ _state.IdentifierToken.ValueText);
+ }
+ }
+
+ protected override Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken)
+ {
+ var newRoot = GetNewRoot(cancellationToken);
+ var newDocument = _document.WithSyntaxRoot(newRoot);
+
+ return Task.FromResult(newDocument);
+ }
+
+ private SyntaxNode GetNewRoot(CancellationToken cancellationToken)
+ {
+ SyntaxNode newRoot;
+ if (_service.TryConvertToLocalDeclaration(_state.LocalType, _state.IdentifierToken, _document.Project.Solution.Workspace.Options, out newRoot))
+ {
+ return newRoot;
+ }
+
+ var syntaxFactory = _document.GetLanguageService<SyntaxGenerator>();
+ var initializer = _state.IsOnlyWrittenTo
+ ? null
+ : syntaxFactory.DefaultExpression(_state.LocalType);
+
+ var type = _state.LocalType;
+ var localStatement = syntaxFactory.LocalDeclarationStatement(type, _state.IdentifierToken.ValueText, initializer);
+ localStatement = localStatement.WithAdditionalAnnotations(Microsoft.CodeAnalysis.Formatting.Formatter.Annotation);
+
+ var codeGenService = new CSharpCodeGenerationService (_document.Project.Solution.Workspace);
+ var root = _state.IdentifierToken.GetAncestors<SyntaxNode>().Last();
+
+ return codeGenService.AddStatements(
+ root,
+ SpecializedCollections.SingletonEnumerable(localStatement),
+ options: new CodeGenerationOptions(beforeThisLocation: _state.IdentifierToken.GetLocation()),
+ cancellationToken: cancellationToken);
+ }
+ }
+
+ private partial class State
+ {
+ public INamedTypeSymbol ContainingType { get; private set; }
+ public INamedTypeSymbol TypeToGenerateIn { get; private set; }
+ public bool IsStatic { get; private set; }
+ public bool IsConstant { get; private set; }
+ public bool IsIndexer { get; private set; }
+ public bool IsContainedInUnsafeType { get; private set; }
+ public IList<IParameterSymbol> Parameters { get; private set; }
+
+ // Just the name of the method. i.e. "Foo" in "Foo" or "X.Foo"
+ public SyntaxToken IdentifierToken { get; private set; }
+ public TSimpleNameSyntax SimpleNameOpt { get; private set; }
+
+ // The entire expression containing the name. i.e. "X.Foo"
+ public TExpressionSyntax SimpleNameOrMemberAccessExpressionOpt { get; private set; }
+
+ public ITypeSymbol TypeMemberType { get; private set; }
+ public ITypeSymbol LocalType { get; private set; }
+
+ public bool IsWrittenTo { get; private set; }
+ public bool IsOnlyWrittenTo { get; private set; }
+
+ public bool IsInConstructor { get; private set; }
+ public bool IsInRefContext { get; private set; }
+ public bool IsInOutContext { get; private set; }
+ public bool IsInMemberContext { get; private set; }
+
+ public bool IsInExecutableBlock { get; private set; }
+ public bool IsInConditionalAccessExpression { get; private set; }
+
+ public static async Task<State> GenerateAsync(
+ TService service,
+ SemanticDocument document,
+ SyntaxNode interfaceNode,
+ CancellationToken cancellationToken)
+ {
+ var state = new State();
+ if (!await state.TryInitializeAsync(service, document, interfaceNode, cancellationToken).ConfigureAwait(false))
+ {
+ return null;
+ }
+
+ return state;
+ }
+
+ private async Task<bool> TryInitializeAsync(
+ TService service,
+ SemanticDocument document,
+ SyntaxNode node,
+ CancellationToken cancellationToken)
+ {
+ if (service.IsIdentifierNameGeneration(node))
+ {
+ // Cases that we deal with currently:
+ //
+ // 1) expr.Foo
+ // 2) expr->Foo
+ // 3) Foo
+ if (!TryInitializeSimpleName(service, document, (TSimpleNameSyntax)node, cancellationToken))
+ {
+ return false;
+ }
+ }
+ else if (service.IsExplicitInterfaceGeneration(node))
+ {
+ // 4) bool IFoo.NewProp
+ if (!TryInitializeExplicitInterface(service, document, node, cancellationToken))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ // Ok. It either didn't bind to any symbols, or it bound to a symbol but with
+ // errors. In the former case we definitely want to offer to generate a field. In
+ // the latter case, we want to generate a field *unless* there's an existing member
+ // with the same name. Note: it's ok if there's a method with the same name.
+ var existingMembers = this.TypeToGenerateIn.GetMembers(this.IdentifierToken.ValueText)
+ .Where(m => m.Kind != SymbolKind.Method);
+ if (existingMembers.Any())
+ {
+ // TODO: Code coverage
+ // There was an existing method that the new method would clash with.
+ return false;
+ }
+
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return false;
+ }
+
+ this.TypeToGenerateIn = await SymbolFinder.FindSourceDefinitionAsync(this.TypeToGenerateIn, document.Project.Solution, cancellationToken).ConfigureAwait(false) as INamedTypeSymbol;
+
+ if (!service.ValidateTypeToGenerateIn(
+ document.Project.Solution, this.TypeToGenerateIn, this.IsStatic, ClassInterfaceModuleStructTypes, cancellationToken))
+ {
+ return false;
+ }
+
+ this.IsContainedInUnsafeType = service.ContainingTypesOrSelfHasUnsafeKeyword(this.TypeToGenerateIn);
+
+ return CanGenerateLocal() || CodeGenerator.CanAdd(document.Project.Solution, this.TypeToGenerateIn, cancellationToken);
+ }
+
+ internal bool CanGenerateLocal()
+ {
+ return !this.IsInMemberContext && this.IsInExecutableBlock;
+ }
+
+ private bool TryInitializeExplicitInterface(
+ TService service,
+ SemanticDocument document,
+ SyntaxNode propertyDeclaration,
+ CancellationToken cancellationToken)
+ {
+ SyntaxToken identifierToken;
+ IPropertySymbol propertySymbol;
+ INamedTypeSymbol typeToGenerateIn;
+ if (!service.TryInitializeExplicitInterfaceState(
+ document, propertyDeclaration, cancellationToken,
+ out identifierToken, out propertySymbol, out typeToGenerateIn))
+ {
+ return false;
+ }
+
+ this.IdentifierToken = identifierToken;
+ this.TypeToGenerateIn = typeToGenerateIn;
+
+ if (propertySymbol.ExplicitInterfaceImplementations.Any())
+ {
+ return false;
+ }
+
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var semanticModel = document.SemanticModel;
+ this.ContainingType = semanticModel.GetEnclosingNamedType(this.IdentifierToken.SpanStart, cancellationToken);
+ if (this.ContainingType == null)
+ {
+ return false;
+ }
+
+ if (!this.ContainingType.Interfaces.OfType<INamedTypeSymbol>().Contains(this.TypeToGenerateIn))
+ {
+ return false;
+ }
+
+ this.IsIndexer = propertySymbol.IsIndexer;
+ this.Parameters = propertySymbol.Parameters;
+ this.TypeMemberType = propertySymbol.Type;
+
+ // By default, make it readonly, unless there's already an setter defined.
+ this.IsWrittenTo = propertySymbol.SetMethod != null;
+
+ return true;
+ }
+
+ private bool TryInitializeSimpleName(
+ TService service,
+ SemanticDocument document,
+ TSimpleNameSyntax simpleName,
+ CancellationToken cancellationToken)
+ {
+ SyntaxToken identifierToken;
+ TExpressionSyntax simpleNameOrMemberAccessExpression;
+ bool isInExecutableBlock;
+ bool isInConditionalAccessExpression;
+ if (!service.TryInitializeIdentifierNameState(
+ document, simpleName, cancellationToken,
+ out identifierToken, out simpleNameOrMemberAccessExpression, out isInExecutableBlock, out isInConditionalAccessExpression))
+ {
+ return false;
+ }
+
+ if (string.IsNullOrWhiteSpace(identifierToken.ValueText))
+ {
+ return false;
+ }
+
+ this.SimpleNameOpt = simpleName;
+ this.IdentifierToken = identifierToken;
+ this.SimpleNameOrMemberAccessExpressionOpt = simpleNameOrMemberAccessExpression;
+ this.IsInExecutableBlock = isInExecutableBlock;
+ this.IsInConditionalAccessExpression = isInConditionalAccessExpression;
+
+ // If we're in a type context then we shouldn't offer to generate a field or
+ // property.
+ if (SimpleNameOrMemberAccessExpressionOpt.IsInNamespaceOrTypeContext())
+ {
+ return false;
+ }
+
+ var expr = SimpleNameOrMemberAccessExpressionOpt as ExpressionSyntax;
+ this.IsConstant = expr.IsInConstantContext();
+
+ // If we're not in a type, don't even bother. NOTE(cyrusn): We'll have to rethink this
+ // for C# Script.
+ cancellationToken.ThrowIfCancellationRequested();
+ var semanticModel = document.SemanticModel;
+ this.ContainingType = semanticModel.GetEnclosingNamedType(this.IdentifierToken.SpanStart, cancellationToken);
+ if (this.ContainingType == null)
+ {
+ return false;
+ }
+
+ // Now, try to bind the invocation and see if it succeeds or not. if it succeeds and
+ // binds uniquely, then we don't need to offer this quick fix.
+ cancellationToken.ThrowIfCancellationRequested();
+ var semanticInfo = semanticModel.GetSymbolInfo(this.SimpleNameOrMemberAccessExpressionOpt, cancellationToken);
+
+ cancellationToken.ThrowIfCancellationRequested();
+ if (semanticInfo.Symbol != null)
+ {
+ return false;
+ }
+
+ // Either we found no matches, or this was ambiguous. Either way, we might be able
+ // to generate a method here. Determine where the user wants to generate the method
+ // into, and if it's valid then proceed.
+ cancellationToken.ThrowIfCancellationRequested();
+ INamedTypeSymbol typeToGenerateIn;
+ bool isStatic;
+ if (!service.TryDetermineTypeToGenerateIn(document, this.ContainingType, this.SimpleNameOrMemberAccessExpressionOpt, cancellationToken,
+ out typeToGenerateIn, out isStatic))
+ {
+ return false;
+ }
+
+ this.TypeToGenerateIn = typeToGenerateIn;
+ this.IsStatic = isStatic;
+
+ DetermineFieldType(document, cancellationToken);
+
+ this.IsInRefContext = expr.IsInRefContext();
+ this.IsInOutContext = expr.IsInOutContext();
+ this.IsWrittenTo = expr.IsWrittenTo();
+ this.IsOnlyWrittenTo = expr.IsOnlyWrittenTo();
+ this.IsInConstructor = DetermineIsInConstructor(document);
+ this.IsInMemberContext = this.SimpleNameOpt != this.SimpleNameOrMemberAccessExpressionOpt ||
+ expr.IsObjectInitializerNamedAssignmentIdentifier();
+ return true;
+ }
+
+ private void DetermineFieldType(
+ SemanticDocument document,
+ CancellationToken cancellationToken)
+ {
+ var inferredType = TypeGuessing.typeInferenceService.InferType(
+ document.SemanticModel, this.SimpleNameOrMemberAccessExpressionOpt,
+ objectAsDefault: true,
+ cancellationToken: cancellationToken);
+ inferredType = inferredType.SpecialType == SpecialType.System_Void
+ ? document.SemanticModel.Compilation.ObjectType
+ : inferredType;
+
+ if (this.IsInConditionalAccessExpression)
+ {
+ inferredType = inferredType.RemoveNullableIfPresent();
+ }
+
+ // Substitute 'object' for all captured method type parameters. Note: we may need to
+ // do this for things like anonymous types, as well as captured type parameters that
+ // aren't in scope in the destination type.
+ var capturedMethodTypeParameters = inferredType.GetReferencedMethodTypeParameters();
+ var mapping = capturedMethodTypeParameters.ToDictionary(tp => tp,
+ tp => document.SemanticModel.Compilation.ObjectType);
+
+ this.TypeMemberType = inferredType.SubstituteTypes(mapping, document.SemanticModel.Compilation);
+ var availableTypeParameters = this.TypeToGenerateIn.GetAllTypeParameters();
+ this.TypeMemberType = TypeMemberType.RemoveUnavailableTypeParameters(
+ document.SemanticModel.Compilation, availableTypeParameters);
+
+ var enclosingMethodSymbol = document.SemanticModel.GetEnclosingSymbol<IMethodSymbol>(this.SimpleNameOrMemberAccessExpressionOpt.SpanStart, cancellationToken);
+ if (enclosingMethodSymbol != null && enclosingMethodSymbol.TypeParameters != null && enclosingMethodSymbol.TypeParameters.Count() != 0)
+ {
+ var combinedTypeParameters = new List<ITypeParameterSymbol>();
+ combinedTypeParameters.AddRange(availableTypeParameters);
+ combinedTypeParameters.AddRange(enclosingMethodSymbol.TypeParameters);
+ this.LocalType = inferredType.RemoveUnavailableTypeParameters(
+ document.SemanticModel.Compilation, combinedTypeParameters);
+ }
+ else
+ {
+ this.LocalType = this.TypeMemberType;
+ }
+ }
+
+ private bool DetermineIsInConstructor(SemanticDocument document)
+ {
+ if (!this.ContainingType.OriginalDefinition.Equals(this.TypeToGenerateIn.OriginalDefinition))
+ {
+ return false;
+ }
+
+ return SimpleNameOpt.IsInConstructor();
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateVariable/CSharpGenerateVariableService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateVariable/CSharpGenerateVariableService.cs
new file mode 100644
index 0000000000..e305451626
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateMember/GenerateVariable/CSharpGenerateVariableService.cs
@@ -0,0 +1,167 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Composition;
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Symbols;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Formatting;
+using Microsoft.CodeAnalysis.GenerateMember.GenerateVariable;
+using Microsoft.CodeAnalysis.Host;
+using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.Options;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using ICSharpCode.NRefactory6.CSharp;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace Microsoft.CodeAnalysis.GenerateMember.GenerateVariable
+{
+ public partial class CSharpGenerateVariableService :
+ AbstractGenerateVariableService<CSharpGenerateVariableService, SimpleNameSyntax, ExpressionSyntax>
+ {
+ protected override bool IsExplicitInterfaceGeneration(SyntaxNode node)
+ {
+ return node is PropertyDeclarationSyntax;
+ }
+
+ protected override bool IsIdentifierNameGeneration(SyntaxNode node)
+ {
+ return node is IdentifierNameSyntax;
+ }
+
+ protected override bool ContainingTypesOrSelfHasUnsafeKeyword(INamedTypeSymbol containingType)
+ {
+ return containingType.ContainingTypesOrSelfHasUnsafeKeyword();
+ }
+
+ protected override bool TryInitializeExplicitInterfaceState(
+ SemanticDocument document, SyntaxNode node, CancellationToken cancellationToken,
+ out SyntaxToken identifierToken, out IPropertySymbol propertySymbol, out INamedTypeSymbol typeToGenerateIn)
+ {
+ var propertyDeclaration = (PropertyDeclarationSyntax)node;
+ identifierToken = propertyDeclaration.Identifier;
+
+ if (propertyDeclaration.ExplicitInterfaceSpecifier != null)
+ {
+ var semanticModel = document.SemanticModel;
+ propertySymbol = semanticModel.GetDeclaredSymbol(propertyDeclaration, cancellationToken) as IPropertySymbol;
+ if (propertySymbol != null && !propertySymbol.ExplicitInterfaceImplementations.Any())
+ {
+ var info = semanticModel.GetTypeInfo(propertyDeclaration.ExplicitInterfaceSpecifier.Name, cancellationToken);
+ typeToGenerateIn = info.Type as INamedTypeSymbol;
+ return typeToGenerateIn != null;
+ }
+ }
+
+ identifierToken = default(SyntaxToken);
+ propertySymbol = null;
+ typeToGenerateIn = null;
+ return false;
+ }
+
+ protected override bool TryInitializeIdentifierNameState(
+ SemanticDocument document, SimpleNameSyntax identifierName, CancellationToken cancellationToken,
+ out SyntaxToken identifierToken, out ExpressionSyntax simpleNameOrMemberAccessExpression, out bool isInExecutableBlock, out bool isConditionalAccessExpression)
+ {
+ identifierToken = identifierName.Identifier;
+ if (identifierToken.ValueText != string.Empty &&
+ !identifierName.IsVar)
+ {
+ var memberAccess = identifierName.Parent as MemberAccessExpressionSyntax;
+ var conditionalMemberAccess = identifierName.Parent.Parent as ConditionalAccessExpressionSyntax;
+ if (memberAccess?.Name == identifierName)
+ {
+ simpleNameOrMemberAccessExpression = (ExpressionSyntax)memberAccess;
+ }
+ else if ((conditionalMemberAccess?.WhenNotNull as MemberBindingExpressionSyntax)?.Name == identifierName)
+ {
+ simpleNameOrMemberAccessExpression = conditionalMemberAccess;
+ }
+ else
+ {
+ simpleNameOrMemberAccessExpression = identifierName;
+ }
+
+ // If we're being invoked, then don't offer this, offer generate method instead.
+ // Note: we could offer to generate a field with a delegate type. However, that's
+ // very esoteric and probably not what most users want.
+ if (!IsLegal(document, simpleNameOrMemberAccessExpression, cancellationToken))
+ {
+ isInExecutableBlock = false;
+ isConditionalAccessExpression = false;
+ return false;
+ }
+
+ var block = identifierName.GetAncestor<BlockSyntax>();
+ isInExecutableBlock = block != null && !block.OverlapsHiddenPosition(cancellationToken);
+ isConditionalAccessExpression = conditionalMemberAccess != null;
+ return true;
+ }
+
+ identifierToken = default(SyntaxToken);
+ simpleNameOrMemberAccessExpression = null;
+ isInExecutableBlock = false;
+ isConditionalAccessExpression = false;
+ return false;
+ }
+
+ private bool IsLegal(
+ SemanticDocument document,
+ ExpressionSyntax expression,
+ CancellationToken cancellationToken)
+ {
+ // TODO(cyrusn): Consider supporting this at some point. It is difficult because we'd
+ // need to replace the identifier typed with the fully qualified name of the field we
+ // were generating.
+ if (expression.IsParentKind(SyntaxKind.AttributeArgument))
+ {
+ return false;
+ }
+
+ if (expression.IsParentKind(SyntaxKind.ConditionalAccessExpression))
+ {
+ return true;
+ }
+
+ return expression.CanReplaceWithLValue(document.SemanticModel, cancellationToken);
+ }
+
+ protected override bool TryConvertToLocalDeclaration(ITypeSymbol type, SyntaxToken identifierToken, OptionSet options, out SyntaxNode newRoot)
+ {
+ var token = (SyntaxToken)identifierToken;
+ var node = identifierToken.Parent as IdentifierNameSyntax;
+ if (node.IsLeftSideOfAssignExpression() && node.Parent.IsParentKind(SyntaxKind.ExpressionStatement))
+ {
+ var assignExpression = (AssignmentExpressionSyntax)node.Parent;
+ var expressionStatement = (StatementSyntax)assignExpression.Parent;
+
+ var declarationStatement = SyntaxFactory.LocalDeclarationStatement(
+ SyntaxFactory.VariableDeclaration(
+ GenerateTypeSyntax(type, options),
+ SyntaxFactory.SingletonSeparatedList(
+ SyntaxFactory.VariableDeclarator(token, null, SyntaxFactory.EqualsValueClause(
+ assignExpression.OperatorToken, assignExpression.Right)))));
+ declarationStatement = declarationStatement.WithAdditionalAnnotations(Formatter.Annotation);
+
+ var root = token.GetAncestor<CompilationUnitSyntax>();
+ newRoot = root.ReplaceNode(expressionStatement, declarationStatement);
+
+ return true;
+ }
+
+ newRoot = null;
+ return false;
+ }
+
+ private static TypeSyntax GenerateTypeSyntax(ITypeSymbol type, OptionSet options)
+ {
+ return type.ContainsAnonymousType() ||
+ (true/*options.GetOption(CSharpCodeStyleOptions.UseVarWhenDeclaringLocals)*/ && type.TypeKind != TypeKind.Delegate)
+ ? SyntaxFactory.IdentifierName("var")
+ : type.GenerateTypeSyntax();
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateType/AbstractGenerateTypeService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateType/AbstractGenerateTypeService.cs
new file mode 100644
index 0000000000..0c89087fed
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateType/AbstractGenerateTypeService.cs
@@ -0,0 +1,1812 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.CodeGeneration;
+using Microsoft.CodeAnalysis.Host;
+using Microsoft.CodeAnalysis.Internal.Log;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Shared.Utilities;
+using Roslyn.Utilities;
+using ICSharpCode.NRefactory6.CSharp.CodeGeneration;
+using Microsoft.CodeAnalysis.Editing;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.CodeAnalysis.FindSymbols;
+using System.IO;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateType
+{
+ public abstract partial class AbstractGenerateTypeService<TService, TSimpleNameSyntax, TObjectCreationExpressionSyntax, TExpressionSyntax, TTypeDeclarationSyntax, TArgumentSyntax>
+ where TService : AbstractGenerateTypeService<TService, TSimpleNameSyntax, TObjectCreationExpressionSyntax, TExpressionSyntax, TTypeDeclarationSyntax, TArgumentSyntax>
+ where TSimpleNameSyntax : TExpressionSyntax
+ where TObjectCreationExpressionSyntax : TExpressionSyntax
+ where TExpressionSyntax : SyntaxNode
+ where TTypeDeclarationSyntax : SyntaxNode
+ where TArgumentSyntax : SyntaxNode
+ {
+ protected AbstractGenerateTypeService ()
+ {
+ }
+
+ protected abstract bool TryInitializeState (SemanticDocument document, TSimpleNameSyntax simpleName, CancellationToken cancellationToken, out GenerateTypeServiceStateOptions generateTypeServiceStateOptions);
+
+ protected abstract TExpressionSyntax GetLeftSideOfDot (TSimpleNameSyntax simpleName);
+
+ protected abstract bool TryGetArgumentList (TObjectCreationExpressionSyntax objectCreationExpression, out IList<TArgumentSyntax> argumentList);
+
+ protected abstract string DefaultFileExtension { get; }
+
+ protected abstract IList<ITypeParameterSymbol> GetTypeParameters (State state, SemanticModel semanticModel, CancellationToken cancellationToken);
+
+ protected abstract Accessibility GetAccessibility (State state, SemanticModel semanticModel, bool intoNamespace, CancellationToken cancellationToken);
+
+ protected abstract IList<string> GenerateParameterNames (SemanticModel semanticModel, IList<TArgumentSyntax> arguments);
+
+ protected abstract INamedTypeSymbol DetermineTypeToGenerateIn (SemanticModel semanticModel, TSimpleNameSyntax simpleName, CancellationToken cancellationToken);
+
+ protected abstract ITypeSymbol DetermineArgumentType (SemanticModel semanticModel, TArgumentSyntax argument, CancellationToken cancellationToken);
+
+ protected abstract bool IsInCatchDeclaration (TExpressionSyntax expression);
+
+ protected abstract bool IsArrayElementType (TExpressionSyntax expression);
+
+ protected abstract bool IsInVariableTypeContext (TExpressionSyntax expression);
+
+ protected abstract bool IsInValueTypeConstraintContext (SemanticModel semanticModel, TExpressionSyntax expression, CancellationToken cancellationToken);
+
+ protected abstract bool IsInInterfaceList (TExpressionSyntax expression);
+
+ internal abstract bool TryGetBaseList (TExpressionSyntax expression, out TypeKindOptions returnValue);
+
+ internal abstract bool IsPublicOnlyAccessibility (TExpressionSyntax expression, Project project);
+
+ internal abstract bool IsGenericName (TSimpleNameSyntax simpleName);
+
+ internal abstract bool IsSimpleName (TExpressionSyntax expression);
+
+ internal abstract Solution TryAddUsingsOrImportToDocument (Solution updatedSolution, SyntaxNode modifiedRoot, Document document, TSimpleNameSyntax simpleName, string includeUsingsOrImports, CancellationToken cancellationToken);
+
+ protected abstract bool TryGetNameParts (TExpressionSyntax expression, out IList<string> nameParts);
+
+ public abstract string GetRootNamespace (CompilationOptions options);
+
+ public abstract Task<Tuple<INamespaceSymbol, INamespaceOrTypeSymbol, Location>> GetOrGenerateEnclosingNamespaceSymbol (INamedTypeSymbol namedTypeSymbol, string[] containers, Document selectedDocument, SyntaxNode selectedDocumentRoot, CancellationToken cancellationToken);
+
+ public async Task<IEnumerable<CodeAction>> GenerateTypeAsync (
+ Document document,
+ SyntaxNode node,
+ CancellationToken cancellationToken)
+ {
+ //using (Logger.LogBlock (FunctionId.Refactoring_GenerateType, cancellationToken)) {
+ var semanticDocument = await SemanticDocument.CreateAsync (document, cancellationToken).ConfigureAwait (false);
+
+ var state = State.Generate ((TService)this, semanticDocument, node, cancellationToken);
+ if (state != null) {
+ return GetActions (semanticDocument, node, state, cancellationToken);
+ }
+
+ return SpecializedCollections.EmptyEnumerable<CodeAction> ();
+ //}
+ }
+
+ private IEnumerable<CodeAction> GetActions (
+ SemanticDocument document,
+ SyntaxNode node,
+ State state,
+ CancellationToken cancellationToken)
+ {
+ var generateNewTypeInDialog = false;
+ if (state.NamespaceToGenerateInOpt != null) {
+ var workspace = document.Project.Solution.Workspace;
+ if (workspace == null || workspace.CanApplyChange (ApplyChangesKind.AddDocument)) {
+ generateNewTypeInDialog = true;
+ yield return new GenerateTypeCodeAction ((TService)this, document.Document, state, intoNamespace: true, inNewFile: true);
+ }
+
+ // If they just are generating "Foo" then we want to offer to generate it into the
+ // namespace in the same file. However, if they are generating "SomeNS.Foo", then we
+ // only want to allow them to generate if "SomeNS" is the namespace they are
+ // currently in.
+ var isSimpleName = state.SimpleName == state.NameOrMemberAccessExpression;
+ var generateIntoContaining = IsGeneratingIntoContainingNamespace (document, node, state, cancellationToken);
+
+ if ((isSimpleName || generateIntoContaining) &&
+ CanGenerateIntoContainingNamespace (document, node, state, cancellationToken)) {
+ yield return new GenerateTypeCodeAction ((TService)this, document.Document, state, intoNamespace: true, inNewFile: false);
+ }
+ }
+
+ if (state.TypeToGenerateInOpt != null) {
+ yield return new GenerateTypeCodeAction ((TService)this, document.Document, state, intoNamespace: false, inNewFile: false);
+ }
+
+ //if (generateNewTypeInDialog) {
+ // yield return new GenerateTypeCodeActionWithOption ((TService)this, document.Document, state);
+ //}
+ }
+
+ private bool CanGenerateIntoContainingNamespace (SemanticDocument document, SyntaxNode node, State state, CancellationToken cancellationToken)
+ {
+ var containingNamespace = document.SemanticModel.GetEnclosingNamespace (node.SpanStart, cancellationToken);
+
+ // Only allow if the containing namespace is one that can be generated
+ // into.
+ var decl = containingNamespace.GetDeclarations ()
+ .Where (r => r.SyntaxTree == node.SyntaxTree)
+ .Select (r => r.GetSyntax (cancellationToken))
+ .FirstOrDefault (node.GetAncestorsOrThis<SyntaxNode> ().Contains);
+
+ return
+ decl != null &&
+ new CSharpCodeGenerationService (document.Project.Solution.Workspace).CanAddTo (decl, document.Project.Solution, cancellationToken);
+ }
+
+ private bool IsGeneratingIntoContainingNamespace (
+ SemanticDocument document,
+ SyntaxNode node,
+ State state,
+ CancellationToken cancellationToken)
+ {
+ var containingNamespace = document.SemanticModel.GetEnclosingNamespace (node.SpanStart, cancellationToken);
+ if (containingNamespace != null) {
+ var containingNamespaceName = containingNamespace.ToDisplayString ();
+ return containingNamespaceName.Equals (state.NamespaceToGenerateInOpt);
+ }
+
+ return false;
+ }
+
+ protected static string GetTypeName (State state)
+ {
+ const string AttributeSuffix = "Attribute";
+
+ return state.IsAttribute && !state.NameIsVerbatim && !state.Name.EndsWith (AttributeSuffix, StringComparison.Ordinal)
+ ? state.Name + AttributeSuffix
+ : state.Name;
+ }
+
+ protected IList<ITypeParameterSymbol> GetTypeParameters (
+ State state,
+ SemanticModel semanticModel,
+ IEnumerable<SyntaxNode> typeArguments,
+ CancellationToken cancellationToken)
+ {
+ var arguments = typeArguments.ToList ();
+ var arity = arguments.Count;
+ var typeParameters = new List<ITypeParameterSymbol> ();
+
+ // For anything that was a type parameter, just use the name (if we haven't already
+ // used it). Otherwise, synthesize new names for the parameters.
+ var names = new string[arity];
+ var isFixed = new bool[arity];
+ for (var i = 0; i < arity; i++) {
+ var argument = i < arguments.Count ? arguments [i] : null;
+ var type = argument == null ? null : semanticModel.GetTypeInfo (argument, cancellationToken).Type;
+ if (type is ITypeParameterSymbol) {
+ var name = type.Name;
+
+ // If we haven't seen this type parameter already, then we can use this name
+ // and 'fix' it so that it doesn't change. Otherwise, use it, but allow it
+ // to be changed if it collides with anything else.
+ isFixed [i] = !names.Contains (name);
+ names [i] = name;
+ typeParameters.Add ((ITypeParameterSymbol)type);
+ } else {
+ names [i] = "T";
+ typeParameters.Add (null);
+ }
+ }
+
+ // We can use a type parameter as long as it hasn't been used in an outer type.
+ var canUse = state.TypeToGenerateInOpt == null
+ ? default(Func<string, bool>)
+ : s => state.TypeToGenerateInOpt.GetAllTypeParameters ().All (t => t.Name != s);
+
+ var uniqueNames = NameGenerator.EnsureUniqueness (names, isFixed, canUse: canUse);
+ for (int i = 0; i < uniqueNames.Count; i++) {
+ if (typeParameters [i] == null || typeParameters [i].Name != uniqueNames [i]) {
+ typeParameters [i] = CodeGenerationSymbolFactory.CreateTypeParameterSymbol (uniqueNames [i]);
+ }
+ }
+
+ return typeParameters;
+ }
+
+ protected Accessibility DetermineDefaultAccessibility (
+ State state,
+ SemanticModel semanticModel,
+ bool intoNamespace,
+ CancellationToken cancellationToken)
+ {
+ if (state.IsPublicAccessibilityForTypeGeneration) {
+ return Accessibility.Public;
+ }
+
+ // If we're a nested type of the type being generated into, then the new type can be
+ // private. otherwise, it needs to be internal.
+ if (!intoNamespace && state.TypeToGenerateInOpt != null) {
+ var outerTypeSymbol = semanticModel.GetEnclosingNamedType (state.SimpleName.SpanStart, cancellationToken);
+
+ if (outerTypeSymbol != null && outerTypeSymbol.IsContainedWithin (state.TypeToGenerateInOpt)) {
+ return Accessibility.Private;
+ }
+ }
+
+ return Accessibility.Internal;
+ }
+
+ protected IList<ITypeParameterSymbol> GetAvailableTypeParameters (
+ State state,
+ SemanticModel semanticModel,
+ bool intoNamespace,
+ CancellationToken cancellationToken)
+ {
+ var availableInnerTypeParameters = GetTypeParameters (state, semanticModel, cancellationToken);
+ var availableOuterTypeParameters = !intoNamespace && state.TypeToGenerateInOpt != null
+ ? state.TypeToGenerateInOpt.GetAllTypeParameters ()
+ : SpecializedCollections.EmptyEnumerable<ITypeParameterSymbol> ();
+
+ return availableOuterTypeParameters.Concat (availableInnerTypeParameters).ToList ();
+ }
+
+ protected bool IsWithinTheImportingNamespace (Document document, int triggeringPosition, string includeUsingsOrImports, CancellationToken cancellationToken)
+ {
+ var semanticModel = document.GetSemanticModelAsync (cancellationToken).WaitAndGetResult (cancellationToken);
+ if (semanticModel != null) {
+ var namespaceSymbol = semanticModel.GetEnclosingNamespace (triggeringPosition, cancellationToken);
+ if (namespaceSymbol != null && namespaceSymbol.ToDisplayString ().StartsWith (includeUsingsOrImports, StringComparison.Ordinal)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ protected bool GeneratedTypesMustBePublic (Project project)
+ {
+// var projectInfoService = project.Solution.Workspace.Services.GetService<IProjectInfoService> ();
+// if (projectInfoService != null) {
+// return projectInfoService.GeneratedTypesMustBePublic (project);
+// }
+
+ return false;
+ }
+
+ private class GenerateTypeCodeAction : CodeAction
+ {
+ private readonly bool _intoNamespace;
+ private readonly bool _inNewFile;
+ private readonly TService _service;
+ private readonly Document _document;
+ private readonly State _state;
+ private readonly string _equivalenceKey;
+
+ public GenerateTypeCodeAction (
+ TService service,
+ Document document,
+ State state,
+ bool intoNamespace,
+ bool inNewFile)
+ {
+ _service = service;
+ _document = document;
+ _state = state;
+ _intoNamespace = intoNamespace;
+ _inNewFile = inNewFile;
+ _equivalenceKey = Title;
+ }
+
+ private static string FormatDisplayText (
+ State state,
+ bool inNewFile,
+ string destination)
+ {
+ var finalName = GetTypeName (state);
+
+ if (inNewFile) {
+ return string.Format (Resources.GenerateForInNewFile,
+ state.IsStruct ? "struct" : state.IsInterface ? "interface" : "class",
+ state.Name, destination);
+ } else {
+ return string.Format (Resources.GenerateForIn,
+ state.IsStruct ? "struct" : state.IsInterface ? "interface" : "class",
+ state.Name, destination);
+ }
+ }
+
+ protected override async Task<IEnumerable<CodeActionOperation>> ComputeOperationsAsync (CancellationToken cancellationToken)
+ {
+ var semanticDocument = await SemanticDocument.CreateAsync (_document, cancellationToken).ConfigureAwait (false);
+
+ var editor = new Editor ( _service, semanticDocument, _state, _intoNamespace, _inNewFile, cancellationToken: cancellationToken);
+
+ return await editor.GetOperationsAsync ().ConfigureAwait (false);
+ }
+
+ public override string Title {
+ get {
+ if (_intoNamespace) {
+ var namespaceToGenerateIn = string.IsNullOrEmpty (_state.NamespaceToGenerateInOpt) ? Resources.GlobalNamespace : _state.NamespaceToGenerateInOpt;
+ return FormatDisplayText (_state, _inNewFile, namespaceToGenerateIn);
+ } else {
+ return FormatDisplayText (_state, inNewFile: false, destination: _state.TypeToGenerateInOpt.Name);
+ }
+ }
+ }
+
+ public override string EquivalenceKey {
+ get {
+ return _equivalenceKey;
+ }
+ }
+ }
+
+ private class GenerateTypeCodeActionWithOption : CodeActionWithOptions
+ {
+ private readonly TService _service;
+ private readonly Document _document;
+ private readonly State _state;
+
+ internal GenerateTypeCodeActionWithOption (TService service, Document document, State state)
+ {
+ _service = service;
+ _document = document;
+ _state = state;
+ }
+
+ public override string Title {
+ get {
+ return Resources.GenerateNewType;
+ }
+ }
+
+ public override string EquivalenceKey {
+ get {
+ return _state.Name;
+ }
+ }
+
+ public override object GetOptions (CancellationToken cancellationToken)
+ {
+ var typeKindValue = GetTypeKindOption (_state);
+ var isPublicOnlyAccessibility = IsPublicOnlyAccessibility (_state, _document.Project);
+
+ // TODO : Callback
+ return new GenerateTypeOptionsResult (
+ isPublicOnlyAccessibility ? Accessibility.Public : Accessibility.Internal,
+ TypeKind.Class,
+ _state.Name,
+ _document.Project,
+ true,
+ _state.Name + ".cs",
+ null,
+ null,
+ _document,
+ false
+ );
+ /*
+ // return generateTypeOptionsService.GetGenerateTypeOptions (
+// _state.Name,
+ // new GenerateTypeDialogOptions (isPublicOnlyAccessibility, typeKindValue, _state.IsAttribute),
+// _document,
+// notificationService,
+// projectManagementService,
+// syntaxFactsService);
+ private class VisualStudioGenerateTypeOptionsService : IGenerateTypeOptionsService
+ {
+ private bool _isNewFile = false;
+ private string _accessSelectString = "";
+ private string _typeKindSelectString = "";
+
+ private IGeneratedCodeRecognitionService _generatedCodeService;
+
+ public VisualStudioGenerateTypeOptionsService(IGeneratedCodeRecognitionService generatedCodeService)
+ {
+ _generatedCodeService = generatedCodeService;
+ }
+
+ public GenerateTypeOptionsResult GetGenerateTypeOptions(
+ string typeName,
+ GenerateTypeDialogOptions generateTypeDialogOptions,
+ Document document,
+ INotificationService notificationService,
+ IProjectManagementService projectManagementService,
+ ISyntaxFactsService syntaxFactsService)
+ {
+ var viewModel = new GenerateTypeDialogViewModel(
+ document,
+ notificationService,
+ projectManagementService,
+ syntaxFactsService,
+ _generatedCodeService,
+ generateTypeDialogOptions,
+ typeName,
+ document.Project.Language == LanguageNames.CSharp ? ".cs" : ".vb",
+ _isNewFile,
+ _accessSelectString,
+ _typeKindSelectString);
+
+ var dialog = new GenerateTypeDialog(viewModel);
+ var result = dialog.ShowModal();
+
+ if (result.HasValue && result.Value)
+ {
+ // Retain choice
+ _isNewFile = viewModel.IsNewFile;
+ _accessSelectString = viewModel.SelectedAccessibilityString;
+ _typeKindSelectString = viewModel.SelectedTypeKindString;
+
+ return new GenerateTypeOptionsResult(
+ accessibility: viewModel.SelectedAccessibility,
+ typeKind: viewModel.SelectedTypeKind,
+ typeName: viewModel.TypeName,
+ project: viewModel.SelectedProject,
+ isNewFile: viewModel.IsNewFile,
+ newFileName: viewModel.FileName.Trim(),
+ folders: viewModel.Folders,
+ fullFilePath: viewModel.FullFilePath,
+ existingDocument: viewModel.SelectedDocument,
+ areFoldersValidIdentifiers: viewModel.AreFoldersValidIdentifiers);
+ }
+ else
+ {
+ return GenerateTypeOptionsResult.Cancelled;
+ }
+ }
+ }
+
+ */
+ }
+
+ private bool IsPublicOnlyAccessibility (State state, Project project)
+ {
+ return _service.IsPublicOnlyAccessibility (state.NameOrMemberAccessExpression, project) || _service.IsPublicOnlyAccessibility (state.SimpleName, project);
+ }
+
+ private TypeKindOptions GetTypeKindOption (State state)
+ {
+ TypeKindOptions typeKindValue;
+
+ var gotPreassignedTypeOptions = GetPredefinedTypeKindOption (state, out typeKindValue);
+ if (!gotPreassignedTypeOptions) {
+ typeKindValue = state.IsSimpleNameGeneric ? TypeKindOptionsHelper.RemoveOptions (typeKindValue, TypeKindOptions.GenericInCompatibleTypes) : typeKindValue;
+ typeKindValue = state.IsMembersWithModule ? TypeKindOptionsHelper.AddOption (typeKindValue, TypeKindOptions.Module) : typeKindValue;
+ typeKindValue = state.IsInterfaceOrEnumNotAllowedInTypeContext ? TypeKindOptionsHelper.RemoveOptions (typeKindValue, TypeKindOptions.Interface, TypeKindOptions.Enum) : typeKindValue;
+ typeKindValue = state.IsDelegateAllowed ? typeKindValue : TypeKindOptionsHelper.RemoveOptions (typeKindValue, TypeKindOptions.Delegate);
+ typeKindValue = state.IsEnumNotAllowed ? TypeKindOptionsHelper.RemoveOptions (typeKindValue, TypeKindOptions.Enum) : typeKindValue;
+ }
+
+ return typeKindValue;
+ }
+
+ private bool GetPredefinedTypeKindOption (State state, out TypeKindOptions typeKindValueFinal)
+ {
+ if (state.IsAttribute) {
+ typeKindValueFinal = TypeKindOptions.Attribute;
+ return true;
+ }
+
+ TypeKindOptions typeKindValue = TypeKindOptions.None;
+ if (_service.TryGetBaseList (state.NameOrMemberAccessExpression, out typeKindValue) || _service.TryGetBaseList (state.SimpleName, out typeKindValue)) {
+ typeKindValueFinal = typeKindValue;
+ return true;
+ }
+
+ if (state.IsClassInterfaceTypes) {
+ typeKindValueFinal = TypeKindOptions.BaseList;
+ return true;
+ }
+
+ if (state.IsDelegateOnly) {
+ typeKindValueFinal = TypeKindOptions.Delegate;
+ return true;
+ }
+
+ if (state.IsTypeGeneratedIntoNamespaceFromMemberAccess) {
+ typeKindValueFinal = state.IsSimpleNameGeneric ? TypeKindOptionsHelper.RemoveOptions (TypeKindOptions.MemberAccessWithNamespace, TypeKindOptions.GenericInCompatibleTypes) : TypeKindOptions.MemberAccessWithNamespace;
+ typeKindValueFinal = state.IsEnumNotAllowed ? TypeKindOptionsHelper.RemoveOptions (typeKindValueFinal, TypeKindOptions.Enum) : typeKindValueFinal;
+ return true;
+ }
+
+ typeKindValueFinal = TypeKindOptions.AllOptions;
+ return false;
+ }
+
+ protected override async Task<IEnumerable<CodeActionOperation>> ComputeOperationsAsync (object options, CancellationToken cancellationToken)
+ {
+ IEnumerable<CodeActionOperation> operations = null;
+
+ var generateTypeOptions = options as GenerateTypeOptionsResult;
+ if (generateTypeOptions != null && !generateTypeOptions.IsCancelled) {
+ var semanticDocument = SemanticDocument.CreateAsync (_document, cancellationToken).WaitAndGetResult (cancellationToken);
+ var editor = new Editor (_service, semanticDocument, _state, true, generateTypeOptions, cancellationToken);
+ operations = await editor.GetOperationsAsync ().ConfigureAwait (false);
+ }
+
+ return operations;
+ }
+ }
+
+ protected abstract bool IsConversionImplicit (Compilation compilation, ITypeSymbol sourceType, ITypeSymbol targetType);
+
+ private partial class Editor
+ {
+ private TService _service;
+ private TargetProjectChangeInLanguage _targetProjectChangeInLanguage = TargetProjectChangeInLanguage.NoChange;
+ AbstractGenerateTypeService<TService, TSimpleNameSyntax, TObjectCreationExpressionSyntax, TExpressionSyntax, TTypeDeclarationSyntax, TArgumentSyntax> _targetLanguageService;
+
+ private readonly SemanticDocument _document;
+ private readonly State _state;
+ private readonly bool _intoNamespace;
+ private readonly bool _inNewFile;
+ private readonly bool _fromDialog;
+ private readonly GenerateTypeOptionsResult _generateTypeOptionsResult;
+ private readonly CancellationToken _cancellationToken;
+
+
+ public Editor (
+ TService service,
+ SemanticDocument document,
+ State state,
+ bool intoNamespace,
+ bool inNewFile,
+ CancellationToken cancellationToken)
+ {
+ _service = service;
+ _document = document;
+ _state = state;
+ _intoNamespace = intoNamespace;
+ _inNewFile = inNewFile;
+ _cancellationToken = cancellationToken;
+ }
+
+ public Editor (
+ TService service,
+ SemanticDocument document,
+ State state,
+ bool fromDialog,
+ GenerateTypeOptionsResult generateTypeOptionsResult,
+ CancellationToken cancellationToken)
+ {
+ _service = service;
+ _document = document;
+ _state = state;
+ _fromDialog = fromDialog;
+ _generateTypeOptionsResult = generateTypeOptionsResult;
+ _cancellationToken = cancellationToken;
+ }
+
+ private enum TargetProjectChangeInLanguage
+ {
+ NoChange,
+ CSharpToVisualBasic,
+ VisualBasicToCSharp
+ }
+
+ internal async Task<IEnumerable<CodeActionOperation>> GetOperationsAsync ()
+ {
+ // Check to see if it is from GFU Dialog
+ if (!_fromDialog) {
+ // Generate the actual type declaration.
+ var namedType = GenerateNamedType ();
+
+ if (_intoNamespace) {
+ if (_inNewFile) {
+ // Generating into a new file is somewhat complicated.
+ var documentName = GetTypeName (_state) + _service.DefaultFileExtension;
+
+ return await GetGenerateInNewFileOperationsAsync (
+ namedType,
+ documentName,
+ null,
+ true,
+ null,
+ _document.Project,
+ _document.Project,
+ isDialog: false).ConfigureAwait (false);
+ } else {
+ return await GetGenerateIntoContainingNamespaceOperationsAsync (namedType).ConfigureAwait (false);
+ }
+ } else {
+ return await GetGenerateIntoTypeOperationsAsync (namedType).ConfigureAwait (false);
+ }
+ } else {
+ var namedType = GenerateNamedType (_generateTypeOptionsResult);
+
+// // Honor the options from the dialog
+// // Check to see if the type is requested to be generated in cross language Project
+// // e.g.: C# -> VB or VB -> C#
+// if (_document.Project.Language != _generateTypeOptionsResult.Project.Language) {
+// _targetProjectChangeInLanguage =
+// _generateTypeOptionsResult.Project.Language == LanguageNames.CSharp
+// ? TargetProjectChangeInLanguage.VisualBasicToCSharp
+// : TargetProjectChangeInLanguage.CSharpToVisualBasic;
+//
+// // Get the cross language service
+// _targetLanguageService = _generateTypeOptionsResult.Project.LanguageServices.GetService<IGenerateTypeService> ();
+// }
+
+ if (_generateTypeOptionsResult.IsNewFile) {
+ return await GetGenerateInNewFileOperationsAsync (
+ namedType,
+ _generateTypeOptionsResult.NewFileName,
+ _generateTypeOptionsResult.Folders,
+ _generateTypeOptionsResult.AreFoldersValidIdentifiers,
+ _generateTypeOptionsResult.FullFilePath,
+ _generateTypeOptionsResult.Project,
+ _document.Project,
+ isDialog: true).ConfigureAwait (false);
+ } else {
+ return await GetGenerateIntoExistingDocumentAsync (
+ namedType,
+ _document.Project,
+ _generateTypeOptionsResult,
+ isDialog: true).ConfigureAwait (false);
+ }
+ }
+ }
+
+ private string GetNamespaceToGenerateInto ()
+ {
+ var namespaceToGenerateInto = _state.NamespaceToGenerateInOpt.Trim ();
+ var rootNamespace = _service.GetRootNamespace (_document.SemanticModel.Compilation.Options).Trim ();
+ if (!string.IsNullOrWhiteSpace (rootNamespace)) {
+ if (namespaceToGenerateInto == rootNamespace ||
+ namespaceToGenerateInto.StartsWith (rootNamespace + ".", StringComparison.Ordinal)) {
+ namespaceToGenerateInto = namespaceToGenerateInto.Substring (rootNamespace.Length);
+ }
+ }
+
+ return namespaceToGenerateInto;
+ }
+
+ private string GetNamespaceToGenerateIntoForUsageWithNamespace (Project targetProject, Project triggeringProject)
+ {
+ var namespaceToGenerateInto = _state.NamespaceToGenerateInOpt.Trim ();
+
+ if (targetProject.Language == LanguageNames.CSharp ||
+ targetProject == triggeringProject) {
+ // If the target project is C# project then we don't have to make any modification to the namespace
+ // or
+ // This is a VB project generation into itself which requires no change as well
+ return namespaceToGenerateInto;
+ }
+
+ // If the target Project is VB then we have to check if the RootNamespace of the VB project is the parent most namespace of the type being generated
+ // True, Remove the RootNamespace
+ // False, Add Global to the Namespace
+ //Contract.Assert (targetProject.Language == LanguageNames.VisualBasic);
+ var targetLanguageService = _targetLanguageService;
+// if (_document.Project.Language == LanguageNames.VisualBasic) {
+// targetLanguageService = _service;
+// } else {
+// targetLanguageService = _targetLanguageService;
+// }
+
+ var rootNamespace = targetLanguageService.GetRootNamespace (targetProject.CompilationOptions).Trim ();
+ if (!string.IsNullOrWhiteSpace (rootNamespace)) {
+ var rootNamespaceLength = CheckIfRootNamespacePresentInNamespace (namespaceToGenerateInto, rootNamespace);
+ if (rootNamespaceLength > -1) {
+ // True, Remove the RootNamespace
+ namespaceToGenerateInto = namespaceToGenerateInto.Substring (rootNamespaceLength);
+ } else {
+ // False, Add Global to the Namespace
+ namespaceToGenerateInto = AddGlobalDotToTheNamespace (namespaceToGenerateInto);
+ }
+ } else {
+ // False, Add Global to the Namespace
+ namespaceToGenerateInto = AddGlobalDotToTheNamespace (namespaceToGenerateInto);
+ }
+
+ return namespaceToGenerateInto;
+ }
+
+ private string AddGlobalDotToTheNamespace (string namespaceToBeGenerated)
+ {
+ return "Global." + namespaceToBeGenerated;
+ }
+
+ // Returns the length of the meaningful rootNamespace substring part of namespaceToGenerateInto
+ private int CheckIfRootNamespacePresentInNamespace (string namespaceToGenerateInto, string rootNamespace)
+ {
+ if (namespaceToGenerateInto == rootNamespace) {
+ return rootNamespace.Length;
+ }
+
+ if (namespaceToGenerateInto.StartsWith (rootNamespace + ".", StringComparison.Ordinal)) {
+ return rootNamespace.Length + 1;
+ }
+
+ return -1;
+ }
+
+ private void AddFoldersToNamespaceContainers (List<string> container, IList<string> folders)
+ {
+ // Add the folder as part of the namespace if there are not empty
+ if (folders != null && folders.Count != 0) {
+ // Remove the empty entries and replace the spaces in the folder name to '_'
+ var refinedFolders = folders.Where (n => n != null && !n.IsEmpty ()).Select (n => n.Replace (' ', '_')).ToArray ();
+ container.AddRange (refinedFolders);
+ }
+ }
+
+ private async Task<IEnumerable<CodeActionOperation>> GetGenerateInNewFileOperationsAsync (
+ INamedTypeSymbol namedType,
+ string documentName,
+ IList<string> folders,
+ bool areFoldersValidIdentifiers,
+ string fullFilePath,
+ Project projectToBeUpdated,
+ Project triggeringProject,
+ bool isDialog)
+ {
+ // First, we fork the solution with a new, empty, file in it.
+ var newDocumentId = DocumentId.CreateNewId (projectToBeUpdated.Id, debugName: documentName);
+ var newSolution = projectToBeUpdated.Solution.AddDocument (newDocumentId, documentName, string.Empty, folders, fullFilePath);
+
+ // Now we get the semantic model for that file we just added. We do that to get the
+ // root namespace in that new document, along with location for that new namespace.
+ // That way, when we use the code gen service we can say "add this symbol to the
+ // root namespace" and it will pick the one in the new file.
+ var newDocument = newSolution.GetDocument (newDocumentId);
+ var newSemanticModel = await newDocument.GetSemanticModelAsync (_cancellationToken).ConfigureAwait (false);
+ var enclosingNamespace = newSemanticModel.GetEnclosingNamespace (0, _cancellationToken);
+
+ var namespaceContainersAndUsings = GetNamespaceContainersAndAddUsingsOrImport (isDialog, folders, areFoldersValidIdentifiers, projectToBeUpdated, triggeringProject);
+
+ var containers = namespaceContainersAndUsings.Item1;
+ var includeUsingsOrImports = namespaceContainersAndUsings.Item2;
+
+ var rootNamespaceOrType = namedType.GenerateRootNamespaceOrType (containers);
+
+ // Now, actually ask the code gen service to add this namespace or type to the root
+ // namespace in the new file. This will properly generate the code, and add any
+ // additional niceties like imports/usings.
+ var codeGenResult = await CodeGenerator.AddNamespaceOrTypeDeclarationAsync (
+ newSolution,
+ enclosingNamespace,
+ rootNamespaceOrType,
+ new CodeGenerationOptions (newSemanticModel.SyntaxTree.GetLocation (new TextSpan ()), generateDefaultAccessibility: false),
+ _cancellationToken).ConfigureAwait (false);
+
+ // containers is determined to be
+ // 1: folders -> if triggered from Dialog
+ // 2: containers -> if triggered not from a Dialog but from QualifiedName
+ // 3: triggering document folder structure -> if triggered not from a Dialog and a SimpleName
+ var adjustedContainer = isDialog ? folders :
+ _state.SimpleName != _state.NameOrMemberAccessExpression ? containers.ToList () : _document.Document.Folders.ToList ();
+
+ // Now, take the code that would be generated and actually create an edit that would
+ // produce a document with that code in it.
+
+ return CreateAddDocumentAndUpdateUsingsOrImportsOperations (
+ projectToBeUpdated,
+ triggeringProject,
+ documentName,
+ await codeGenResult.GetSyntaxRootAsync (_cancellationToken).ConfigureAwait (false),
+ _document.Document,
+ includeUsingsOrImports,
+ adjustedContainer,
+ SourceCodeKind.Regular,
+ _cancellationToken);
+ }
+
+ private IEnumerable<CodeActionOperation> CreateAddDocumentAndUpdateUsingsOrImportsOperations (
+ Project projectToBeUpdated,
+ Project triggeringProject,
+ string documentName,
+ SyntaxNode root,
+ Document generatingDocument,
+ string includeUsingsOrImports,
+ IList<string> containers,
+ SourceCodeKind sourceCodeKind,
+ CancellationToken cancellationToken)
+ {
+ // TODO(cyrusn): make sure documentId is unique.
+ var documentId = DocumentId.CreateNewId (projectToBeUpdated.Id, documentName);
+
+ var updatedSolution = projectToBeUpdated.Solution.AddDocument (DocumentInfo.Create (
+ documentId,
+ documentName,
+ containers,
+ sourceCodeKind,
+ filePath: Path.Combine (Path.GetDirectoryName (generatingDocument.FilePath), documentName)
+ ));
+
+ updatedSolution = updatedSolution.WithDocumentSyntaxRoot (documentId, root, PreservationMode.PreserveIdentity);
+
+ // Update the Generating Document with a using if required
+ if (includeUsingsOrImports != null) {
+ updatedSolution = _service.TryAddUsingsOrImportToDocument (updatedSolution, null, _document.Document, _state.SimpleName, includeUsingsOrImports, cancellationToken);
+ }
+
+ // Add reference of the updated project to the triggering Project if they are 2 different projects
+ updatedSolution = AddProjectReference (projectToBeUpdated, triggeringProject, updatedSolution);
+
+ return new CodeActionOperation[] {
+ new ApplyChangesOperation (updatedSolution),
+ new OpenDocumentOperation (documentId)
+ };
+ }
+
+ private static Solution AddProjectReference (Project projectToBeUpdated, Project triggeringProject, Solution updatedSolution)
+ {
+ if (projectToBeUpdated != triggeringProject) {
+ if (!triggeringProject.ProjectReferences.Any (pr => pr.ProjectId == projectToBeUpdated.Id)) {
+ updatedSolution = updatedSolution.AddProjectReference (triggeringProject.Id, new ProjectReference (projectToBeUpdated.Id));
+ }
+ }
+
+ return updatedSolution;
+ }
+
+ private async Task<IEnumerable<CodeActionOperation>> GetGenerateIntoContainingNamespaceOperationsAsync (INamedTypeSymbol namedType)
+ {
+ var enclosingNamespace = _document.SemanticModel.GetEnclosingNamespace (
+ _state.SimpleName.SpanStart, _cancellationToken);
+
+ var solution = _document.Project.Solution;
+ var codeGenResult = await CodeGenerator.AddNamedTypeDeclarationAsync (
+ solution,
+ enclosingNamespace,
+ namedType,
+ new CodeGenerationOptions (afterThisLocation: _document.SyntaxTree.GetLocation (_state.SimpleName.Span), generateDefaultAccessibility: false),
+ _cancellationToken)
+ .ConfigureAwait (false);
+
+ return new CodeActionOperation[] { new ApplyChangesOperation (codeGenResult.Project.Solution) };
+ }
+
+ private async Task<IEnumerable<CodeActionOperation>> GetGenerateIntoExistingDocumentAsync (
+ INamedTypeSymbol namedType,
+ Project triggeringProject,
+ GenerateTypeOptionsResult generateTypeOptionsResult,
+ bool isDialog)
+ {
+ var root = await generateTypeOptionsResult.ExistingDocument.GetSyntaxRootAsync (_cancellationToken).ConfigureAwait (false);
+ var folders = generateTypeOptionsResult.ExistingDocument.Folders;
+
+ var namespaceContainersAndUsings = GetNamespaceContainersAndAddUsingsOrImport (isDialog, new List<string> (folders), generateTypeOptionsResult.AreFoldersValidIdentifiers, generateTypeOptionsResult.Project, triggeringProject);
+
+ var containers = namespaceContainersAndUsings.Item1;
+ var includeUsingsOrImports = namespaceContainersAndUsings.Item2;
+
+ Tuple<INamespaceSymbol, INamespaceOrTypeSymbol, Location> enclosingNamespaceGeneratedTypeToAddAndLocation = null;
+ if (_targetProjectChangeInLanguage == TargetProjectChangeInLanguage.NoChange) {
+ enclosingNamespaceGeneratedTypeToAddAndLocation = _service.GetOrGenerateEnclosingNamespaceSymbol (
+ namedType,
+ containers,
+ generateTypeOptionsResult.ExistingDocument,
+ root,
+ _cancellationToken).WaitAndGetResult (_cancellationToken);
+ } else {
+ enclosingNamespaceGeneratedTypeToAddAndLocation = _targetLanguageService.GetOrGenerateEnclosingNamespaceSymbol (
+ namedType,
+ containers,
+ generateTypeOptionsResult.ExistingDocument,
+ root,
+ _cancellationToken).WaitAndGetResult (_cancellationToken);
+ }
+
+ var solution = _document.Project.Solution;
+ var codeGenResult = await CodeGenerator.AddNamespaceOrTypeDeclarationAsync (
+ solution,
+ enclosingNamespaceGeneratedTypeToAddAndLocation.Item1,
+ enclosingNamespaceGeneratedTypeToAddAndLocation.Item2,
+ new CodeGenerationOptions (afterThisLocation: enclosingNamespaceGeneratedTypeToAddAndLocation.Item3, generateDefaultAccessibility: false),
+ _cancellationToken)
+ .ConfigureAwait (false);
+ var newRoot = await codeGenResult.GetSyntaxRootAsync (_cancellationToken).ConfigureAwait (false);
+ var updatedSolution = solution.WithDocumentSyntaxRoot (generateTypeOptionsResult.ExistingDocument.Id, newRoot, PreservationMode.PreserveIdentity);
+
+ // Update the Generating Document with a using if required
+ if (includeUsingsOrImports != null) {
+ updatedSolution = _service.TryAddUsingsOrImportToDocument (
+ updatedSolution,
+ generateTypeOptionsResult.ExistingDocument.Id == _document.Document.Id ? newRoot : null,
+ _document.Document,
+ _state.SimpleName,
+ includeUsingsOrImports,
+ _cancellationToken);
+ }
+
+ updatedSolution = AddProjectReference (generateTypeOptionsResult.Project, triggeringProject, updatedSolution);
+
+ return new CodeActionOperation[] { new ApplyChangesOperation (updatedSolution) };
+ }
+
+ private Tuple<string[], string> GetNamespaceContainersAndAddUsingsOrImport (
+ bool isDialog,
+ IList<string> folders,
+ bool areFoldersValidIdentifiers,
+ Project targetProject,
+ Project triggeringProject)
+ {
+ string includeUsingsOrImports = null;
+ if (!areFoldersValidIdentifiers) {
+ folders = SpecializedCollections.EmptyList<string> ();
+ }
+
+ // Now actually create the symbol that we want to add to the root namespace. The
+ // symbol may either be a named type (if we're not generating into a namespace) or
+ // it may be a namespace symbol.
+ string[] containers = null;
+ if (!isDialog) {
+ // Not generated from the Dialog
+ containers = GetNamespaceToGenerateInto ().Split (new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
+ } else if (!_service.IsSimpleName (_state.NameOrMemberAccessExpression)) {
+ // If the usage was with a namespace
+ containers = GetNamespaceToGenerateIntoForUsageWithNamespace (targetProject, triggeringProject).Split (new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
+ } else {
+ // Generated from the Dialog
+ List<string> containerList = new List<string> ();
+
+ string rootNamespaceOfTheProjectGeneratedInto;
+
+ if (_targetProjectChangeInLanguage == TargetProjectChangeInLanguage.NoChange) {
+ rootNamespaceOfTheProjectGeneratedInto = _service.GetRootNamespace (_generateTypeOptionsResult.Project.CompilationOptions).Trim ();
+ } else {
+ rootNamespaceOfTheProjectGeneratedInto = _targetLanguageService.GetRootNamespace (_generateTypeOptionsResult.Project.CompilationOptions).Trim ();
+ }
+
+ // TODO : Default namespace support
+ //var projectManagementService = _document.Project.Solution.Workspace.Services.GetService<IProjectManagementService> ();
+ var defaultNamespace = "";// projectManagementService.GetDefaultNamespace (targetProject, targetProject.Solution.Workspace);
+
+ // Case 1 : If the type is generated into the same C# project or
+ // Case 2 : If the type is generated from a C# project to a C# Project
+ // Case 3 : If the Type is generated from a VB Project to a C# Project
+ // Using and Namespace will be the DefaultNamespace + Folder Structure
+ if ((_document.Project == _generateTypeOptionsResult.Project && _document.Project.Language == LanguageNames.CSharp) ||
+ (_targetProjectChangeInLanguage == TargetProjectChangeInLanguage.NoChange && _generateTypeOptionsResult.Project.Language == LanguageNames.CSharp) ||
+ _targetProjectChangeInLanguage == TargetProjectChangeInLanguage.VisualBasicToCSharp) {
+ if (!string.IsNullOrWhiteSpace (defaultNamespace)) {
+ containerList.Add (defaultNamespace);
+ }
+
+ // Populate the ContainerList
+ AddFoldersToNamespaceContainers (containerList, folders);
+
+ containers = containerList.ToArray ();
+ includeUsingsOrImports = string.Join (".", containerList.ToArray ());
+ }
+
+ // Case 4 : If the type is generated into the same VB project or
+ // Case 5 : If Type is generated from a VB Project to VB Project
+ // Case 6 : If Type is generated from a C# Project to VB Project
+ // Namespace will be Folder Structure and Import will have the RootNamespace of the project generated into as part of the Imports
+ if ((_document.Project == _generateTypeOptionsResult.Project && _document.Project.Language == LanguageNames.VisualBasic) ||
+ (_document.Project != _generateTypeOptionsResult.Project && _targetProjectChangeInLanguage == TargetProjectChangeInLanguage.NoChange && _generateTypeOptionsResult.Project.Language == LanguageNames.VisualBasic) ||
+ _targetProjectChangeInLanguage == TargetProjectChangeInLanguage.CSharpToVisualBasic) {
+ // Populate the ContainerList
+ AddFoldersToNamespaceContainers (containerList, folders);
+ containers = containerList.ToArray ();
+ includeUsingsOrImports = string.Join (".", containerList.ToArray ());
+ if (!string.IsNullOrWhiteSpace (rootNamespaceOfTheProjectGeneratedInto)) {
+ includeUsingsOrImports = string.IsNullOrEmpty (includeUsingsOrImports) ?
+ rootNamespaceOfTheProjectGeneratedInto :
+ rootNamespaceOfTheProjectGeneratedInto + "." + includeUsingsOrImports;
+ }
+ }
+ }
+
+ return Tuple.Create (containers, includeUsingsOrImports);
+ }
+
+ private async Task<IEnumerable<CodeActionOperation>> GetGenerateIntoTypeOperationsAsync (INamedTypeSymbol namedType)
+ {
+ var codeGenService = GetCodeGenerationService ();
+ var solution = _document.Project.Solution;
+ var codeGenResult = await CodeGenerator.AddNamedTypeDeclarationAsync (
+ solution,
+ _state.TypeToGenerateInOpt,
+ namedType,
+ new CodeGenerationOptions (contextLocation: _state.SimpleName.GetLocation (), generateDefaultAccessibility: false),
+ _cancellationToken)
+ .ConfigureAwait (false);
+
+ return new CodeActionOperation[] { new ApplyChangesOperation (codeGenResult.Project.Solution) };
+ }
+
+ private IList<ITypeSymbol> GetArgumentTypes (IList<TArgumentSyntax> argumentList)
+ {
+ var types = argumentList.Select (a => _service.DetermineArgumentType (_document.SemanticModel, a, _cancellationToken));
+ return types.Select (FixType).ToList ();
+ }
+
+ private ITypeSymbol FixType (
+ ITypeSymbol typeSymbol)
+ {
+ var compilation = _document.SemanticModel.Compilation;
+ return typeSymbol.RemoveUnnamedErrorTypes (compilation);
+ }
+
+ private CSharpCodeGenerationService GetCodeGenerationService ()
+ {
+ var language = _state.TypeToGenerateInOpt == null
+ ? _state.SimpleName.Language
+ : _state.TypeToGenerateInOpt.Language;
+ return new CSharpCodeGenerationService(_document.Project.Solution.Workspace, language);
+ }
+
+ private bool TryFindMatchingField (
+ string parameterName,
+ ITypeSymbol parameterType,
+ Dictionary<string, ISymbol> parameterToFieldMap,
+ bool caseSensitive)
+ {
+ // If the base types have an accessible field or property with the same name and
+ // an acceptable type, then we should just defer to that.
+ if (_state.BaseTypeOrInterfaceOpt != null) {
+ var comparison = caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
+ var query =
+ _state.BaseTypeOrInterfaceOpt
+ .GetBaseTypesAndThis ()
+ .SelectMany (t => t.GetMembers ())
+ .Where (s => s.Name.Equals (parameterName, comparison));
+ var symbol = query.FirstOrDefault (IsSymbolAccessible);
+
+ if (IsViableFieldOrProperty (parameterType, symbol)) {
+ parameterToFieldMap [parameterName] = symbol;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private bool IsViableFieldOrProperty (
+ ITypeSymbol parameterType,
+ ISymbol symbol)
+ {
+ if (symbol != null && !symbol.IsStatic && parameterType.Language == symbol.Language) {
+ if (symbol is IFieldSymbol) {
+ var field = (IFieldSymbol)symbol;
+ return
+ !field.IsReadOnly &&
+ _service.IsConversionImplicit (_document.SemanticModel.Compilation, parameterType, field.Type);
+ } else if (symbol is IPropertySymbol) {
+ var property = (IPropertySymbol)symbol;
+ return
+ property.Parameters.Length == 0 &&
+ property.SetMethod != null &&
+ IsSymbolAccessible (property.SetMethod) &&
+ _service.IsConversionImplicit (_document.SemanticModel.Compilation, parameterType, property.Type);
+ }
+ }
+
+ return false;
+ }
+
+ private bool IsSymbolAccessible (ISymbol symbol)
+ {
+ // Public and protected constructors are accessible. Internal constructors are
+ // accessible if we have friend access. We can't call the normal accessibility
+ // checkers since they will think that a protected constructor isn't accessible
+ // (since we don't have the destination type that would have access to them yet).
+ switch (symbol.DeclaredAccessibility) {
+ case Accessibility.ProtectedOrInternal:
+ case Accessibility.Protected:
+ case Accessibility.Public:
+ return true;
+ case Accessibility.ProtectedAndInternal:
+ case Accessibility.Internal:
+ // TODO: Code coverage
+ return _document.SemanticModel.Compilation.Assembly.IsSameAssemblyOrHasFriendAccessTo (
+ symbol.ContainingAssembly);
+
+ default:
+ return false;
+ }
+ }
+ }
+
+ internal abstract IMethodSymbol GetDelegatingConstructor (TObjectCreationExpressionSyntax objectCreation, INamedTypeSymbol namedType, SemanticModel model, ISet<IMethodSymbol> candidates, CancellationToken cancellationToken);
+
+ private partial class Editor
+ {
+ private INamedTypeSymbol GenerateNamedType ()
+ {
+ return CodeGenerationSymbolFactory.CreateNamedTypeSymbol (
+ DetermineAttributes (),
+ DetermineAccessibility (),
+ DetermineModifiers (),
+ DetermineTypeKind (),
+ DetermineName (),
+ DetermineTypeParameters (),
+ DetermineBaseType (),
+ DetermineInterfaces (),
+ members: DetermineMembers ());
+ }
+
+ private INamedTypeSymbol GenerateNamedType (GenerateTypeOptionsResult options)
+ {
+ if (options.TypeKind == TypeKind.Delegate) {
+ return CodeGenerationSymbolFactory.CreateDelegateTypeSymbol (
+ DetermineAttributes (),
+ options.Accessibility,
+ DetermineModifiers (),
+ DetermineReturnType (options),
+ options.TypeName,
+ DetermineTypeParameters (options),
+ DetermineParameters (options));
+ }
+
+ return CodeGenerationSymbolFactory.CreateNamedTypeSymbol (
+ DetermineAttributes (),
+ options.Accessibility,
+ DetermineModifiers (),
+ options.TypeKind,
+ options.TypeName,
+ DetermineTypeParameters (),
+ DetermineBaseType (),
+ DetermineInterfaces (),
+ members: DetermineMembers (options));
+ }
+
+ private ITypeSymbol DetermineReturnType (GenerateTypeOptionsResult options)
+ {
+ if (_state.DelegateMethodSymbol == null ||
+ _state.DelegateMethodSymbol.ReturnType == null ||
+ _state.DelegateMethodSymbol.ReturnType is IErrorTypeSymbol) {
+ // Since we cannot determine the return type, we are returning void
+ return _state.Compilation.GetSpecialType (SpecialType.System_Void);
+ } else {
+ return _state.DelegateMethodSymbol.ReturnType;
+ }
+ }
+
+ private IList<ITypeParameterSymbol> DetermineTypeParameters (GenerateTypeOptionsResult options)
+ {
+ if (_state.DelegateMethodSymbol != null) {
+ return _state.DelegateMethodSymbol.TypeParameters;
+ }
+
+ // If the delegate symbol cannot be determined then
+ return DetermineTypeParameters ();
+ }
+
+ private IList<IParameterSymbol> DetermineParameters (GenerateTypeOptionsResult options)
+ {
+ if (_state.DelegateMethodSymbol != null) {
+ return _state.DelegateMethodSymbol.Parameters;
+ }
+
+ return null;
+ }
+
+ private IList<ISymbol> DetermineMembers (GenerateTypeOptionsResult options = null)
+ {
+ var members = new List<ISymbol> ();
+ AddMembers (members, options);
+
+ if (_state.IsException) {
+ AddExceptionConstructors (members);
+ }
+
+ return members;
+ }
+
+ private void AddMembers (IList<ISymbol> members, GenerateTypeOptionsResult options = null)
+ {
+ AddProperties (members);
+
+ IList<TArgumentSyntax> argumentList;
+ if (!_service.TryGetArgumentList (_state.ObjectCreationExpressionOpt, out argumentList)) {
+ return;
+ }
+
+ var parameterTypes = GetArgumentTypes (argumentList);
+
+ // Don't generate this constructor if it would conflict with a default exception
+ // constructor. Default exception constructors will be added automatically by our
+ // caller.
+ if (_state.IsException &&
+ _state.BaseTypeOrInterfaceOpt.InstanceConstructors.Any (
+ c => c.Parameters.Select (p => p.Type).SequenceEqual (parameterTypes))) {
+ return;
+ }
+
+ // If there's an accessible base constructor that would accept these types, then
+ // just call into that instead of generating fields.
+ if (_state.BaseTypeOrInterfaceOpt != null) {
+ if (_state.BaseTypeOrInterfaceOpt.TypeKind == TypeKind.Interface && argumentList.Count == 0) {
+ // No need to add the default constructor if our base type is going to be
+ // 'object'. We get that constructor for free.
+ return;
+ }
+
+ var accessibleInstanceConstructors = _state.BaseTypeOrInterfaceOpt.InstanceConstructors.Where (
+ IsSymbolAccessible).ToSet ();
+
+ if (accessibleInstanceConstructors.Any ()) {
+ var delegatedConstructor = _service.GetDelegatingConstructor (_state.ObjectCreationExpressionOpt, _state.BaseTypeOrInterfaceOpt, _document.SemanticModel, accessibleInstanceConstructors, _cancellationToken);
+ if (delegatedConstructor != null) {
+ // There was a best match. Call it directly.
+ AddBaseDelegatingConstructor (delegatedConstructor, members);
+ return;
+ }
+ }
+ }
+
+ // Otherwise, just generate a normal constructor that assigns any provided
+ // parameters into fields.
+ AddFieldDelegatingConstructor (argumentList, members, options);
+ }
+
+ private void AddProperties (IList<ISymbol> members)
+ {
+ foreach (var property in _state.PropertiesToGenerate) {
+ IPropertySymbol generatedProperty;
+ if (_service.TryGenerateProperty (property, _document.SemanticModel, _cancellationToken, out generatedProperty)) {
+ members.Add (generatedProperty);
+ }
+ }
+ }
+
+ private void AddBaseDelegatingConstructor (
+ IMethodSymbol methodSymbol,
+ IList<ISymbol> members)
+ {
+ // If we're generating a constructor to delegate into the no-param base constructor
+ // then we can just elide the constructor entirely.
+ if (methodSymbol.Parameters.Length == 0) {
+ return;
+ }
+
+ var factory = _document.Project.LanguageServices.GetService<SyntaxGenerator> ();
+ members.Add (factory.CreateBaseDelegatingConstructor (
+ methodSymbol, DetermineName ()));
+ }
+
+ private void AddFieldDelegatingConstructor (
+ IList<TArgumentSyntax> argumentList, IList<ISymbol> members, GenerateTypeOptionsResult options = null)
+ {
+ var factory = _document.Project.LanguageServices.GetService<SyntaxGenerator> ();
+
+ var availableTypeParameters = _service.GetAvailableTypeParameters (_state, _document.SemanticModel, _intoNamespace, _cancellationToken);
+ var parameterTypes = GetArgumentTypes (argumentList);
+ var parameterNames = _service.GenerateParameterNames (_document.SemanticModel, argumentList);
+ var parameters = new List<IParameterSymbol> ();
+
+ var parameterToExistingFieldMap = new Dictionary<string, ISymbol> ();
+ var parameterToNewFieldMap = new Dictionary<string, string> ();
+
+ for (var i = 0; i < parameterNames.Count; i++) {
+ var refKind = argumentList [i].GetRefKindOfArgument ();
+
+ var parameterName = parameterNames [i];
+ var parameterType = (ITypeSymbol)parameterTypes [i];
+ parameterType = parameterType.RemoveUnavailableTypeParameters (
+ _document.SemanticModel.Compilation, availableTypeParameters);
+
+ if (!TryFindMatchingField (parameterName, parameterType, parameterToExistingFieldMap, caseSensitive: true)) {
+ if (!TryFindMatchingField (parameterName, parameterType, parameterToExistingFieldMap, caseSensitive: false)) {
+ parameterToNewFieldMap [parameterName] = parameterName;
+ }
+ }
+
+ parameters.Add (CodeGenerationSymbolFactory.CreateParameterSymbol (
+ attributes: null,
+ refKind: refKind,
+ isParams: false,
+ type: parameterType,
+ name: parameterName));
+ }
+
+ // Empty Constructor for Struct is not allowed
+ if (!(parameters.Count == 0 && options != null && (options.TypeKind == TypeKind.Struct || options.TypeKind == TypeKind.Structure))) {
+ var symbols = factory.CreateFieldDelegatingConstructor (DetermineName (), null, parameters, parameterToExistingFieldMap, parameterToNewFieldMap, _cancellationToken);
+ foreach (var c in symbols)
+ members.Add (c);
+ }
+ }
+
+ private void AddExceptionConstructors (IList<ISymbol> members)
+ {
+ var factory = _document.Project.LanguageServices.GetService<SyntaxGenerator> ();
+ var exceptionType = _document.SemanticModel.Compilation.ExceptionType ();
+ var constructors =
+ exceptionType.InstanceConstructors
+ .Where (c => c.DeclaredAccessibility == Accessibility.Public || c.DeclaredAccessibility == Accessibility.Protected)
+ .Select (c => CodeGenerationSymbolFactory.CreateConstructorSymbol (
+ attributes: null,
+ accessibility: c.DeclaredAccessibility,
+ modifiers: default(DeclarationModifiers),
+ typeName: DetermineName (),
+ parameters: c.Parameters,
+ statements: null,
+ baseConstructorArguments: c.Parameters.Length == 0 ? null : factory.CreateArguments (c.Parameters)));
+ foreach (var c in constructors)
+ members.Add (c);
+ }
+
+ private IList<AttributeData> DetermineAttributes ()
+ {
+ if (_state.IsException) {
+ var serializableType = _document.SemanticModel.Compilation.SerializableAttributeType ();
+ if (serializableType != null) {
+ var attribute = CodeGenerationSymbolFactory.CreateAttributeData (serializableType);
+ return new[] { attribute };
+ }
+ }
+
+ return null;
+ }
+
+ private Accessibility DetermineAccessibility ()
+ {
+ return _service.GetAccessibility (_state, _document.SemanticModel, _intoNamespace, _cancellationToken);
+ }
+
+ private DeclarationModifiers DetermineModifiers ()
+ {
+ return default(DeclarationModifiers);
+ }
+
+ private INamedTypeSymbol DetermineBaseType ()
+ {
+ if (_state.BaseTypeOrInterfaceOpt == null || _state.BaseTypeOrInterfaceOpt.TypeKind == TypeKind.Interface) {
+ return null;
+ }
+
+ return RemoveUnavailableTypeParameters (_state.BaseTypeOrInterfaceOpt);
+ }
+
+ private IList<INamedTypeSymbol> DetermineInterfaces ()
+ {
+ if (_state.BaseTypeOrInterfaceOpt != null && _state.BaseTypeOrInterfaceOpt.TypeKind == TypeKind.Interface) {
+ var type = RemoveUnavailableTypeParameters (_state.BaseTypeOrInterfaceOpt);
+ if (type != null) {
+ return new[] { type };
+ }
+ }
+
+ return SpecializedCollections.EmptyList<INamedTypeSymbol> ();
+ }
+
+ private INamedTypeSymbol RemoveUnavailableTypeParameters (INamedTypeSymbol type)
+ {
+ return type.RemoveUnavailableTypeParameters (
+ _document.SemanticModel.Compilation, GetAvailableTypeParameters ()) as INamedTypeSymbol;
+ }
+
+ private string DetermineName ()
+ {
+ return GetTypeName (_state);
+ }
+
+ private IList<ITypeParameterSymbol> DetermineTypeParameters ()
+ {
+ return _service.GetTypeParameters (_state, _document.SemanticModel, _cancellationToken);
+ }
+
+ private TypeKind DetermineTypeKind ()
+ {
+ return _state.IsStruct
+ ? TypeKind.Struct
+ : _state.IsInterface
+ ? TypeKind.Interface
+ : TypeKind.Class;
+ }
+
+ protected IList<ITypeParameterSymbol> GetAvailableTypeParameters ()
+ {
+ var availableInnerTypeParameters = _service.GetTypeParameters (_state, _document.SemanticModel, _cancellationToken);
+ var availableOuterTypeParameters = !_intoNamespace && _state.TypeToGenerateInOpt != null
+ ? _state.TypeToGenerateInOpt.GetAllTypeParameters ()
+ : SpecializedCollections.EmptyEnumerable<ITypeParameterSymbol> ();
+
+ return availableOuterTypeParameters.Concat (availableInnerTypeParameters).ToList ();
+ }
+ }
+
+ internal abstract bool TryGenerateProperty (TSimpleNameSyntax propertyName, SemanticModel semanticModel, CancellationToken cancellationToken, out IPropertySymbol property);
+
+ protected class State
+ {
+ public string Name { get; private set; }
+
+ public bool NameIsVerbatim { get; private set; }
+
+ // The name node that we're on. Will be used to the name the type if it's
+ // generated.
+ public TSimpleNameSyntax SimpleName { get; private set; }
+
+ // The entire expression containing the name, not including the creation. i.e. "X.Foo"
+ // in "new X.Foo()".
+ public TExpressionSyntax NameOrMemberAccessExpression { get; private set; }
+
+ // The object creation node if we have one. i.e. if we're on the 'Foo' in "new X.Foo()".
+ public TObjectCreationExpressionSyntax ObjectCreationExpressionOpt { get; private set; }
+
+ // One of these will be non null. It's also possible for both to be non null. For
+ // example, if you have "class C { Foo f; }", then "Foo" can be generated inside C or
+ // inside the global namespace. The namespace can be null or the type can be null if the
+ // user has something like "ExistingType.NewType" or "ExistingNamespace.NewType". In
+ // that case they're being explicit about what they want to generate into.
+ public INamedTypeSymbol TypeToGenerateInOpt { get; private set; }
+
+ public string NamespaceToGenerateInOpt { get; private set; }
+
+ // If we can infer a base type or interface for this type.
+ //
+ // i.e.: "IList<int> foo = new MyList();"
+ public INamedTypeSymbol BaseTypeOrInterfaceOpt { get; private set; }
+
+ public bool IsInterface { get; private set; }
+
+ public bool IsStruct { get; private set; }
+
+ public bool IsAttribute { get; private set; }
+
+ public bool IsException { get; private set; }
+
+ public bool IsMembersWithModule { get; private set; }
+
+ public bool IsTypeGeneratedIntoNamespaceFromMemberAccess { get; private set; }
+
+ public bool IsSimpleNameGeneric { get; private set; }
+
+ public bool IsPublicAccessibilityForTypeGeneration { get; private set; }
+
+ public bool IsInterfaceOrEnumNotAllowedInTypeContext { get; private set; }
+
+ public IMethodSymbol DelegateMethodSymbol { get; private set; }
+
+ public bool IsDelegateAllowed { get; private set; }
+
+ public bool IsEnumNotAllowed { get; private set; }
+
+ public Compilation Compilation { get; }
+
+ public bool IsDelegateOnly { get; private set; }
+
+ public bool IsClassInterfaceTypes { get; private set; }
+
+ public List<TSimpleNameSyntax> PropertiesToGenerate { get; private set; }
+
+ private State (Compilation compilation)
+ {
+ this.Compilation = compilation;
+ }
+
+ public static State Generate (
+ TService service,
+ SemanticDocument document,
+ SyntaxNode node,
+ CancellationToken cancellationToken)
+ {
+ var state = new State (document.SemanticModel.Compilation);
+ if (!state.TryInitialize (service, document, node, cancellationToken)) {
+ return null;
+ }
+
+ return state;
+ }
+
+ private bool TryInitialize (
+ TService service,
+ SemanticDocument document,
+ SyntaxNode node,
+ CancellationToken cancellationToken)
+ {
+ if (!(node is TSimpleNameSyntax)) {
+ return false;
+ }
+
+ this.SimpleName = (TSimpleNameSyntax)node;
+ string name;
+ int arity;
+ this.SimpleName.GetNameAndArityOfSimpleName (out name, out arity);
+
+ this.Name = name;
+ this.NameIsVerbatim = this.SimpleName.GetFirstToken ().IsVerbatimIdentifier ();
+ if (string.IsNullOrWhiteSpace (this.Name)) {
+ return false;
+ }
+
+ // We only support simple names or dotted names. i.e. "(some + expr).Foo" is not a
+ // valid place to generate a type for Foo.
+ GenerateTypeServiceStateOptions generateTypeServiceStateOptions;
+ if (!service.TryInitializeState (document, this.SimpleName, cancellationToken, out generateTypeServiceStateOptions)) {
+ return false;
+ }
+
+ this.NameOrMemberAccessExpression = generateTypeServiceStateOptions.NameOrMemberAccessExpression;
+ this.ObjectCreationExpressionOpt = generateTypeServiceStateOptions.ObjectCreationExpressionOpt;
+
+ var semanticModel = document.SemanticModel;
+ var info = semanticModel.GetSymbolInfo (this.SimpleName, cancellationToken);
+ if (info.Symbol != null) {
+ // This bound, so no need to generate anything.
+ return false;
+ }
+
+ if (!semanticModel.IsTypeContext (this.NameOrMemberAccessExpression.SpanStart, cancellationToken) &&
+ !semanticModel.IsExpressionContext (this.NameOrMemberAccessExpression.SpanStart, cancellationToken) &&
+ !semanticModel.IsStatementContext (this.NameOrMemberAccessExpression.SpanStart, cancellationToken) &&
+ !semanticModel.IsNameOfContext (this.NameOrMemberAccessExpression.SpanStart, cancellationToken) &&
+ !semanticModel.IsNamespaceContext (this.NameOrMemberAccessExpression.SpanStart, cancellationToken)) {
+ return false;
+ }
+
+ // If this isn't something that can be created, then don't bother offering to create
+ // it.
+ if (info.CandidateReason == CandidateReason.NotCreatable) {
+ return false;
+ }
+
+ if (info.CandidateReason == CandidateReason.Inaccessible ||
+ info.CandidateReason == CandidateReason.NotReferencable ||
+ info.CandidateReason == CandidateReason.OverloadResolutionFailure) {
+ // We bound to something inaccessible, or overload resolution on a
+ // constructor call failed. Don't want to offer GenerateType here.
+ return false;
+ }
+
+ if (this.ObjectCreationExpressionOpt != null) {
+ // If we're new'ing up something illegal, then don't offer generate type.
+ var typeInfo = semanticModel.GetTypeInfo (this.ObjectCreationExpressionOpt, cancellationToken);
+ if (typeInfo.Type.IsModuleType ()) {
+ return false;
+ }
+ }
+
+ DetermineNamespaceOrTypeToGenerateIn (service, document, cancellationToken);
+
+ // Now, try to infer a possible base type for this new class/interface.
+ this.InferBaseType (service, document, cancellationToken);
+ this.IsInterface = GenerateInterface (service, cancellationToken);
+ this.IsStruct = GenerateStruct (service, semanticModel, cancellationToken);
+ this.IsAttribute = this.BaseTypeOrInterfaceOpt != null && this.BaseTypeOrInterfaceOpt.Equals (semanticModel.Compilation.AttributeType ());
+ this.IsException = this.BaseTypeOrInterfaceOpt != null && this.BaseTypeOrInterfaceOpt.Equals (semanticModel.Compilation.ExceptionType ());
+ this.IsMembersWithModule = generateTypeServiceStateOptions.IsMembersWithModule;
+ this.IsTypeGeneratedIntoNamespaceFromMemberAccess = generateTypeServiceStateOptions.IsTypeGeneratedIntoNamespaceFromMemberAccess;
+ this.IsInterfaceOrEnumNotAllowedInTypeContext = generateTypeServiceStateOptions.IsInterfaceOrEnumNotAllowedInTypeContext;
+ this.IsDelegateAllowed = generateTypeServiceStateOptions.IsDelegateAllowed;
+ this.IsDelegateOnly = generateTypeServiceStateOptions.IsDelegateOnly;
+ this.IsEnumNotAllowed = generateTypeServiceStateOptions.IsEnumNotAllowed;
+ this.DelegateMethodSymbol = generateTypeServiceStateOptions.DelegateCreationMethodSymbol;
+ this.IsClassInterfaceTypes = generateTypeServiceStateOptions.IsClassInterfaceTypes;
+ this.IsSimpleNameGeneric = service.IsGenericName (this.SimpleName);
+ this.PropertiesToGenerate = generateTypeServiceStateOptions.PropertiesToGenerate;
+
+ if (this.IsAttribute && this.TypeToGenerateInOpt.GetAllTypeParameters ().Any ()) {
+ this.TypeToGenerateInOpt = null;
+ }
+
+ return this.TypeToGenerateInOpt != null || this.NamespaceToGenerateInOpt != null;
+ }
+
+ private void InferBaseType (
+ TService service,
+ SemanticDocument document,
+ CancellationToken cancellationToken)
+ {
+ // See if we can find a possible base type for the type being generated.
+ // NOTE(cyrusn): I currently limit this to when we have an object creation node.
+ // That's because that's when we would have an expression that could be conerted to
+ // somethign else. i.e. if the user writes "IList<int> list = new Foo()" then we can
+ // infer a base interface for 'Foo'. However, if they write "IList<int> list = Foo"
+ // then we don't really want to infer a base type for 'Foo'.
+
+ // However, there are a few other cases were we can infer a base type.
+ if (service.IsInCatchDeclaration (this.NameOrMemberAccessExpression)) {
+ this.BaseTypeOrInterfaceOpt = document.SemanticModel.Compilation.ExceptionType ();
+ } else if (NameOrMemberAccessExpression.IsAttributeName ()) {
+ this.BaseTypeOrInterfaceOpt = document.SemanticModel.Compilation.AttributeType ();
+ } else if (
+ service.IsArrayElementType (this.NameOrMemberAccessExpression) ||
+ service.IsInVariableTypeContext (this.NameOrMemberAccessExpression) ||
+ this.ObjectCreationExpressionOpt != null) {
+ var expr = this.ObjectCreationExpressionOpt ?? this.NameOrMemberAccessExpression;
+ var baseType = TypeGuessing.typeInferenceService.InferType (document.SemanticModel, expr, objectAsDefault: true, cancellationToken: cancellationToken) as INamedTypeSymbol;
+ SetBaseType (baseType);
+ }
+ }
+
+ private void SetBaseType (INamedTypeSymbol baseType)
+ {
+ if (baseType == null) {
+ return;
+ }
+
+ // A base type need to be non class or interface type. Also, being 'object' is
+ // redundant as the base type.
+ if (baseType.IsSealed || baseType.IsStatic || baseType.SpecialType == SpecialType.System_Object) {
+ return;
+ }
+
+ if (baseType.TypeKind != TypeKind.Class && baseType.TypeKind != TypeKind.Interface) {
+ return;
+ }
+
+ this.BaseTypeOrInterfaceOpt = baseType;
+ }
+
+ private bool GenerateStruct (TService service, SemanticModel semanticModel, CancellationToken cancellationToken)
+ {
+ return service.IsInValueTypeConstraintContext (semanticModel, this.NameOrMemberAccessExpression, cancellationToken);
+ }
+
+ private bool GenerateInterface (
+ TService service,
+ CancellationToken cancellationToken)
+ {
+ if (!this.IsAttribute &&
+ !this.IsException &&
+ this.Name.LooksLikeInterfaceName () &&
+ this.ObjectCreationExpressionOpt == null &&
+ (this.BaseTypeOrInterfaceOpt == null || this.BaseTypeOrInterfaceOpt.TypeKind == TypeKind.Interface)) {
+ return true;
+ }
+
+ return service.IsInInterfaceList (this.NameOrMemberAccessExpression);
+ }
+
+ private void DetermineNamespaceOrTypeToGenerateIn (
+ TService service,
+ SemanticDocument document,
+ CancellationToken cancellationToken)
+ {
+ DetermineNamespaceOrTypeToGenerateInWorker (service, document.SemanticModel, cancellationToken);
+
+ // Can only generate into a type if it's a class and it's from source.
+ if (this.TypeToGenerateInOpt != null) {
+ if (this.TypeToGenerateInOpt.TypeKind != TypeKind.Class &&
+ this.TypeToGenerateInOpt.TypeKind != TypeKind.Module) {
+ this.TypeToGenerateInOpt = null;
+ } else {
+ var symbol = SymbolFinder.FindSourceDefinitionAsync (this.TypeToGenerateInOpt, document.Project.Solution, cancellationToken).WaitAndGetResult (cancellationToken);
+ if (symbol == null ||
+ !symbol.IsKind (SymbolKind.NamedType) ||
+ !symbol.Locations.Any (loc => loc.IsInSource)) {
+ this.TypeToGenerateInOpt = null;
+ return;
+ }
+
+ var sourceTreeToBeGeneratedIn = symbol.Locations.First (loc => loc.IsInSource).SourceTree;
+ var documentToBeGeneratedIn = document.Project.Solution.GetDocument (sourceTreeToBeGeneratedIn);
+
+ if (documentToBeGeneratedIn == null) {
+ this.TypeToGenerateInOpt = null;
+ return;
+ }
+
+ // If the 2 documents are in different project then we must have Public Accessibility.
+ // If we are generating in a website project, we also want to type to be public so the
+ // designer files can access the type.
+ if (documentToBeGeneratedIn.Project != document.Project ||
+ service.GeneratedTypesMustBePublic (documentToBeGeneratedIn.Project)) {
+ this.IsPublicAccessibilityForTypeGeneration = true;
+ }
+
+ this.TypeToGenerateInOpt = (INamedTypeSymbol)symbol;
+ }
+ }
+
+ if (this.TypeToGenerateInOpt != null) {
+ if (!CodeGenerator.CanAdd (document.Project.Solution, this.TypeToGenerateInOpt, cancellationToken)) {
+ this.TypeToGenerateInOpt = null;
+ }
+ }
+ }
+
+ private bool DetermineNamespaceOrTypeToGenerateInWorker (
+ TService service,
+ SemanticModel semanticModel,
+ CancellationToken cancellationToken)
+ {
+ // If we're on the right of a dot, see if we can figure out what's on the left. If
+ // it doesn't bind to a type or a namespace, then we can't proceed.
+ if (this.SimpleName != this.NameOrMemberAccessExpression) {
+ return DetermineNamespaceOrTypeToGenerateIn (
+ service, semanticModel,
+ service.GetLeftSideOfDot (this.SimpleName), cancellationToken);
+ } else {
+ // The name is standing alone. We can either generate the type into our
+ // containing type, or into our containing namespace.
+ //
+ // TODO(cyrusn): We need to make this logic work if the type is in the
+ // base/interface list of a type.
+ var format = SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle (SymbolDisplayGlobalNamespaceStyle.Omitted);
+ this.TypeToGenerateInOpt = service.DetermineTypeToGenerateIn (semanticModel, this.SimpleName, cancellationToken);
+ if (this.TypeToGenerateInOpt != null) {
+ this.NamespaceToGenerateInOpt = this.TypeToGenerateInOpt.ContainingNamespace.ToDisplayString (format);
+ } else {
+ var namespaceSymbol = semanticModel.GetEnclosingNamespace (this.SimpleName.SpanStart, cancellationToken);
+ if (namespaceSymbol != null) {
+ this.NamespaceToGenerateInOpt = namespaceSymbol.ToDisplayString (format);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private bool DetermineNamespaceOrTypeToGenerateIn (
+ TService service,
+ SemanticModel semanticModel,
+ TExpressionSyntax leftSide,
+ CancellationToken cancellationToken)
+ {
+ var leftSideInfo = semanticModel.GetSymbolInfo (leftSide, cancellationToken);
+
+ if (leftSideInfo.Symbol != null) {
+ var symbol = leftSideInfo.Symbol;
+
+ if (symbol is INamespaceSymbol) {
+ this.NamespaceToGenerateInOpt = symbol.ToDisplayString (ICSharpCode.NRefactory6.CSharp.Completion.DelegateCreationContextHandler.NameFormat);
+ return true;
+ } else if (symbol is INamedTypeSymbol) {
+ // TODO: Code coverage
+ this.TypeToGenerateInOpt = (INamedTypeSymbol)symbol.OriginalDefinition;
+ return true;
+ }
+
+ // We bound to something other than a namespace or named type. Can't generate a
+ // type inside this.
+ return false;
+ } else {
+ // If it's a dotted name, then perhaps it's a namespace. i.e. the user wrote
+ // "new Foo.Bar.Baz()". In this case we want to generate a namespace for
+ // "Foo.Bar".
+ IList<string> nameParts;
+ if (service.TryGetNameParts (leftSide, out nameParts)) {
+ this.NamespaceToGenerateInOpt = string.Join (".", nameParts);
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
+ protected class GenerateTypeServiceStateOptions
+ {
+ public TExpressionSyntax NameOrMemberAccessExpression { get; set; }
+
+ public TObjectCreationExpressionSyntax ObjectCreationExpressionOpt { get; set; }
+
+ public IMethodSymbol DelegateCreationMethodSymbol { get; set; }
+
+ public List<TSimpleNameSyntax> PropertiesToGenerate { get; }
+
+ public bool IsMembersWithModule { get; set; }
+
+ public bool IsTypeGeneratedIntoNamespaceFromMemberAccess { get; set; }
+
+ public bool IsInterfaceOrEnumNotAllowedInTypeContext { get; set; }
+
+ public bool IsDelegateAllowed { get; set; }
+
+ public bool IsEnumNotAllowed { get; set; }
+
+ public bool IsDelegateOnly { get; internal set; }
+
+ public bool IsClassInterfaceTypes { get; internal set; }
+
+ public GenerateTypeServiceStateOptions ()
+ {
+ NameOrMemberAccessExpression = null;
+ ObjectCreationExpressionOpt = null;
+ DelegateCreationMethodSymbol = null;
+ IsMembersWithModule = false;
+ PropertiesToGenerate = new List<TSimpleNameSyntax> ();
+ IsTypeGeneratedIntoNamespaceFromMemberAccess = false;
+ IsInterfaceOrEnumNotAllowedInTypeContext = false;
+ IsDelegateAllowed = true;
+ IsEnumNotAllowed = false;
+ IsDelegateOnly = false;
+ }
+ }
+
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateType/CSharpGenerateTypeService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateType/CSharpGenerateTypeService.cs
new file mode 100644
index 0000000000..72abb6eb2a
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateType/CSharpGenerateTypeService.cs
@@ -0,0 +1,940 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Composition;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.CodeGeneration;
+using Microsoft.CodeAnalysis.Editing;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.Formatting;
+using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Text;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using ICSharpCode.NRefactory6.CSharp.GenerateMember.GenerateConstructor;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateType
+{
+ public class CSharpGenerateTypeService :
+ AbstractGenerateTypeService<CSharpGenerateTypeService, SimpleNameSyntax, ObjectCreationExpressionSyntax, ExpressionSyntax, TypeDeclarationSyntax, ArgumentSyntax>
+ {
+ private static readonly SyntaxAnnotation s_annotation = new SyntaxAnnotation();
+
+ protected override string DefaultFileExtension
+ {
+ get
+ {
+ return ".cs";
+ }
+ }
+
+ protected override ExpressionSyntax GetLeftSideOfDot(SimpleNameSyntax simpleName)
+ {
+ return simpleName.GetLeftSideOfDot();
+ }
+
+ protected override bool IsInCatchDeclaration(ExpressionSyntax expression)
+ {
+ return expression.IsParentKind(SyntaxKind.CatchDeclaration);
+ }
+
+ protected override bool IsArrayElementType(ExpressionSyntax expression)
+ {
+ return expression.IsParentKind(SyntaxKind.ArrayType) &&
+ expression.Parent.IsParentKind(SyntaxKind.ArrayCreationExpression);
+ }
+
+ protected override bool IsInValueTypeConstraintContext(
+ SemanticModel semanticModel,
+ ExpressionSyntax expression,
+ CancellationToken cancellationToken)
+ {
+ if (expression is TypeSyntax && expression.IsParentKind(SyntaxKind.TypeArgumentList))
+ {
+ var typeArgumentList = (TypeArgumentListSyntax)expression.Parent;
+ var symbolInfo = semanticModel.GetSymbolInfo(typeArgumentList.Parent, cancellationToken);
+ var symbol = symbolInfo.GetAnySymbol();
+ if (symbol.IsConstructor())
+ {
+ symbol = symbol.ContainingType;
+ }
+
+ var parameterIndex = typeArgumentList.Arguments.IndexOf((TypeSyntax)expression);
+ var type = symbol as INamedTypeSymbol;
+ if (type != null)
+ {
+ type = type.OriginalDefinition;
+ var typeParameter = parameterIndex < type.TypeParameters.Length ? type.TypeParameters[parameterIndex] : null;
+ return typeParameter != null && typeParameter.HasValueTypeConstraint;
+ }
+
+ var method = symbol as IMethodSymbol;
+ if (method != null)
+ {
+ method = method.OriginalDefinition;
+ var typeParameter = parameterIndex < method.TypeParameters.Length ? method.TypeParameters[parameterIndex] : null;
+ return typeParameter != null && typeParameter.HasValueTypeConstraint;
+ }
+ }
+
+ return false;
+ }
+
+ protected override bool IsInInterfaceList(ExpressionSyntax expression)
+ {
+ if (expression is TypeSyntax &&
+ expression.Parent is BaseTypeSyntax &&
+ expression.Parent.IsParentKind(SyntaxKind.BaseList) &&
+ ((BaseTypeSyntax)expression.Parent).Type == expression)
+ {
+ var baseList = (BaseListSyntax)expression.Parent.Parent;
+
+ // If it's after the first item, then it's definitely an interface.
+ if (baseList.Types[0] != expression.Parent)
+ {
+ return true;
+ }
+
+ // If it's in the base list of an interface or struct, then it's definitely an
+ // interface.
+ return
+ baseList.IsParentKind(SyntaxKind.InterfaceDeclaration) ||
+ baseList.IsParentKind(SyntaxKind.StructDeclaration);
+ }
+
+ if (expression is TypeSyntax &&
+ expression.IsParentKind(SyntaxKind.TypeConstraint) &&
+ expression.Parent.IsParentKind(SyntaxKind.TypeParameterConstraintClause))
+ {
+ var typeConstraint = (TypeConstraintSyntax)expression.Parent;
+ var constraintClause = (TypeParameterConstraintClauseSyntax)typeConstraint.Parent;
+ var index = constraintClause.Constraints.IndexOf(typeConstraint);
+
+ // If it's after the first item, then it's definitely an interface.
+ return index > 0;
+ }
+
+ return false;
+ }
+
+ protected override bool TryGetNameParts(ExpressionSyntax expression, out IList<string> nameParts)
+ {
+ nameParts = new List<string>();
+ return expression.TryGetNameParts(out nameParts);
+ }
+
+ protected override bool TryInitializeState(
+ SemanticDocument document,
+ SimpleNameSyntax simpleName,
+ CancellationToken cancellationToken,
+ out GenerateTypeServiceStateOptions generateTypeServiceStateOptions)
+ {
+ generateTypeServiceStateOptions = new GenerateTypeServiceStateOptions();
+
+ if (simpleName.IsVar)
+ {
+ return false;
+ }
+
+ if (SyntaxFacts.IsAliasQualifier(simpleName))
+ {
+ return false;
+ }
+
+ // Never offer if we're in a using directive, unless its a static using. The feeling here is that it's highly
+ // unlikely that this would be a location where a user would be wanting to generate
+ // something. They're really just trying to reference something that exists but
+ // isn't available for some reason (i.e. a missing reference).
+ var usingDirectiveSyntax = simpleName.GetAncestorOrThis<UsingDirectiveSyntax>();
+ if (usingDirectiveSyntax != null && usingDirectiveSyntax.StaticKeyword.Kind() != SyntaxKind.StaticKeyword)
+ {
+ return false;
+ }
+
+ ExpressionSyntax nameOrMemberAccessExpression = null;
+ if (simpleName.IsRightSideOfDot())
+ {
+ // This simplename comes from the cref
+ if (simpleName.IsParentKind(SyntaxKind.NameMemberCref))
+ {
+ return false;
+ }
+
+ nameOrMemberAccessExpression = generateTypeServiceStateOptions.NameOrMemberAccessExpression = (ExpressionSyntax)simpleName.Parent;
+
+ // If we're on the right side of a dot, then the left side better be a name (and
+ // not an arbitrary expression).
+ var leftSideExpression = simpleName.GetLeftSideOfDot();
+ if (!leftSideExpression.IsKind(
+ SyntaxKind.QualifiedName,
+ SyntaxKind.IdentifierName,
+ SyntaxKind.AliasQualifiedName,
+ SyntaxKind.GenericName,
+ SyntaxKind.SimpleMemberAccessExpression))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ nameOrMemberAccessExpression = generateTypeServiceStateOptions.NameOrMemberAccessExpression = simpleName;
+ }
+
+ // BUG(5712): Don't offer generate type in an enum's base list.
+ if (nameOrMemberAccessExpression.Parent is BaseTypeSyntax &&
+ nameOrMemberAccessExpression.Parent.IsParentKind(SyntaxKind.BaseList) &&
+ ((BaseTypeSyntax)nameOrMemberAccessExpression.Parent).Type == nameOrMemberAccessExpression &&
+ nameOrMemberAccessExpression.Parent.Parent.IsParentKind(SyntaxKind.EnumDeclaration))
+ {
+ return false;
+ }
+
+ // If we can guarantee it's a type only context, great. Otherwise, we may not want to
+ // provide this here.
+ var semanticModel = document.SemanticModel;
+ if (!SyntaxFacts.IsInNamespaceOrTypeContext(nameOrMemberAccessExpression))
+ {
+ // Don't offer Generate Type in an expression context *unless* we're on the left
+ // side of a dot. In that case the user might be making a type that they're
+ // accessing a static off of.
+ var syntaxTree = semanticModel.SyntaxTree;
+ var start = nameOrMemberAccessExpression.SpanStart;
+ var tokenOnLeftOfStart = syntaxTree.FindTokenOnLeftOfPosition(start, cancellationToken);
+ var isExpressionContext = syntaxTree.IsExpressionContext(start, tokenOnLeftOfStart, attributes: true, cancellationToken: cancellationToken, semanticModelOpt: semanticModel);
+ var isStatementContext = syntaxTree.IsStatementContext(start, tokenOnLeftOfStart, cancellationToken);
+ var isExpressionOrStatementContext = isExpressionContext || isStatementContext;
+
+ // Delegate Type Creation is not allowed in Non Type Namespace Context
+ generateTypeServiceStateOptions.IsDelegateAllowed = false;
+
+ if (!isExpressionOrStatementContext)
+ {
+ return false;
+ }
+
+ if (!simpleName.IsLeftSideOfDot() && !simpleName.IsInsideNameOf())
+ {
+ if (nameOrMemberAccessExpression == null || !nameOrMemberAccessExpression.IsKind(SyntaxKind.SimpleMemberAccessExpression) || !simpleName.IsRightSideOfDot())
+ {
+ return false;
+ }
+
+ var leftSymbol = semanticModel.GetSymbolInfo(((MemberAccessExpressionSyntax)nameOrMemberAccessExpression).Expression, cancellationToken).Symbol;
+ var token = simpleName.GetLastToken().GetNextToken();
+
+ // We let only the Namespace to be left of the Dot
+ if (leftSymbol == null ||
+ !leftSymbol.IsKind(SymbolKind.Namespace) ||
+ !token.IsKind(SyntaxKind.DotToken))
+ {
+ return false;
+ }
+ else
+ {
+ generateTypeServiceStateOptions.IsMembersWithModule = true;
+ generateTypeServiceStateOptions.IsTypeGeneratedIntoNamespaceFromMemberAccess = true;
+ }
+ }
+
+ // Global Namespace
+ if (!generateTypeServiceStateOptions.IsTypeGeneratedIntoNamespaceFromMemberAccess &&
+ !SyntaxFacts.IsInNamespaceOrTypeContext(simpleName))
+ {
+ var token = simpleName.GetLastToken().GetNextToken();
+ if (token.IsKind(SyntaxKind.DotToken) &&
+ simpleName.Parent == token.Parent)
+ {
+ generateTypeServiceStateOptions.IsMembersWithModule = true;
+ generateTypeServiceStateOptions.IsTypeGeneratedIntoNamespaceFromMemberAccess = true;
+ }
+ }
+ }
+
+ var fieldDeclaration = simpleName.GetAncestor<FieldDeclarationSyntax>();
+ if (fieldDeclaration != null &&
+ fieldDeclaration.Parent is CompilationUnitSyntax &&
+ document.Document.SourceCodeKind == SourceCodeKind.Regular)
+ {
+ return false;
+ }
+
+ // Check to see if Module could be an option in the Type Generation in Cross Language Generation
+ var nextToken = simpleName.GetLastToken().GetNextToken();
+ if (simpleName.IsLeftSideOfDot() ||
+ nextToken.IsKind(SyntaxKind.DotToken))
+ {
+ if (simpleName.IsRightSideOfDot())
+ {
+ var parent = simpleName.Parent as QualifiedNameSyntax;
+ if (parent != null)
+ {
+ var leftSymbol = semanticModel.GetSymbolInfo(parent.Left, cancellationToken).Symbol;
+
+ if (leftSymbol != null && leftSymbol.IsKind(SymbolKind.Namespace))
+ {
+ generateTypeServiceStateOptions.IsMembersWithModule = true;
+ }
+ }
+ }
+ }
+
+ if (SyntaxFacts.IsInNamespaceOrTypeContext(nameOrMemberAccessExpression))
+ {
+ if (nextToken.IsKind(SyntaxKind.DotToken))
+ {
+ // In Namespace or Type Context we cannot have Interface, Enum, Delegate as part of the Left Expression of a QualifiedName
+ generateTypeServiceStateOptions.IsDelegateAllowed = false;
+ generateTypeServiceStateOptions.IsInterfaceOrEnumNotAllowedInTypeContext = true;
+ generateTypeServiceStateOptions.IsMembersWithModule = true;
+ }
+
+ // case: class Foo<T> where T: MyType
+ if (nameOrMemberAccessExpression.GetAncestors<TypeConstraintSyntax>().Any())
+ {
+ generateTypeServiceStateOptions.IsClassInterfaceTypes = true;
+ return true;
+ }
+
+ // Events
+ if (nameOrMemberAccessExpression.GetAncestors<EventFieldDeclarationSyntax>().Any() ||
+ nameOrMemberAccessExpression.GetAncestors<EventDeclarationSyntax>().Any())
+ {
+ // Case : event foo name11
+ // Only Delegate
+ if (simpleName.Parent != null && !(simpleName.Parent is QualifiedNameSyntax))
+ {
+ generateTypeServiceStateOptions.IsDelegateOnly = true;
+ return true;
+ }
+
+ // Case : event SomeSymbol.foo name11
+ if (nameOrMemberAccessExpression is QualifiedNameSyntax)
+ {
+ // Only Namespace, Class, Struct and Module are allowed to contain Delegate
+ // Case : event Something.Mytype.<Delegate> Identifier
+ if (nextToken.IsKind(SyntaxKind.DotToken))
+ {
+ if (nameOrMemberAccessExpression.Parent != null && nameOrMemberAccessExpression.Parent is QualifiedNameSyntax)
+ {
+ return true;
+ }
+ else
+ {
+ //Contract.Fail("Cannot reach this point");
+ }
+ }
+ else
+ {
+ // Case : event Something.<Delegate> Identifier
+ generateTypeServiceStateOptions.IsDelegateOnly = true;
+ return true;
+ }
+ }
+ }
+ }
+ else
+ {
+ // MemberAccessExpression
+ if ((nameOrMemberAccessExpression.IsKind(SyntaxKind.SimpleMemberAccessExpression) || (nameOrMemberAccessExpression.Parent != null && nameOrMemberAccessExpression.IsParentKind(SyntaxKind.SimpleMemberAccessExpression)))
+ && nameOrMemberAccessExpression.IsLeftSideOfDot())
+ {
+ // Check to see if the expression is part of Invocation Expression
+ ExpressionSyntax outerMostMemberAccessExpression = null;
+ if (nameOrMemberAccessExpression.IsKind(SyntaxKind.SimpleMemberAccessExpression))
+ {
+ outerMostMemberAccessExpression = nameOrMemberAccessExpression;
+ }
+ else
+ {
+ Debug.Assert(nameOrMemberAccessExpression.IsParentKind(SyntaxKind.SimpleMemberAccessExpression));
+ outerMostMemberAccessExpression = (ExpressionSyntax)nameOrMemberAccessExpression.Parent;
+ }
+
+ outerMostMemberAccessExpression = outerMostMemberAccessExpression.GetAncestorsOrThis<ExpressionSyntax>().SkipWhile((n) => n != null && n.IsKind(SyntaxKind.SimpleMemberAccessExpression)).FirstOrDefault();
+ if (outerMostMemberAccessExpression != null && outerMostMemberAccessExpression is InvocationExpressionSyntax)
+ {
+ generateTypeServiceStateOptions.IsEnumNotAllowed = true;
+ }
+ }
+ }
+
+ // Cases:
+ // // 1 - Function Address
+ // var s2 = new MyD2(foo);
+
+ // // 2 - Delegate
+ // MyD1 d = null;
+ // var s1 = new MyD2(d);
+
+ // // 3 - Action
+ // Action action1 = null;
+ // var s3 = new MyD2(action1);
+
+ // // 4 - Func
+ // Func<int> lambda = () => { return 0; };
+ // var s4 = new MyD3(lambda);
+
+ if (nameOrMemberAccessExpression.Parent is ObjectCreationExpressionSyntax)
+ {
+ var objectCreationExpressionOpt = generateTypeServiceStateOptions.ObjectCreationExpressionOpt = (ObjectCreationExpressionSyntax)nameOrMemberAccessExpression.Parent;
+
+ // Enum and Interface not Allowed in Object Creation Expression
+ generateTypeServiceStateOptions.IsInterfaceOrEnumNotAllowedInTypeContext = true;
+
+ if (objectCreationExpressionOpt.ArgumentList != null)
+ {
+ if (objectCreationExpressionOpt.ArgumentList.CloseParenToken.IsMissing)
+ {
+ return false;
+ }
+
+ // Get the Method symbol for the Delegate to be created
+ if (generateTypeServiceStateOptions.IsDelegateAllowed &&
+ objectCreationExpressionOpt.ArgumentList.Arguments.Count == 1)
+ {
+ generateTypeServiceStateOptions.DelegateCreationMethodSymbol = GetMethodSymbolIfPresent(semanticModel, objectCreationExpressionOpt.ArgumentList.Arguments[0].Expression, cancellationToken);
+ }
+ else
+ {
+ generateTypeServiceStateOptions.IsDelegateAllowed = false;
+ }
+ }
+
+ if (objectCreationExpressionOpt.Initializer != null)
+ {
+ foreach (var expression in objectCreationExpressionOpt.Initializer.Expressions)
+ {
+ var simpleAssignmentExpression = expression as AssignmentExpressionSyntax;
+ if (simpleAssignmentExpression == null)
+ {
+ continue;
+ }
+
+ var name = simpleAssignmentExpression.Left as SimpleNameSyntax;
+ if (name == null)
+ {
+ continue;
+ }
+
+ generateTypeServiceStateOptions.PropertiesToGenerate.Add(name);
+ }
+ }
+ }
+
+ if (generateTypeServiceStateOptions.IsDelegateAllowed)
+ {
+ // MyD1 z1 = foo;
+ if (nameOrMemberAccessExpression.Parent.IsKind(SyntaxKind.VariableDeclaration))
+ {
+ var variableDeclaration = (VariableDeclarationSyntax)nameOrMemberAccessExpression.Parent;
+ if (variableDeclaration.Variables.Count != 0)
+ {
+ var firstVarDeclWithInitializer = variableDeclaration.Variables.FirstOrDefault(var => var.Initializer != null && var.Initializer.Value != null);
+ if (firstVarDeclWithInitializer != null && firstVarDeclWithInitializer.Initializer != null && firstVarDeclWithInitializer.Initializer.Value != null)
+ {
+ generateTypeServiceStateOptions.DelegateCreationMethodSymbol = GetMethodSymbolIfPresent(semanticModel, firstVarDeclWithInitializer.Initializer.Value, cancellationToken);
+ }
+ }
+ }
+
+ // var w1 = (MyD1)foo;
+ if (nameOrMemberAccessExpression.Parent.IsKind(SyntaxKind.CastExpression))
+ {
+ var castExpression = (CastExpressionSyntax)nameOrMemberAccessExpression.Parent;
+ if (castExpression.Expression != null)
+ {
+ generateTypeServiceStateOptions.DelegateCreationMethodSymbol = GetMethodSymbolIfPresent(semanticModel, castExpression.Expression, cancellationToken);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private IMethodSymbol GetMethodSymbolIfPresent(SemanticModel semanticModel, ExpressionSyntax expression, CancellationToken cancellationToken)
+ {
+ if (expression == null)
+ {
+ return null;
+ }
+
+ var memberGroup = semanticModel.GetMemberGroup(expression, cancellationToken);
+ if (memberGroup.Count() != 0)
+ {
+ return memberGroup.ElementAt(0).IsKind(SymbolKind.Method) ? (IMethodSymbol)memberGroup.ElementAt(0) : null;
+ }
+
+ var expressionType = semanticModel.GetTypeInfo(expression, cancellationToken).Type;
+ if (expressionType.IsDelegateType())
+ {
+ return ((INamedTypeSymbol)expressionType).DelegateInvokeMethod;
+ }
+
+ var expressionSymbol = semanticModel.GetSymbolInfo(expression, cancellationToken).Symbol;
+ if (expressionSymbol.IsKind(SymbolKind.Method))
+ {
+ return (IMethodSymbol)expressionSymbol;
+ }
+
+ return null;
+ }
+
+ private Accessibility DetermineAccessibilityConstraint(
+ State state,
+ SemanticModel semanticModel,
+ CancellationToken cancellationToken)
+ {
+ return semanticModel.DetermineAccessibilityConstraint(
+ state.NameOrMemberAccessExpression as TypeSyntax, cancellationToken);
+ }
+
+ protected override IList<ITypeParameterSymbol> GetTypeParameters(
+ State state,
+ SemanticModel semanticModel,
+ CancellationToken cancellationToken)
+ {
+ if (state.SimpleName is GenericNameSyntax)
+ {
+ var genericName = (GenericNameSyntax)state.SimpleName;
+ var typeArguments = state.SimpleName.Arity == genericName.TypeArgumentList.Arguments.Count
+ ? genericName.TypeArgumentList.Arguments.OfType<SyntaxNode>().ToList()
+ : Enumerable.Repeat<SyntaxNode>(null, state.SimpleName.Arity);
+ return this.GetTypeParameters(state, semanticModel, typeArguments, cancellationToken);
+ }
+
+ return SpecializedCollections.EmptyList<ITypeParameterSymbol>();
+ }
+
+ protected override bool TryGetArgumentList(ObjectCreationExpressionSyntax objectCreationExpression, out IList<ArgumentSyntax> argumentList)
+ {
+ if (objectCreationExpression != null && objectCreationExpression.ArgumentList != null)
+ {
+ argumentList = objectCreationExpression.ArgumentList.Arguments.ToList();
+ return true;
+ }
+
+ argumentList = null;
+ return false;
+ }
+
+ protected override IList<string> GenerateParameterNames(
+ SemanticModel semanticModel, IList<ArgumentSyntax> arguments)
+ {
+ return semanticModel.GenerateParameterNames(arguments);
+ }
+
+ public override string GetRootNamespace(CompilationOptions options)
+ {
+ return string.Empty;
+ }
+
+ protected override bool IsInVariableTypeContext(ExpressionSyntax expression)
+ {
+ return false;
+ }
+
+ protected override INamedTypeSymbol DetermineTypeToGenerateIn(SemanticModel semanticModel, SimpleNameSyntax simpleName, CancellationToken cancellationToken)
+ {
+ return semanticModel.GetEnclosingNamedType(simpleName.SpanStart, cancellationToken);
+ }
+
+ protected override Accessibility GetAccessibility(State state, SemanticModel semanticModel, bool intoNamespace, CancellationToken cancellationToken)
+ {
+ var accessibility = DetermineDefaultAccessibility(state, semanticModel, intoNamespace, cancellationToken);
+ if (!state.IsTypeGeneratedIntoNamespaceFromMemberAccess)
+ {
+ var accessibilityConstraint = DetermineAccessibilityConstraint(state, semanticModel, cancellationToken);
+
+ if (accessibilityConstraint == Accessibility.Public ||
+ accessibilityConstraint == Accessibility.Internal)
+ {
+ accessibility = accessibilityConstraint;
+ }
+ }
+
+ return accessibility;
+ }
+
+ protected override ITypeSymbol DetermineArgumentType(SemanticModel semanticModel, ArgumentSyntax argument, CancellationToken cancellationToken)
+ {
+ return argument.DetermineParameterType(semanticModel, cancellationToken);
+ }
+
+ protected override bool IsConversionImplicit(Compilation compilation, ITypeSymbol sourceType, ITypeSymbol targetType)
+ {
+ return compilation.ClassifyConversion(sourceType, targetType).IsImplicit;
+ }
+
+ public override async Task<Tuple<INamespaceSymbol, INamespaceOrTypeSymbol, Location>> GetOrGenerateEnclosingNamespaceSymbol(INamedTypeSymbol namedTypeSymbol, string[] containers, Document selectedDocument, SyntaxNode selectedDocumentRoot, CancellationToken cancellationToken)
+ {
+ var compilationUnit = (CompilationUnitSyntax)selectedDocumentRoot;
+ var semanticModel = await selectedDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
+ if (containers.Length != 0)
+ {
+ // Search the NS declaration in the root
+ var containerList = new List<string>(containers);
+ var enclosingNamespace = GetDeclaringNamespace(containerList, 0, compilationUnit);
+ if (enclosingNamespace != null)
+ {
+ var enclosingNamespaceSymbol = semanticModel.GetSymbolInfo(enclosingNamespace.Name, cancellationToken);
+ if (enclosingNamespaceSymbol.Symbol != null)
+ {
+ return Tuple.Create((INamespaceSymbol)enclosingNamespaceSymbol.Symbol,
+ (INamespaceOrTypeSymbol)namedTypeSymbol,
+ enclosingNamespace.CloseBraceToken.GetLocation());
+ }
+ }
+ }
+
+ var globalNamespace = semanticModel.GetEnclosingNamespace(0, cancellationToken);
+ var rootNamespaceOrType = namedTypeSymbol.GenerateRootNamespaceOrType(containers);
+ var lastMember = compilationUnit.Members.LastOrDefault();
+ Location afterThisLocation = null;
+ if (lastMember != null)
+ {
+ afterThisLocation = semanticModel.SyntaxTree.GetLocation(new TextSpan(lastMember.Span.End, 0));
+ }
+ else
+ {
+ afterThisLocation = semanticModel.SyntaxTree.GetLocation(new TextSpan());
+ }
+
+ return Tuple.Create(globalNamespace,
+ rootNamespaceOrType,
+ afterThisLocation);
+ }
+
+ private NamespaceDeclarationSyntax GetDeclaringNamespace(List<string> containers, int indexDone, CompilationUnitSyntax compilationUnit)
+ {
+ foreach (var member in compilationUnit.Members)
+ {
+ var namespaceDeclaration = GetDeclaringNamespace(containers, 0, member);
+ if (namespaceDeclaration != null)
+ {
+ return namespaceDeclaration;
+ }
+ }
+
+ return null;
+ }
+
+ private NamespaceDeclarationSyntax GetDeclaringNamespace(List<string> containers, int indexDone, SyntaxNode localRoot)
+ {
+ var namespaceDecl = localRoot as NamespaceDeclarationSyntax;
+ if (namespaceDecl == null || namespaceDecl.Name is AliasQualifiedNameSyntax)
+ {
+ return null;
+ }
+
+ List<string> namespaceContainers = new List<string>();
+ GetNamespaceContainers(namespaceDecl.Name, namespaceContainers);
+
+ if (namespaceContainers.Count + indexDone > containers.Count ||
+ !IdentifierMatches(indexDone, namespaceContainers, containers))
+ {
+ return null;
+ }
+
+ indexDone = indexDone + namespaceContainers.Count;
+ if (indexDone == containers.Count)
+ {
+ return namespaceDecl;
+ }
+
+ foreach (var member in namespaceDecl.Members)
+ {
+ var resultant = GetDeclaringNamespace(containers, indexDone, member);
+ if (resultant != null)
+ {
+ return resultant;
+ }
+ }
+
+ return null;
+ }
+
+ private bool IdentifierMatches(int indexDone, List<string> namespaceContainers, List<string> containers)
+ {
+ for (int i = 0; i < namespaceContainers.Count; ++i)
+ {
+ if (namespaceContainers[i] != containers[indexDone + i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private void GetNamespaceContainers(NameSyntax name, List<string> namespaceContainers)
+ {
+ if (name is QualifiedNameSyntax)
+ {
+ GetNamespaceContainers(((QualifiedNameSyntax)name).Left, namespaceContainers);
+ namespaceContainers.Add(((QualifiedNameSyntax)name).Right.Identifier.ValueText);
+ }
+ else
+ {
+ Debug.Assert(name is SimpleNameSyntax);
+ namespaceContainers.Add(((SimpleNameSyntax)name).Identifier.ValueText);
+ }
+ }
+
+ internal override bool TryGetBaseList(ExpressionSyntax expression, out TypeKindOptions typeKindValue)
+ {
+ typeKindValue = TypeKindOptions.AllOptions;
+
+ if (expression == null)
+ {
+ return false;
+ }
+
+ var node = expression as SyntaxNode;
+
+ while (node != null)
+ {
+ if (node is BaseListSyntax)
+ {
+ if (node.Parent != null && (node.Parent is InterfaceDeclarationSyntax || node.Parent is StructDeclarationSyntax))
+ {
+ typeKindValue = TypeKindOptions.Interface;
+ return true;
+ }
+
+ typeKindValue = TypeKindOptions.BaseList;
+ return true;
+ }
+
+ node = node.Parent;
+ }
+
+ return false;
+ }
+
+ internal override bool IsPublicOnlyAccessibility(ExpressionSyntax expression, Project project)
+ {
+ if (expression == null)
+ {
+ return false;
+ }
+
+ if (GeneratedTypesMustBePublic(project))
+ {
+ return true;
+ }
+
+ var node = expression as SyntaxNode;
+ SyntaxNode previousNode = null;
+
+ while (node != null)
+ {
+ // Types in BaseList, Type Constraint or Member Types cannot be of restricter accessibility than the declaring type
+ if ((node is BaseListSyntax || node is TypeParameterConstraintClauseSyntax) &&
+ node.Parent != null &&
+ node.Parent is TypeDeclarationSyntax)
+ {
+ var typeDecl = node.Parent as TypeDeclarationSyntax;
+ if (typeDecl != null)
+ {
+ if (typeDecl.GetModifiers().Any(m => m.Kind() == SyntaxKind.PublicKeyword))
+ {
+ return IsAllContainingTypeDeclsPublic(typeDecl);
+ }
+ else
+ {
+ // The Type Decl which contains the BaseList does not contain Public
+ return false;
+ }
+ }
+
+ //Contract.Fail("Cannot reach this point");
+ }
+
+ if ((node is EventDeclarationSyntax || node is EventFieldDeclarationSyntax) &&
+ node.Parent != null &&
+ node.Parent is TypeDeclarationSyntax)
+ {
+ // Make sure the GFU is not inside the Accessors
+ if (previousNode != null && previousNode is AccessorListSyntax)
+ {
+ return false;
+ }
+
+ // Make sure that Event Declaration themselves are Public in the first place
+ if (!node.GetModifiers().Any(m => m.Kind() == SyntaxKind.PublicKeyword))
+ {
+ return false;
+ }
+
+ return IsAllContainingTypeDeclsPublic(node);
+ }
+
+ previousNode = node;
+ node = node.Parent;
+ }
+
+ return false;
+ }
+
+ private bool IsAllContainingTypeDeclsPublic(SyntaxNode node)
+ {
+ // Make sure that all the containing Type Declarations are also Public
+ var containingTypeDeclarations = node.GetAncestors<TypeDeclarationSyntax>();
+ if (containingTypeDeclarations.Count() == 0)
+ {
+ return true;
+ }
+ else
+ {
+ return containingTypeDeclarations.All(typedecl => typedecl.GetModifiers().Any(m => m.Kind() == SyntaxKind.PublicKeyword));
+ }
+ }
+
+ internal override bool IsGenericName(SimpleNameSyntax simpleName)
+ {
+ if (simpleName == null)
+ {
+ return false;
+ }
+
+ var genericName = simpleName as GenericNameSyntax;
+ return genericName != null;
+ }
+
+ internal override bool IsSimpleName(ExpressionSyntax expression)
+ {
+ return expression is SimpleNameSyntax;
+ }
+
+ internal override Solution TryAddUsingsOrImportToDocument(Solution updatedSolution, SyntaxNode modifiedRoot, Document document, SimpleNameSyntax simpleName, string includeUsingsOrImports, CancellationToken cancellationToken)
+ {
+ // Nothing to include
+ if (string.IsNullOrWhiteSpace(includeUsingsOrImports))
+ {
+ return updatedSolution;
+ }
+
+ var placeSystemNamespaceFirst = true;//document.Project.Solution.Workspace.Options.GetOption(OrganizerOptions.PlaceSystemNamespaceFirst, document.Project.Language);
+
+ SyntaxNode root = null;
+ if (modifiedRoot == null)
+ {
+ root = document.GetSyntaxRootAsync(cancellationToken).WaitAndGetResult(cancellationToken);
+ }
+ else
+ {
+ root = modifiedRoot;
+ }
+
+ if (root is CompilationUnitSyntax)
+ {
+ var compilationRoot = (CompilationUnitSyntax)root;
+ var usingDirective = SyntaxFactory.UsingDirective(SyntaxFactory.ParseName(includeUsingsOrImports));
+
+ // Check if the usings is already present
+ if (compilationRoot.Usings.Where(n => n != null && n.Alias == null)
+ .Select(n => n.Name.ToString())
+ .Any(n => n.Equals(includeUsingsOrImports)))
+ {
+ return updatedSolution;
+ }
+
+ // Check if the GFU is triggered from the namespace same as the usings namespace
+ if (IsWithinTheImportingNamespace(document, simpleName.SpanStart, includeUsingsOrImports, cancellationToken))
+ {
+ return updatedSolution;
+ }
+
+ var addedCompilationRoot = compilationRoot.AddUsingDirectives(new[] { usingDirective }, placeSystemNamespaceFirst, Formatter.Annotation);
+ updatedSolution = updatedSolution.WithDocumentSyntaxRoot(document.Id, addedCompilationRoot, PreservationMode.PreserveIdentity);
+ }
+
+ return updatedSolution;
+ }
+
+ private ITypeSymbol GetPropertyType(
+ SimpleNameSyntax property,
+ SemanticModel semanticModel,
+ CancellationToken cancellationToken)
+ {
+ var parent = property.Parent as AssignmentExpressionSyntax;
+ if (parent != null)
+ {
+ return TypeGuessing.typeInferenceService.InferType(semanticModel, parent.Left, true, cancellationToken);
+ }
+
+ return null;
+ }
+
+ private IPropertySymbol CreatePropertySymbol(SimpleNameSyntax propertyName, ITypeSymbol propertyType)
+ {
+ return CodeGenerationSymbolFactory.CreatePropertySymbol(
+ attributes: SpecializedCollections.EmptyList<AttributeData>(),
+ accessibility: Accessibility.Public,
+ modifiers: new DeclarationModifiers(),
+ explicitInterfaceSymbol: null,
+ name: propertyName.ToString(),
+ type: propertyType,
+ parameters: null,
+ getMethod: s_accessor,
+ setMethod: s_accessor,
+ isIndexer: false);
+ }
+
+ private static readonly IMethodSymbol s_accessor = CodeGenerationSymbolFactory.CreateAccessorSymbol(
+ attributes: null,
+ accessibility: Accessibility.Public,
+ statements: null);
+
+ internal override bool TryGenerateProperty(
+ SimpleNameSyntax propertyName,
+ SemanticModel semanticModel,
+ CancellationToken cancellationToken,
+ out IPropertySymbol property)
+ {
+ property = null;
+ var propertyType = GetPropertyType(propertyName, semanticModel, cancellationToken);
+ if (propertyType == null || propertyType is IErrorTypeSymbol)
+ {
+ property = CreatePropertySymbol(propertyName, semanticModel.Compilation.ObjectType);
+ return true;
+ }
+
+ property = CreatePropertySymbol(propertyName, propertyType);
+ return true;
+ }
+
+ internal override IMethodSymbol GetDelegatingConstructor(ObjectCreationExpressionSyntax objectCreation, INamedTypeSymbol namedType, SemanticModel model, ISet<IMethodSymbol> candidates, CancellationToken cancellationToken)
+ {
+ var oldNode = objectCreation
+ .AncestorsAndSelf(ascendOutOfTrivia: false)
+ .Where(node => SpeculationAnalyzer.CanSpeculateOnNode(node))
+ .LastOrDefault();
+
+ var typeNameToReplace = objectCreation.Type;
+ var newTypeName = namedType.GenerateTypeSyntax();
+ var newObjectCreation = objectCreation.WithType(newTypeName).WithAdditionalAnnotations(s_annotation);
+ var newNode = oldNode.ReplaceNode(objectCreation, newObjectCreation);
+
+ var speculativeModel = SpeculationAnalyzer.CreateSpeculativeSemanticModelForNode(oldNode, newNode, model);
+ if (speculativeModel != null)
+ {
+ newObjectCreation = (ObjectCreationExpressionSyntax)newNode.GetAnnotatedNodes(s_annotation).Single();
+ var symbolInfo = speculativeModel.GetSymbolInfo(newObjectCreation, cancellationToken);
+ return GenerateConstructorHelpers.GetDelegatingConstructor(symbolInfo, candidates, namedType);
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateType/GenerateTypeDialogOptions.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateType/GenerateTypeDialogOptions.cs
new file mode 100644
index 0000000000..ad969f88b3
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateType/GenerateTypeDialogOptions.cs
@@ -0,0 +1,27 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateType
+{
+ public class GenerateTypeDialogOptions
+ {
+ public bool IsPublicOnlyAccessibility { get; }
+ public TypeKindOptions TypeKindOptions { get; }
+ public bool IsAttribute { get; }
+
+ public GenerateTypeDialogOptions(
+ bool isPublicOnlyAccessibility = false,
+ TypeKindOptions typeKindOptions = TypeKindOptions.AllOptions,
+ bool isAttribute = false)
+ {
+ IsPublicOnlyAccessibility = isPublicOnlyAccessibility;
+ this.TypeKindOptions = typeKindOptions;
+ IsAttribute = isAttribute;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateType/GenerateTypeOptionsResult.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateType/GenerateTypeOptionsResult.cs
new file mode 100644
index 0000000000..17a4e6b4cc
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateType/GenerateTypeOptionsResult.cs
@@ -0,0 +1,55 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateType
+{
+ public class GenerateTypeOptionsResult
+ {
+ public static readonly GenerateTypeOptionsResult Cancelled = new GenerateTypeOptionsResult(isCancelled: true);
+
+ public Accessibility Accessibility { get; }
+ public Document ExistingDocument { get; }
+ public bool IsCancelled { get; }
+ public bool IsNewFile { get; }
+ public IList<string> Folders { get; }
+ public string NewFileName { get; }
+ public Project Project { get; }
+ public TypeKind TypeKind { get; }
+ public string FullFilePath { get; }
+ public string TypeName { get; }
+ public bool AreFoldersValidIdentifiers { get; }
+
+ public GenerateTypeOptionsResult(
+ Accessibility accessibility,
+ TypeKind typeKind,
+ string typeName,
+ Project project,
+ bool isNewFile,
+ string newFileName,
+ IList<string> folders,
+ string fullFilePath,
+ Document existingDocument,
+ bool areFoldersValidIdentifiers,
+ bool isCancelled = false)
+ {
+ this.Accessibility = accessibility;
+ this.TypeKind = typeKind;
+ this.TypeName = typeName;
+ this.Project = project;
+ this.IsNewFile = isNewFile;
+ this.NewFileName = newFileName;
+ this.Folders = folders;
+ this.FullFilePath = fullFilePath;
+ this.ExistingDocument = existingDocument;
+ this.AreFoldersValidIdentifiers = areFoldersValidIdentifiers;
+ this.IsCancelled = isCancelled;
+ }
+
+ private GenerateTypeOptionsResult(bool isCancelled)
+ {
+ this.IsCancelled = isCancelled;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateType/TypeKindOptions.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateType/TypeKindOptions.cs
new file mode 100644
index 0000000000..3bfdc96dfb
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GenerateType/TypeKindOptions.cs
@@ -0,0 +1,89 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ICSharpCode.NRefactory6.CSharp.GenerateType
+{
+ [Flags]
+ public enum TypeKindOptions
+ {
+ None = 0x0,
+
+ Class = 0x1,
+ Structure = 0x2,
+ Interface = 0x4,
+ Enum = 0x8,
+ Delegate = 0x10,
+ Module = 0x20,
+
+ // Enables class, struct, interface, enum and delegate
+ AllOptions = Class | Structure | Interface | Enum | Delegate,
+
+ // Only class is valid with Attribute
+ Attribute = Class,
+
+ // Only class, struct and interface are allowed. No Enums
+ BaseList = Class | Interface,
+
+ AllOptionsWithModule = AllOptions | Module,
+
+ // Only Interface and Delegate cannot be part of the member access with Namespace as Left expression
+ MemberAccessWithNamespace = Class | Structure | Enum | Module,
+
+ // Enum and Modules are incompatible with Generics
+ GenericInCompatibleTypes = Enum | Module
+ }
+
+ internal class TypeKindOptionsHelper
+ {
+ public static bool IsClass(TypeKindOptions option)
+ {
+ return (option & TypeKindOptions.Class) != 0 ? true : false;
+ }
+
+ public static bool IsStructure(TypeKindOptions option)
+ {
+ return (option & TypeKindOptions.Structure) != 0 ? true : false;
+ }
+
+ public static bool IsInterface(TypeKindOptions option)
+ {
+ return (option & TypeKindOptions.Interface) != 0 ? true : false;
+ }
+
+ public static bool IsEnum(TypeKindOptions option)
+ {
+ return (option & TypeKindOptions.Enum) != 0 ? true : false;
+ }
+
+ public static bool IsDelegate(TypeKindOptions option)
+ {
+ return (option & TypeKindOptions.Delegate) != 0 ? true : false;
+ }
+
+ public static bool IsModule(TypeKindOptions option)
+ {
+ return (option & TypeKindOptions.Module) != 0 ? true : false;
+ }
+
+ public static TypeKindOptions RemoveOptions(TypeKindOptions fromValue, params TypeKindOptions[] removeValues)
+ {
+ var tempReturnValue = fromValue;
+ foreach (var removeValue in removeValues)
+ {
+ tempReturnValue = tempReturnValue & ~removeValue;
+ }
+
+ return tempReturnValue;
+ }
+
+ internal static TypeKindOptions AddOption(TypeKindOptions toValue, TypeKindOptions addValue)
+ {
+ return toValue | addValue;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GotoDefinition/GotoDefinitionHelpers.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GotoDefinition/GotoDefinitionHelpers.cs
new file mode 100644
index 0000000000..11f39afa91
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GotoDefinition/GotoDefinitionHelpers.cs
@@ -0,0 +1,130 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Threading;
+using Microsoft.CodeAnalysis.FindSymbols;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.GotoDefinition
+{
+ static class GoToDefinitionHelpers
+ {
+
+ public static bool TryGoToDefinition(
+ ISymbol symbol,
+ Project project,
+ ITypeSymbol containingTypeSymbol,
+ bool throwOnHiddenDefinition,
+ CancellationToken cancellationToken)
+ {
+ var alias = symbol as IAliasSymbol;
+ if (alias != null)
+ {
+ var ns = alias.Target as INamespaceSymbol;
+ if (ns != null && ns.IsGlobalNamespace)
+ {
+ return false;
+ }
+ }
+
+ // VB global import aliases have a synthesized SyntaxTree.
+ // We can't go to the definition of the alias, so use the target type.
+
+ var solution = project.Solution;
+ if (symbol is IAliasSymbol &&
+ GeneratedCodeRecognitionService.GetPreferredSourceLocations(solution, symbol).All(l => project.Solution.GetDocument(l.SourceTree) == null))
+ {
+ symbol = ((IAliasSymbol)symbol).Target;
+ }
+
+ var definition = SymbolFinder.FindSourceDefinitionAsync(symbol, solution, cancellationToken).Result;
+ cancellationToken.ThrowIfCancellationRequested();
+
+ symbol = definition ?? symbol;
+
+ if (TryThirdPartyNavigation(symbol, solution, containingTypeSymbol))
+ {
+ return true;
+ }
+
+ // If it is a partial method declaration with no body, choose to go to the implementation
+ // that has a method body.
+ if (symbol is IMethodSymbol)
+ {
+ symbol = ((IMethodSymbol)symbol).PartialImplementationPart ?? symbol;
+ }
+
+ var preferredSourceLocations = GeneratedCodeRecognitionService.GetPreferredSourceLocations(solution, symbol).ToArray();
+ if (!preferredSourceLocations.Any())
+ {
+ // If there are no visible source locations, then tell the host about the symbol and
+ // allow it to navigate to it. THis will either navigate to any non-visible source
+ // locations, or it can appropriately deal with metadata symbols for hosts that can go
+ // to a metadata-as-source view.
+ return GoToDefinitionService.TryNavigateToSymbol(symbol, project, true);
+ }
+
+ // If we have a single location, then just navigate to it.
+ if (preferredSourceLocations.Length == 1)
+ {
+ var firstItem = preferredSourceLocations[0];
+ var workspace = project.Solution.Workspace;
+ if (GoToDefinitionService.CanNavigateToSpan(workspace, solution.GetDocument(firstItem.SourceTree).Id, firstItem.SourceSpan))
+ {
+ return GoToDefinitionService.TryNavigateToSpan(workspace, solution.GetDocument(firstItem.SourceTree).Id, firstItem.SourceSpan, true);
+ }
+ else
+ {
+ if (throwOnHiddenDefinition)
+ {
+ const int E_FAIL = -2147467259;
+ throw new COMException("The definition of the object is hidden.", E_FAIL);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ // We have multiple viable source locations, so ask the host what to do. Most hosts
+ // will simply display the results to the user and allow them to choose where to
+ // go.
+ GoToDefinitionService.DisplayMultiple (preferredSourceLocations.Select (location => Tuple.Create (solution, symbol, location)).ToList ());
+
+ return false;
+ }
+ }
+
+ private static bool TryThirdPartyNavigation(ISymbol symbol, Solution solution, ITypeSymbol containingTypeSymbol)
+ {
+ // Allow third parties to navigate to all symbols except types/constructors
+ // if we are navigating from the corresponding type.
+
+ if (containingTypeSymbol != null &&
+ (symbol is ITypeSymbol || symbol.IsConstructor()))
+ {
+ var candidateTypeSymbol = symbol is ITypeSymbol
+ ? symbol
+ : symbol.ContainingType;
+
+ if (containingTypeSymbol == candidateTypeSymbol)
+ {
+ // We are navigating from the same type, so don't allow third parties to perform the navigation.
+ // This ensures that if we navigate to a class from within that class, we'll stay in the same file
+ // rather than navigate to, say, XAML.
+ return false;
+ }
+ }
+
+ // Notify of navigation so third parties can intercept the navigation
+ return GoToDefinitionService.TrySymbolNavigationNotify(symbol, solution);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GotoDefinition/GotoDefinitionService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GotoDefinition/GotoDefinitionService.cs
new file mode 100644
index 0000000000..70d90023a6
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GotoDefinition/GotoDefinitionService.cs
@@ -0,0 +1,114 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.FindSymbols;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.GotoDefinition
+{
+ public static class GoToDefinitionService
+ {
+ /// <summary>
+ /// Navigate to the first source location of a given symbol.
+ /// bool TryNavigateToSymbol(ISymbol symbol, Project project, bool usePreviewTab = false);
+ /// </summary>
+ public static Func<ISymbol, Project, bool, bool> TryNavigateToSymbol = delegate {
+ return true;
+ };
+
+ /// <summary>
+ /// Navigates to the given position in the specified document, opening it if necessary.
+ /// bool TryNavigateToSpan(Workspace workspace, DocumentId documentId, TextSpan textSpan, bool usePreviewTab = false);
+ /// </summary>
+ public static Func<Workspace, DocumentId, TextSpan, bool, bool> TryNavigateToSpan = delegate {
+ return true;
+ };
+
+ /// <summary>
+ /// Determines whether it is possible to navigate to the given position in the specified document.
+ /// bool CanNavigateToSpan(Workspace workspace, DocumentId documentId, TextSpan textSpan);
+ /// </summary>
+ public static Func<Workspace, DocumentId, TextSpan, bool> CanNavigateToSpan = delegate {
+ return true;
+ };
+
+ public static Action<IEnumerable<Tuple<Solution, ISymbol, Location>>> DisplayMultiple = delegate {
+ };
+
+ /// <summary>
+ /// bool TrySymbolNavigationNotify(ISymbol symbol, Solution solution);
+ /// </summary>
+ /// <returns>True if the navigation was handled, indicating that the caller should not
+ /// perform the navigation.
+ ///
+ /// </returns>
+ public static Func<ISymbol, Solution, bool> TrySymbolNavigationNotify = delegate {
+ return false;
+ };
+
+ static ISymbol FindRelatedExplicitlyDeclaredSymbol(ISymbol symbol, Compilation compilation)
+ {
+ return symbol;
+ }
+
+ public static async Task<ISymbol> FindSymbolAsync(Document document, int position, CancellationToken cancellationToken)
+ {
+ var workspace = document.Project.Solution.Workspace;
+
+ var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
+ //var symbol = SymbolFinder.FindSymbolAtPosition(semanticModel, position, workspace, bindLiteralsToUnderlyingType: true, cancellationToken: cancellationToken);
+ var symbol = SymbolFinder.FindSymbolAtPosition(semanticModel, position, workspace, cancellationToken: cancellationToken);
+
+ return FindRelatedExplicitlyDeclaredSymbol(symbol, semanticModel.Compilation);
+ }
+
+// public async Task<IEnumerable<INavigableItem>> FindDefinitionsAsync(Document document, int position, CancellationToken cancellationToken)
+// {
+// var symbol = await FindSymbolAsync(document, position, cancellationToken).ConfigureAwait(false);
+//
+// // realize the list here so that the consumer await'ing the result doesn't lazily cause
+// // them to be created on an inappropriate thread.
+// return NavigableItemFactory.GetItemsfromPreferredSourceLocations(document.Project.Solution, symbol).ToList();
+// }
+
+ public static bool TryGoToDefinition(Document document, int position, CancellationToken cancellationToken)
+ {
+ var symbol = FindSymbolAsync(document, position, cancellationToken).Result;
+
+ if (symbol != null)
+ {
+ var containingTypeSymbol = GetContainingTypeSymbol(position, document, cancellationToken);
+
+ if (GoToDefinitionHelpers.TryGoToDefinition(symbol, document.Project, containingTypeSymbol, throwOnHiddenDefinition: true, cancellationToken: cancellationToken))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static ITypeSymbol GetContainingTypeSymbol(int caretPosition, Document document, CancellationToken cancellationToken)
+ {
+ var syntaxRoot = document.GetSyntaxRootAsync(cancellationToken).Result;
+ var containingTypeDeclaration = syntaxRoot.GetContainingTypeDeclaration(caretPosition);
+
+ if (containingTypeDeclaration != null)
+ {
+ var semanticModel = document.GetSemanticModelAsync(cancellationToken).Result;
+ return semanticModel.GetDeclaredSymbol(containingTypeDeclaration, cancellationToken) as ITypeSymbol;
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementAbstractClass/AbstractImplementAbstractClassService.Editor.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementAbstractClass/AbstractImplementAbstractClassService.Editor.cs
new file mode 100644
index 0000000000..8c7f9384b9
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementAbstractClass/AbstractImplementAbstractClassService.Editor.cs
@@ -0,0 +1,166 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.CodeGeneration;
+using Microsoft.CodeAnalysis.Editing;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+using ICSharpCode.NRefactory6.CSharp.CodeGeneration;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.ImplementAbstractClass
+{
+ public partial class AbstractImplementAbstractClassService
+ {
+ private partial class Editor
+ {
+ private readonly Document _document;
+ private readonly SemanticModel _model;
+ private readonly State _state;
+
+ public Editor(
+ Document document,
+ SemanticModel model,
+ State state)
+ {
+ _document = document;
+ _model = model;
+ _state = state;
+ }
+
+ public async Task<Document> GetEditAsync(CancellationToken cancellationToken)
+ {
+ var unimplementedMembers = _state.UnimplementedMembers;
+
+ var memberDefinitions = GenerateMembers(
+ unimplementedMembers,
+ cancellationToken);
+
+ var result = await CodeGenerator.AddMemberDeclarationsAsync(
+ _document.Project.Solution,
+ _state.ClassType,
+ memberDefinitions,
+ new CodeGenerationOptions(_state.Location.GetLocation(), generateDefaultAccessibility: false),
+ cancellationToken)
+ .ConfigureAwait(false);
+
+ return result;
+ }
+
+ private IList<ISymbol> GenerateMembers(
+ IList<Tuple<INamedTypeSymbol, IList<ISymbol>>> unimplementedMembers,
+ CancellationToken cancellationToken)
+ {
+ return
+ unimplementedMembers.SelectMany(t => t.Item2)
+ .Select(m => GenerateMember(m, cancellationToken))
+ .WhereNotNull()
+ .ToList();
+ }
+
+ private ISymbol GenerateMember(
+ ISymbol member,
+ CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ // Check if we need to add 'unsafe' to the signature we're generating.
+ var addUnsafe = member.IsUnsafe() && !_state.Location.IsUnsafeContext();
+
+ return GenerateMember(member, addUnsafe, cancellationToken);
+ }
+
+ private ISymbol GenerateMember(
+ ISymbol member,
+ bool addUnsafe,
+ CancellationToken cancellationToken)
+ {
+ var modifiers = DeclarationModifiers.None.WithIsOverride(true).WithIsUnsafe (addUnsafe);
+ var accessibility = member.ComputeResultantAccessibility(_state.ClassType);
+
+ if (member.Kind == SymbolKind.Method)
+ {
+ return GenerateMethod((IMethodSymbol)member, modifiers, accessibility, cancellationToken);
+ }
+ else if (member.Kind == SymbolKind.Property)
+ {
+ return GenerateProperty((IPropertySymbol)member, modifiers, accessibility, cancellationToken);
+ }
+ else if (member.Kind == SymbolKind.Event)
+ {
+ var @event = (IEventSymbol)member;
+ return CodeGenerationSymbolFactory.CreateEventSymbol(
+ @event,
+ accessibility: accessibility,
+ modifiers: modifiers);
+ }
+
+ return null;
+ }
+
+ private ISymbol GenerateMethod(
+ IMethodSymbol method, DeclarationModifiers modifiers, Accessibility accessibility, CancellationToken cancellationToken)
+ {
+ var syntaxFactory = _document.Project.LanguageServices.GetService<SyntaxGenerator>();
+
+ var throwingBody = syntaxFactory.CreateThrowNotImplementStatement (_model.Compilation);
+
+ method = method.EnsureNonConflictingNames(_state.ClassType, cancellationToken);
+
+ return CodeGenerationSymbolFactory.CreateMethodSymbol(
+ method,
+ accessibility: accessibility,
+ modifiers: modifiers,
+ statements: new [] { throwingBody });
+ }
+
+ private IPropertySymbol GenerateProperty(
+ IPropertySymbol property,
+ DeclarationModifiers modifiers,
+ Accessibility accessibility,
+ CancellationToken cancellationToken)
+ {
+ var syntaxFactory = _document.Project.LanguageServices.GetService<SyntaxGenerator>();
+
+ var throwingBody = syntaxFactory.CreateThrowNotImplementedStatementBlock(
+ _model.Compilation);
+
+ var getMethod = ShouldGenerateAccessor(property.GetMethod)
+ ? CodeGenerationSymbolFactory.CreateAccessorSymbol(
+ property.GetMethod,
+ attributes: null,
+ accessibility: property.GetMethod.ComputeResultantAccessibility(_state.ClassType),
+ statements: throwingBody)
+ : null;
+
+ var setMethod = ShouldGenerateAccessor(property.SetMethod)
+ ? CodeGenerationSymbolFactory.CreateAccessorSymbol(
+ property.SetMethod,
+ attributes: null,
+ accessibility: property.SetMethod.ComputeResultantAccessibility(_state.ClassType),
+ statements: throwingBody)
+ : null;
+
+ return CodeGenerationSymbolFactory.CreatePropertySymbol(
+ property,
+ accessibility: accessibility,
+ modifiers: modifiers,
+ getMethod: getMethod,
+ setMethod: setMethod);
+ }
+
+ private bool ShouldGenerateAccessor(IMethodSymbol method)
+ {
+ return
+ method != null &&
+ method.IsAccessibleWithin(_state.ClassType) &&
+ _state.ClassType.FindImplementationForAbstractMember(method) == null;
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementAbstractClass/AbstractImplementAbstractClassService.State.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementAbstractClass/AbstractImplementAbstractClassService.State.cs
new file mode 100644
index 0000000000..f2d52104af
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementAbstractClass/AbstractImplementAbstractClassService.State.cs
@@ -0,0 +1,70 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CodeGeneration;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.ImplementAbstractClass
+{
+ public partial class AbstractImplementAbstractClassService
+ {
+ private class State
+ {
+ public SyntaxNode Location { get; }
+ public INamedTypeSymbol ClassType { get; }
+ public INamedTypeSymbol AbstractClassType { get; }
+
+ // The members that are not implemented at all.
+ public IList<Tuple<INamedTypeSymbol, IList<ISymbol>>> UnimplementedMembers { get; }
+
+ private State(SyntaxNode node, INamedTypeSymbol classType, INamedTypeSymbol abstractClassType, IList<Tuple<INamedTypeSymbol, IList<ISymbol>>> unimplementedMembers)
+ {
+ this.Location = node;
+ this.ClassType = classType;
+ this.AbstractClassType = abstractClassType;
+ this.UnimplementedMembers = unimplementedMembers;
+ }
+
+ public static State Generate(
+ AbstractImplementAbstractClassService service,
+ Document document,
+ SemanticModel model,
+ SyntaxNode node,
+ CancellationToken cancellationToken)
+ {
+ INamedTypeSymbol classType, abstractClassType;
+ if (!service.TryInitializeState(document, model, node, cancellationToken,
+ out classType, out abstractClassType))
+ {
+ return null;
+ }
+
+ if (!CodeGenerator.CanAdd(document.Project.Solution, classType, cancellationToken))
+ {
+ return null;
+ }
+
+ if (classType.IsAbstract)
+ {
+ return null;
+ }
+
+ var unimplementedMembers = classType.GetAllUnimplementedMembers(
+ SpecializedCollections.SingletonEnumerable(abstractClassType), cancellationToken);
+
+ if (unimplementedMembers != null && unimplementedMembers.Count >= 1)
+ {
+ return new State(node, classType, abstractClassType, unimplementedMembers);
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementAbstractClass/AbstractImplementAbstractClassService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementAbstractClass/AbstractImplementAbstractClassService.cs
new file mode 100644
index 0000000000..88603c1b64
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementAbstractClass/AbstractImplementAbstractClassService.cs
@@ -0,0 +1,35 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.Internal.Log;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.ImplementAbstractClass
+{
+ public abstract partial class AbstractImplementAbstractClassService
+ {
+ protected AbstractImplementAbstractClassService()
+ {
+ }
+
+ protected abstract bool TryInitializeState(Document document, SemanticModel model, SyntaxNode classNode, CancellationToken cancellationToken, out INamedTypeSymbol classType, out INamedTypeSymbol abstractClassType);
+
+ public Task<Document> ImplementAbstractClassAsync(Document document, SemanticModel model, SyntaxNode node, CancellationToken cancellationToken)
+ {
+ var state = State.Generate(this, document, model, node, cancellationToken);
+ if (state == null)
+ {
+ return Task.FromResult (default(Document));
+ }
+
+ return new Editor(document, model, state).GetEditAsync(cancellationToken);
+ }
+
+ public bool CanImplementAbstractClass(Document document, SemanticModel model, SyntaxNode node, CancellationToken cancellationToken)
+ {
+ return State.Generate(this, document, model, node, cancellationToken) != null;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementAbstractClass/CSharpImplementAbstractClassService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementAbstractClass/CSharpImplementAbstractClassService.cs
new file mode 100644
index 0000000000..6a6042ca1e
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementAbstractClass/CSharpImplementAbstractClassService.cs
@@ -0,0 +1,46 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Composition;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Host;
+using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.ImplementAbstractClass
+{
+ public class CSharpImplementAbstractClassService : AbstractImplementAbstractClassService
+ {
+ protected override bool TryInitializeState(
+ Document document, SemanticModel model, SyntaxNode node, CancellationToken cancellationToken,
+ out INamedTypeSymbol classType, out INamedTypeSymbol abstractClassType)
+ {
+ var baseClassNode = node as TypeSyntax;
+ if (baseClassNode != null && baseClassNode.Parent is BaseTypeSyntax &&
+ baseClassNode.Parent.IsParentKind(SyntaxKind.BaseList) &&
+ ((BaseTypeSyntax)baseClassNode.Parent).Type == baseClassNode)
+ {
+ if (baseClassNode.Parent.Parent.IsParentKind(SyntaxKind.ClassDeclaration))
+ {
+ abstractClassType = model.GetTypeInfo(baseClassNode, cancellationToken).Type as INamedTypeSymbol;
+ cancellationToken.ThrowIfCancellationRequested();
+
+ if (abstractClassType.IsAbstractClass())
+ {
+ var classDecl = baseClassNode.Parent.Parent.Parent as ClassDeclarationSyntax;
+ classType = model.GetDeclaredSymbol(classDecl, cancellationToken) as INamedTypeSymbol;
+
+ return classType != null && abstractClassType != null;
+ }
+ }
+ }
+
+ classType = null;
+ abstractClassType = null;
+ return false;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs
new file mode 100644
index 0000000000..8baba57a93
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs
@@ -0,0 +1,559 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.CodeGeneration;
+using Microsoft.CodeAnalysis.Editing;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Shared.Utilities;
+using Microsoft.CodeAnalysis.Simplification;
+using Roslyn.Utilities;
+using ICSharpCode.NRefactory6.CSharp.CodeGeneration;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.ImplementInterface
+{
+ public abstract partial class AbstractImplementInterfaceService
+ {
+ internal partial class ImplementInterfaceCodeAction : CodeAction
+ {
+ protected readonly bool Explicitly;
+ protected readonly bool Abstractly;
+ protected readonly ISymbol ThroughMember;
+ protected readonly Document Document;
+ protected readonly State State;
+ protected readonly AbstractImplementInterfaceService Service;
+ private readonly string _equivalenceKey;
+
+ internal ImplementInterfaceCodeAction(
+ AbstractImplementInterfaceService service,
+ Document document,
+ State state,
+ bool explicitly,
+ bool abstractly,
+ ISymbol throughMember)
+ {
+ this.Service = service;
+ this.Document = document;
+ this.State = state;
+ this.Abstractly = abstractly;
+ this.Explicitly = explicitly;
+ this.ThroughMember = throughMember;
+ _equivalenceKey = ComputeEquivalenceKey(state, explicitly, abstractly, throughMember, this.GetType().FullName);
+ }
+
+ public static ImplementInterfaceCodeAction CreateImplementAbstractlyCodeAction(
+ AbstractImplementInterfaceService service,
+ Document document,
+ State state)
+ {
+ return new ImplementInterfaceCodeAction(service, document, state, explicitly: false, abstractly: true, throughMember: null);
+ }
+
+ public static ImplementInterfaceCodeAction CreateImplementCodeAction(
+ AbstractImplementInterfaceService service,
+ Document document,
+ State state)
+ {
+ return new ImplementInterfaceCodeAction(service, document, state, explicitly: false, abstractly: false, throughMember: null);
+ }
+
+ public static ImplementInterfaceCodeAction CreateImplementExplicitlyCodeAction(
+ AbstractImplementInterfaceService service,
+ Document document,
+ State state)
+ {
+ return new ImplementInterfaceCodeAction(service, document, state, explicitly: true, abstractly: false, throughMember: null);
+ }
+
+ public static ImplementInterfaceCodeAction CreateImplementThroughMemberCodeAction(
+ AbstractImplementInterfaceService service,
+ Document document,
+ State state,
+ ISymbol throughMember)
+ {
+ return new ImplementInterfaceCodeAction(service, document, state, explicitly: false, abstractly: false, throughMember: throughMember);
+ }
+
+ public override string Title
+ {
+ get
+ {
+ if (Explicitly)
+ {
+ return Resources.ImplementInterfaceExplicitly;
+ }
+ else if (Abstractly)
+ {
+ return Resources.ImplementInterfaceAbstractly;
+ }
+ else if (ThroughMember != null)
+ {
+ return string.Format(Resources.ImplementInterfaceThrough, GetDescription(ThroughMember));
+ }
+ else
+ {
+ return Resources.ImplementInterface;
+ }
+ }
+ }
+
+ private static string ComputeEquivalenceKey(
+ State state,
+ bool explicitly,
+ bool abstractly,
+ ISymbol throughMember,
+ string codeActionTypeName)
+ {
+ var interfaceType = state.InterfaceTypes.First();
+ var typeName = interfaceType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
+ var assemblyName = interfaceType.ContainingAssembly.Name;
+
+ return GetCodeActionEquivalenceKey(assemblyName, typeName, explicitly, abstractly, throughMember, codeActionTypeName);
+ }
+
+ // internal for testing purposes.
+ internal static string GetCodeActionEquivalenceKey(
+ string interfaceTypeAssemblyName,
+ string interfaceTypeFullyQualifiedName,
+ bool explicitly,
+ bool abstractly,
+ ISymbol throughMember,
+ string codeActionTypeName)
+ {
+ if (throughMember != null)
+ {
+ return null;
+ }
+
+ return explicitly.ToString() + ";" +
+ abstractly.ToString() + ";" +
+ interfaceTypeAssemblyName + ";" +
+ interfaceTypeFullyQualifiedName + ";" +
+ codeActionTypeName;
+ }
+
+ public override string EquivalenceKey
+ {
+ get
+ {
+ return _equivalenceKey;
+ }
+ }
+
+ private static string GetDescription(ISymbol throughMember)
+ {
+ return throughMember.TypeSwitch(
+ (IFieldSymbol field) => field.Name,
+ (IPropertySymbol property) => property.Name);
+ }
+
+ protected override Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken)
+ {
+ return GetUpdatedDocumentAsync(cancellationToken);
+ }
+
+ public Task<Document> GetUpdatedDocumentAsync(CancellationToken cancellationToken)
+ {
+ var unimplementedMembers = Explicitly ? State.UnimplementedExplicitMembers : State.UnimplementedMembers;
+ return GetUpdatedDocumentAsync(Document, unimplementedMembers, State.ClassOrStructType, State.ClassOrStructDecl, cancellationToken);
+ }
+
+ public virtual async Task<Document> GetUpdatedDocumentAsync(
+ Document document,
+ IList<Tuple<INamedTypeSymbol, IList<ISymbol>>> unimplementedMembers,
+ INamedTypeSymbol classOrStructType,
+ SyntaxNode classOrStructDecl,
+ CancellationToken cancellationToken)
+ {
+ var result = document;
+ var compilation = await result.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
+
+ var memberDefinitions = GenerateMembers(
+ compilation,
+ unimplementedMembers,
+ cancellationToken);
+
+ result = await CodeGenerator.AddMemberDeclarationsAsync(
+ result.Project.Solution, classOrStructType, memberDefinitions,
+ new CodeGenerationOptions(contextLocation: classOrStructDecl.GetLocation(), generateDefaultAccessibility: false),
+ cancellationToken).ConfigureAwait(false);
+
+ return result;
+ }
+
+ private IList<ISymbol> GenerateMembers(
+ Compilation compilation,
+ IList<Tuple<INamedTypeSymbol, IList<ISymbol>>> unimplementedMembers,
+ CancellationToken cancellationToken)
+ {
+ // As we go along generating members we may end up with conflicts. For example, say
+ // you have "interface IFoo { string Bar { get; } }" and "interface IQuux { int Bar
+ // { get; } }" and we need to implement both 'Bar' methods. The second will have to
+ // be explicitly implemented as it will conflict with the first. So we need to keep
+ // track of what we've actually implemented so that we can check further interface
+ // members against both the actual type and that list.
+ //
+ // Similarly, if you have two interfaces with the same member, then we don't want to
+ // implement that member twice.
+ //
+ // Note: if we implement a method explicitly then we do *not* add it to this list.
+ // That's because later members won't conflict with it even if they have the same
+ // signature otherwise. i.e. if we chose to implement IFoo.Bar explicitly, then we
+ // could implement IQuux.Bar implicitly (and vice versa).
+ var implementedVisibleMembers = new List<ISymbol>();
+ var implementedMembers = new List<ISymbol>();
+
+ foreach (var tuple in unimplementedMembers)
+ {
+ var interfaceType = tuple.Item1;
+ var unimplementedInterfaceMembers = tuple.Item2;
+
+ foreach (var unimplementedInterfaceMember in unimplementedInterfaceMembers)
+ {
+ var member = GenerateMember(compilation, unimplementedInterfaceMember, implementedVisibleMembers, cancellationToken);
+ if (member != null)
+ {
+ implementedMembers.Add(member);
+
+ if (!(member.ExplicitInterfaceImplementations().Any() && Service.HasHiddenExplicitImplementation))
+ {
+ implementedVisibleMembers.Add(member);
+ }
+ }
+ }
+ }
+
+ return implementedMembers;
+ }
+
+ private bool IsReservedName(string name)
+ {
+ return
+ IdentifiersMatch(State.ClassOrStructType.Name, name) ||
+ State.ClassOrStructType.TypeParameters.Any(t => IdentifiersMatch(t.Name, name));
+ }
+
+ private string DetermineMemberName(ISymbol member, List<ISymbol> implementedVisibleMembers)
+ {
+ if (HasConflictingMember(member, implementedVisibleMembers))
+ {
+ var memberNames = State.ClassOrStructType.GetAccessibleMembersInThisAndBaseTypes<ISymbol>(State.ClassOrStructType).Select(m => m.Name);
+
+ return NameGenerator.GenerateUniqueName(
+ string.Format("{0}_{1}", member.ContainingType.Name, member.Name),
+ n => !memberNames.Contains(n) &&
+ !implementedVisibleMembers.Any(m => IdentifiersMatch(m.Name, n)) &&
+ !IsReservedName(n));
+ }
+
+ return member.Name;
+ }
+
+ private ISymbol GenerateMember(
+ Compilation compilation,
+ ISymbol member,
+ List<ISymbol> implementedVisibleMembers,
+ CancellationToken cancellationToken)
+ {
+ // First check if we already generate a member that matches the member we want to
+ // generate. This can happen in C# when you have interfaces that have the same
+ // method, and you are implementing implicitly. For example:
+ //
+ // interface IFoo { void Foo(); }
+ //
+ // interface IBar : IFoo { new void Foo(); }
+ //
+ // class C : IBar
+ //
+ // In this case we only want to generate 'Foo' once.
+ if (HasMatchingMember(implementedVisibleMembers, member))
+ {
+ return null;
+ }
+
+ var memberName = DetermineMemberName(member, implementedVisibleMembers);
+
+ // See if we need to generate an invisible member. If we do, then reset the name
+ // back to what then member wants it to be.
+ var generateInvisibleMember = GenerateInvisibleMember(member, memberName);
+ memberName = generateInvisibleMember ? member.Name : memberName;
+
+ var generateAbstractly = !generateInvisibleMember && Abstractly;
+
+ // Check if we need to add 'new' to the signature we're adding. We only need to do this
+ // if we're not generating something explicit and we have a naming conflict with
+ // something in our base class hierarchy.
+ var addNew = !generateInvisibleMember && HasNameConflict(member, memberName, State.ClassOrStructType.GetBaseTypes());
+
+ // Check if we need to add 'unsafe' to the signature we're generating.
+ var addUnsafe = member.IsUnsafe() && !State.Location.IsUnsafeContext();
+
+ return GenerateMember(compilation, member, memberName, generateInvisibleMember, generateAbstractly, addNew, addUnsafe, cancellationToken);
+ }
+
+ private bool GenerateInvisibleMember(ISymbol member, string memberName)
+ {
+ if (Service.HasHiddenExplicitImplementation)
+ {
+ // User asked for an explicit (i.e. invisible) member.
+ if (Explicitly)
+ {
+ return true;
+ }
+
+ // Have to create an invisible member if we have constraints we can't express
+ // with a visible member.
+ if (HasUnexpressableConstraint(member))
+ {
+ return true;
+ }
+
+ // If we had a conflict with a member of the same name, then we have to generate
+ // as an invisible member.
+ if (member.Name != memberName)
+ {
+ return true;
+ }
+ }
+
+ // Can't generate an invisible member if the lanugage doesn't support it.
+ return false;
+ }
+
+ private bool HasUnexpressableConstraint(ISymbol member)
+ {
+ // interface IFoo<T> { void Bar<U>() where U : T; }
+ //
+ // class A : IFoo<int> { }
+ //
+ // In this case we cannot generate an implement method for Bar. That's because we'd
+ // need to say "where U : int" and that's disallowed by the language. So we must
+ // generate something explicit here.
+ if (member.Kind != SymbolKind.Method)
+ {
+ return false;
+ }
+
+ var method = member as IMethodSymbol;
+
+ return method.TypeParameters.Any(IsUnexpressableTypeParameter);
+ }
+
+ private static bool IsUnexpressableTypeParameter(ITypeParameterSymbol typeParameter)
+ {
+ var condition1 = typeParameter.ConstraintTypes.Count(t => t.TypeKind == TypeKind.Class) >= 2;
+ var condition2 = typeParameter.ConstraintTypes.Any(ts => ts.IsUnexpressableTypeParameterConstraint());
+ var condition3 = typeParameter.HasReferenceTypeConstraint && typeParameter.ConstraintTypes.Any(ts => ts.IsReferenceType && ts.SpecialType != SpecialType.System_Object);
+
+ return condition1 || condition2 || condition3;
+ }
+
+ private ISymbol GenerateMember(
+ Compilation compilation,
+ ISymbol member,
+ string memberName,
+ bool generateInvisibly,
+ bool generateAbstractly,
+ bool addNew,
+ bool addUnsafe,
+ CancellationToken cancellationToken)
+ {
+ var factory = this.Document.GetLanguageService<SyntaxGenerator>();
+ var modifiers = DeclarationModifiers.None.WithIsAbstract(generateAbstractly).WithIsNew (addNew).WithIsUnsafe (addUnsafe);
+
+ var useExplicitInterfaceSymbol = generateInvisibly || !Service.CanImplementImplicitly;
+ var accessibility = member.Name == memberName ? Accessibility.Public : Accessibility.Private;
+
+ if (member.Kind == SymbolKind.Method)
+ {
+ var method = (IMethodSymbol)member;
+
+ return GenerateMethod(compilation, method, accessibility, modifiers, generateAbstractly, useExplicitInterfaceSymbol, memberName, cancellationToken);
+ }
+ else if (member.Kind == SymbolKind.Property)
+ {
+ var property = (IPropertySymbol)member;
+
+ return GenerateProperty(compilation, property, accessibility, modifiers, generateAbstractly, useExplicitInterfaceSymbol, memberName, cancellationToken);
+ }
+ else if (member.Kind == SymbolKind.Event)
+ {
+ var @event = (IEventSymbol)member;
+
+ var accessor = CodeGenerationSymbolFactory.CreateAccessorSymbol(
+ attributes: null,
+ accessibility: Accessibility.NotApplicable,
+ statements: factory.CreateThrowNotImplementedStatementBlock(compilation));
+
+ return CodeGenerationSymbolFactory.CreateEventSymbol(
+ @event,
+ accessibility: accessibility,
+ modifiers: modifiers,
+ explicitInterfaceSymbol: useExplicitInterfaceSymbol ? @event : null,
+ name: memberName,
+ addMethod: generateInvisibly ? accessor : null,
+ removeMethod: generateInvisibly ? accessor : null);
+ }
+
+ return null;
+ }
+
+ private SyntaxNode CreateThroughExpression(SyntaxGenerator factory)
+ {
+ var through = ThroughMember.IsStatic
+ ? factory.IdentifierName(State.ClassOrStructType.Name)
+ : factory.ThisExpression();
+
+ through = factory.MemberAccessExpression(
+ through, factory.IdentifierName(ThroughMember.Name));
+
+ var throughMemberType = ThroughMember.GetMemberType();
+ if ((State.InterfaceTypes != null) && (throughMemberType != null))
+ {
+ // In the case of 'implement interface through field / property' , we need to know what
+ // interface we are implementing so that we can insert casts to this interface on every
+ // usage of the field in the generated code. Without these casts we would end up generating
+ // code that fails compilation in certain situations.
+ //
+ // For example consider the following code.
+ // class C : IReadOnlyList<int> { int[] field; }
+ // When applying the 'implement interface through field' code fix in the above example,
+ // we need to generate the following code to implement the Count property on IReadOnlyList<int>
+ // class C : IReadOnlyList<int> { int[] field; int Count { get { ((IReadOnlyList<int>)field).Count; } ...}
+ // as opposed to the following code which will fail to compile (because the array field
+ // doesn't have a property named .Count) -
+ // class C : IReadOnlyList<int> { int[] field; int Count { get { field.Count; } ...}
+ //
+ // The 'InterfaceTypes' property on the state object always contains only one item
+ // in the case of C# i.e. it will contain exactly the interface we are trying to implement.
+ // This is also the case most of the time in the case of VB, except in certain error conditions
+ // (recursive / circular cases) where the span of the squiggle for the corresponding
+ // diagnostic (BC30149) changes and 'InterfaceTypes' ends up including all interfaces
+ // in the Implements clause. For the purposes of inserting the above cast, we ignore the
+ // uncommon case and optimize for the common one - in other words, we only apply the cast
+ // in cases where we can unambiguously figure out which interface we are trying to implement.
+ var interfaceBeingImplemented = State.InterfaceTypes.SingleOrDefault();
+ if ((interfaceBeingImplemented != null) && (!throughMemberType.Equals(interfaceBeingImplemented)))
+ {
+ through = factory.CastExpression(interfaceBeingImplemented,
+ through.WithAdditionalAnnotations(Simplifier.Annotation));
+
+ through = through.Parenthesize();
+ }
+ }
+
+ return through.WithAdditionalAnnotations(Simplifier.Annotation);
+ }
+
+ private bool HasNameConflict(
+ ISymbol member,
+ string memberName,
+ IEnumerable<INamedTypeSymbol> baseTypes)
+ {
+ // There's a naming conflict if any member in the base types chain is accessible to
+ // us, has our name. Note: a simple name won't conflict with a generic name (and
+ // vice versa). A method only conflicts with another method if they have the same
+ // parameter signature (return type is irrelevant).
+ return
+ baseTypes.Any(ts => ts.GetMembers(memberName)
+ .Where(m => m.IsAccessibleWithin(State.ClassOrStructType))
+ .Any(m => HasNameConflict(member, memberName, m)));
+ }
+
+ private static bool HasNameConflict(
+ ISymbol member,
+ string memberName,
+ ISymbol baseMember)
+ {
+ //Contract.Requires(memberName == baseMember.Name);
+
+ if (member.Kind == SymbolKind.Method && baseMember.Kind == SymbolKind.Method)
+ {
+ // A method only conflicts with another method if htey have the same parameter
+ // signature (return type is irrelevant).
+ var method1 = (IMethodSymbol)member;
+ var method2 = (IMethodSymbol)baseMember;
+
+ if (method1.MethodKind == MethodKind.Ordinary &&
+ method2.MethodKind == MethodKind.Ordinary &&
+ method1.TypeParameters.Length == method2.TypeParameters.Length)
+ {
+ return method1.Parameters.Select(p => p.Type)
+ .SequenceEqual(method2.Parameters.Select(p => p.Type));
+ }
+ }
+
+ // Any non method members with the same name simple name conflict.
+ return true;
+ }
+
+ private bool IdentifiersMatch(string identifier1, string identifier2)
+ {
+ return this.IsCaseSensitive
+ ? identifier1 == identifier2
+ : StringComparer.OrdinalIgnoreCase.Equals(identifier1, identifier2);
+ }
+
+ private bool IsCaseSensitive
+ {
+ get
+ {
+ return true;//this.Document.GetLanguageService<ISyntaxFactsService>().IsCaseSensitive;
+ }
+ }
+
+ private bool HasMatchingMember(List<ISymbol> implementedVisibleMembers, ISymbol member)
+ {
+ // If this is a language that doesn't support implicit implementation then no
+ // implemented members will ever match. For example, if you have:
+ //
+ // Interface IFoo : sub Foo() : End Interface
+ //
+ // Interface IBar : Inherits IFoo : Shadows Sub Foo() : End Interface
+ //
+ // Class C : Implements IBar
+ //
+ // We'll first end up generating:
+ //
+ // Public Sub Foo() Implements IFoo.Foo
+ //
+ // However, that same method won't be viable for IBar.Foo (unlike C#) because it
+ // explicitly specifies its interface).
+ if (!Service.CanImplementImplicitly)
+ {
+ return false;
+ }
+
+ return implementedVisibleMembers.Any(m => MembersMatch(m, member));
+ }
+
+ private bool MembersMatch(ISymbol member1, ISymbol member2)
+ {
+ if (member1.Kind != member2.Kind)
+ {
+ return false;
+ }
+
+ if (member1.DeclaredAccessibility != member1.DeclaredAccessibility ||
+ member1.IsStatic != member1.IsStatic)
+ {
+ return false;
+ }
+
+ if (member1.ExplicitInterfaceImplementations().Any() || member2.ExplicitInterfaceImplementations().Any())
+ {
+ return false;
+ }
+
+ return SignatureComparer.HaveSameSignatureAndConstraintsAndReturnTypeAndAccessors(
+ member1, member2, this.IsCaseSensitive);
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.CodeAction_Conflicts.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.CodeAction_Conflicts.cs
new file mode 100644
index 0000000000..caee4b4f5f
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.CodeAction_Conflicts.cs
@@ -0,0 +1,89 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Shared.Utilities;
+using Roslyn.Utilities;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.ImplementInterface
+{
+ public abstract partial class AbstractImplementInterfaceService
+ {
+ internal partial class ImplementInterfaceCodeAction
+ {
+ private bool HasConflictingMember(ISymbol member, List<ISymbol> implementedVisibleMembers)
+ {
+ // Checks if this member conflicts with an existing member in classOrStructType or with
+ // a method we've already implemented. If so, we'll need to implement this one
+ // explicitly.
+
+ var allMembers = State.ClassOrStructType.GetAccessibleMembersInThisAndBaseTypes<ISymbol>(State.ClassOrStructType).Concat(implementedVisibleMembers);
+
+ var conflict1 = allMembers.Any(m => HasConflict(m, member));
+ var conflict2 = IsReservedName(member.Name);
+
+ return conflict1 || conflict2;
+ }
+
+ private bool HasConflict(ISymbol member1, ISymbol member2)
+ {
+ // If either of these members are invisible explicit, then there is no conflict.
+ if (Service.HasHiddenExplicitImplementation)
+ {
+ if (member1.ExplicitInterfaceImplementations().Any() || member2.ExplicitInterfaceImplementations().Any())
+ {
+ // explicit methods don't conflict with anything.
+ return false;
+ }
+ }
+
+ // Members normally conflict if they have the same name. The exceptions are methods
+ // and parameterized properties (which conflict if htey have the same signature).
+ if (!IdentifiersMatch(member1.Name, member2.Name))
+ {
+ return false;
+ }
+
+ // If they differ in type, then it's almost always a conflict. There may be
+ // exceptions to this, but i don't know of any.
+ if (member1.Kind != member2.Kind)
+ {
+ return true;
+ }
+
+ // At this point, we have two members of the same type with the same name. If they
+ // have a different signature (for example, methods, or parameterized properties),
+ // then they do not conflict.
+ if (!SignatureComparer.HaveSameSignature(member1, member2, this.IsCaseSensitive))
+ {
+ return false;
+ }
+
+ // Now we have to members with the same name, type and signature. If the language
+ // doesn't support implicit implementation, then these members are definitely in
+ // conflict.
+ if (!Service.CanImplementImplicitly)
+ {
+ return true;
+ }
+
+ // two members conflict if they have the same signature and have
+ //
+ // a) different return types
+ // b) different accessibility
+ // c) different constraints
+ if (member1.DeclaredAccessibility != member2.DeclaredAccessibility ||
+ !SignatureComparer.HaveSameSignatureAndConstraintsAndReturnTypeAndAccessors(member1, member2, this.IsCaseSensitive))
+ {
+ return true;
+ }
+
+ // Same name, type, accessibility, return type, *and* the services can implement
+ // implicitly. These are not in conflict.
+ return false;
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.CodeAction_Method.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.CodeAction_Method.cs
new file mode 100644
index 0000000000..fa1bd6dcfe
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.CodeAction_Method.cs
@@ -0,0 +1,80 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis.CodeGeneration;
+using Microsoft.CodeAnalysis.Editing;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.ImplementInterface
+{
+ public abstract partial class AbstractImplementInterfaceService
+ {
+ internal partial class ImplementInterfaceCodeAction
+ {
+ private ISymbol GenerateMethod(
+ Compilation compilation,
+ IMethodSymbol method,
+ Accessibility accessibility,
+ DeclarationModifiers modifiers,
+ bool generateAbstractly,
+ bool useExplicitInterfaceSymbol,
+ string memberName,
+ CancellationToken cancellationToken)
+ {
+
+ var updatedMethod = method.EnsureNonConflictingNames(
+ this.State.ClassOrStructType, cancellationToken);
+
+ updatedMethod = updatedMethod.RemoveAttributeFromParametersAndReturnType(compilation.ComAliasNameAttributeType());
+
+ return CodeGenerationSymbolFactory.CreateMethodSymbol(
+ updatedMethod,
+ accessibility: accessibility,
+ modifiers: modifiers,
+ explicitInterfaceSymbol: useExplicitInterfaceSymbol ? updatedMethod : null,
+ name: memberName,
+ statements: generateAbstractly ? null : new[] { CreateStatement(compilation, updatedMethod, cancellationToken) });
+ }
+
+ private SyntaxNode CreateStatement(
+ Compilation compilation,
+ IMethodSymbol method,
+ CancellationToken cancellationToken)
+ {
+ if (ThroughMember == null)
+ {
+ var factory = this.Document.GetLanguageService<SyntaxGenerator>();
+ return factory.CreateThrowNotImplementStatement(compilation);
+ }
+ else
+ {
+ return CreateDelegationStatement(method);
+ }
+ }
+
+ private SyntaxNode CreateDelegationStatement(
+ IMethodSymbol method)
+ {
+ var factory = this.Document.GetLanguageService<SyntaxGenerator>();
+ var through = CreateThroughExpression(factory);
+
+ var memberName = method.IsGenericMethod
+ ? factory.GenericName(method.Name, method.TypeArguments.OfType<ITypeSymbol>().ToList())
+ : factory.IdentifierName(method.Name);
+
+ through = factory.MemberAccessExpression(
+ through, memberName);
+
+ var arguments = factory.CreateArguments(method.Parameters.As<IParameterSymbol>());
+ var invocationExpression = factory.InvocationExpression(through, arguments);
+
+ return method.ReturnsVoid
+ ? factory.ExpressionStatement(invocationExpression)
+ : factory.ReturnStatement(invocationExpression);
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.CodeAction_Property.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.CodeAction_Property.cs
new file mode 100644
index 0000000000..1cd6f18d37
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.CodeAction_Property.cs
@@ -0,0 +1,150 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis.CodeGeneration;
+using Microsoft.CodeAnalysis.Editing;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Shared.Utilities;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.ImplementInterface
+{
+ public abstract partial class AbstractImplementInterfaceService
+ {
+ internal partial class ImplementInterfaceCodeAction
+ {
+ private ISymbol GenerateProperty(
+ Compilation compilation,
+ IPropertySymbol property,
+ Accessibility accessibility,
+ DeclarationModifiers modifiers,
+ bool generateAbstractly,
+ bool useExplicitInterfaceSymbol,
+ string memberName,
+ CancellationToken cancellationToken)
+ {
+ var factory = this.Document.GetLanguageService<SyntaxGenerator>();
+ var comAliasNameAttribute = compilation.ComAliasNameAttributeType();
+
+ var getAccessor = property.GetMethod == null
+ ? null
+ : CodeGenerationSymbolFactory.CreateAccessorSymbol(
+ property.GetMethod.RemoveAttributeFromParametersAndReturnType(comAliasNameAttribute),
+ attributes: null,
+ accessibility: accessibility,
+ explicitInterfaceSymbol: useExplicitInterfaceSymbol ? property.GetMethod : null,
+ statements: GetGetAccessorStatements(compilation, property, generateAbstractly, cancellationToken));
+
+ var setAccessor = property.SetMethod == null
+ ? null
+ : CodeGenerationSymbolFactory.CreateAccessorSymbol(
+ property.SetMethod.RemoveAttributeFromParametersAndReturnType(comAliasNameAttribute),
+ attributes: null,
+ accessibility: accessibility,
+ explicitInterfaceSymbol: useExplicitInterfaceSymbol ? property.SetMethod : null,
+ statements: GetSetAccessorStatements(compilation, property, generateAbstractly, cancellationToken));
+
+ var parameterNames = NameGenerator.EnsureUniqueness(
+ property.Parameters.Select(p => p.Name).ToList(), isCaseSensitive: true);
+
+ var updatedProperty = property.RenameParameters(parameterNames);
+
+ updatedProperty = updatedProperty.RemoveAttributeFromParameters(comAliasNameAttribute);
+
+ // TODO(cyrusn): Delegate through throughMember if it's non-null.
+ return CodeGenerationSymbolFactory.CreatePropertySymbol(
+ updatedProperty,
+ accessibility: accessibility,
+ modifiers: modifiers,
+ explicitInterfaceSymbol: useExplicitInterfaceSymbol ? property : null,
+ name: memberName,
+ getMethod: getAccessor,
+ setMethod: setAccessor);
+ }
+
+ private IList<SyntaxNode> GetSetAccessorStatements(
+ Compilation compilation,
+ IPropertySymbol property,
+ bool generateAbstractly,
+ CancellationToken cancellationToken)
+ {
+ if (generateAbstractly)
+ {
+ return null;
+ }
+
+ var factory = this.Document.GetLanguageService<SyntaxGenerator>();
+ if (ThroughMember != null)
+ {
+ var throughExpression = CreateThroughExpression(factory);
+ SyntaxNode expression;
+
+ if (property.IsIndexer)
+ {
+ expression = throughExpression;
+ }
+ else
+ {
+ expression = factory.MemberAccessExpression(
+ throughExpression, factory.IdentifierName(property.Name));
+ }
+
+ if (property.Parameters.Length > 0)
+ {
+ var arguments = factory.CreateArguments(property.Parameters.As<IParameterSymbol>());
+ expression = factory.ElementAccessExpression(expression, arguments);
+ }
+
+ expression = factory.AssignmentStatement(expression, factory.IdentifierName("value"));
+
+ return new[] { factory.ExpressionStatement(expression) };
+ }
+
+ return factory.CreateThrowNotImplementedStatementBlock(compilation);
+ }
+
+ private IList<SyntaxNode> GetGetAccessorStatements(
+ Compilation compilation,
+ IPropertySymbol property,
+ bool generateAbstractly,
+ CancellationToken cancellationToken)
+ {
+ if (generateAbstractly)
+ {
+ return null;
+ }
+
+ var factory = this.Document.GetLanguageService<SyntaxGenerator>();
+ if (ThroughMember != null)
+ {
+ var throughExpression = CreateThroughExpression(factory);
+ SyntaxNode expression;
+
+ if (property.IsIndexer)
+ {
+ expression = throughExpression;
+ }
+ else
+ {
+ expression = factory.MemberAccessExpression(
+ throughExpression, factory.IdentifierName(property.Name));
+ }
+
+ if (property.Parameters.Length > 0)
+ {
+ var arguments = factory.CreateArguments(property.Parameters.As<IParameterSymbol>());
+ expression = factory.ElementAccessExpression(expression, arguments);
+ }
+
+ return new[] { factory.ReturnStatement(expression) };
+ }
+
+ return factory.CreateThrowNotImplementedStatementBlock(compilation);
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.DisposePatternCodeAction.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.DisposePatternCodeAction.cs
new file mode 100644
index 0000000000..2830aec492
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.DisposePatternCodeAction.cs
@@ -0,0 +1,150 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.ImplementInterface
+{
+ public abstract partial class AbstractImplementInterfaceService
+ {
+ private static INamedTypeSymbol TryGetSymbolForIDisposable(Compilation compilation)
+ {
+ // Get symbol for 'System.IDisposable'.
+ var idisposable = compilation.GetSpecialType(SpecialType.System_IDisposable);
+ if ((idisposable != null) && (idisposable.TypeKind == TypeKind.Interface))
+ {
+ var idisposableMembers = idisposable.GetMembers().ToArray();
+
+ // Get symbol for 'System.IDisposable.Dispose()'.
+ IMethodSymbol disposeMethod = null;
+ if ((idisposableMembers.Length == 1) && (idisposableMembers[0].Kind == SymbolKind.Method) &&
+ (idisposableMembers[0].Name == "Dispose"))
+ {
+ disposeMethod = idisposableMembers[0] as IMethodSymbol;
+ if ((disposeMethod != null) && (!disposeMethod.IsStatic) && disposeMethod.ReturnsVoid &&
+ (disposeMethod.Arity == 0) && (disposeMethod.Parameters.Length == 0))
+ {
+ return idisposable;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private bool ShouldImplementDisposePattern(Document document, State state, bool explicitly)
+ {
+ // Dispose pattern should be implemented only if -
+ // 1. An interface named 'System.IDisposable' is unimplemented.
+ // 2. This interface has one and only one member - a non-generic method named 'Dispose' that takes no arguments and returns 'void'.
+ // 3. The implementing type is a class that does not already declare any conflicting members named 'disposedValue' or 'Dispose'
+ // (because we will be generating a 'disposedValue' field and a couple of methods named 'Dispose' as part of implementing
+ // the dispose pattern).
+ var unimplementedMembers = explicitly ? state.UnimplementedExplicitMembers : state.UnimplementedMembers;
+ var idisposable = TryGetSymbolForIDisposable(state.Model.Compilation);
+ return (idisposable != null) &&
+ unimplementedMembers.Any(m => m.Item1.Equals(idisposable)) &&
+ this.CanImplementDisposePattern(state.ClassOrStructType, state.ClassOrStructDecl);
+ }
+
+ internal class ImplementInterfaceWithDisposePatternCodeAction : ImplementInterfaceCodeAction
+ {
+ internal ImplementInterfaceWithDisposePatternCodeAction(
+ AbstractImplementInterfaceService service,
+ Document document,
+ State state,
+ bool explicitly,
+ bool abstractly,
+ ISymbol throughMember) : base(service, document, state, explicitly, abstractly, throughMember)
+ {
+ }
+
+ public static ImplementInterfaceWithDisposePatternCodeAction CreateImplementWithDisposePatternCodeAction(
+ AbstractImplementInterfaceService service,
+ Document document,
+ State state)
+ {
+ return new ImplementInterfaceWithDisposePatternCodeAction(service, document, state, explicitly: false, abstractly: false, throughMember: null);
+ }
+
+ public static ImplementInterfaceWithDisposePatternCodeAction CreateImplementExplicitlyWithDisposePatternCodeAction(
+ AbstractImplementInterfaceService service,
+ Document document,
+ State state)
+ {
+ return new ImplementInterfaceWithDisposePatternCodeAction(service, document, state, explicitly: true, abstractly: false, throughMember: null);
+ }
+
+ public override string Title
+ {
+ get
+ {
+ if (Explicitly)
+ {
+ return Resources.ImplementInterfaceExplicitlyWithDisposePattern;
+ }
+ else
+ {
+ return Resources.ImplementInterfaceWithDisposePattern;
+ }
+ }
+ }
+
+ private static readonly SyntaxAnnotation s_implementingTypeAnnotation = new SyntaxAnnotation("ImplementingType");
+ public override async Task<Document> GetUpdatedDocumentAsync(
+ Document document,
+ IList<Tuple<INamedTypeSymbol, IList<ISymbol>>> unimplementedMembers,
+ INamedTypeSymbol classOrStructType,
+ SyntaxNode classOrStructDecl,
+ CancellationToken cancellationToken)
+ {
+ var result = document;
+ var compilation = await result.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
+
+ // Add an annotation to the type declaration node so that we can find it again to append the dispose pattern implementation below.
+ result = await result.ReplaceNodeAsync(
+ classOrStructDecl,
+ classOrStructDecl.WithAdditionalAnnotations(s_implementingTypeAnnotation),
+ cancellationToken).ConfigureAwait(false);
+ var root = await result.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
+ classOrStructDecl = root.GetAnnotatedNodes(s_implementingTypeAnnotation).Single();
+ compilation = await result.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
+ classOrStructType = classOrStructType.GetSymbolKey().Resolve(compilation, cancellationToken: cancellationToken).Symbol as INamedTypeSymbol;
+
+ // Use the code generation service to generate all unimplemented members except those that are
+ // part of the dispose pattern. We can't use the code generation service to implement the dispose
+ // pattern since the code generation service doesn't support injection of the custom boiler
+ // plate code required for implementing the dispose pattern.
+ var idisposable = TryGetSymbolForIDisposable(compilation);
+ result = await base.GetUpdatedDocumentAsync(
+ result,
+ unimplementedMembers.Where(m => !m.Item1.Equals(idisposable)).ToList(),
+ classOrStructType,
+ classOrStructDecl,
+ cancellationToken).ConfigureAwait(false);
+
+ // Now append the dispose pattern implementation.
+ root = await result.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
+ classOrStructDecl = root.GetAnnotatedNodes(s_implementingTypeAnnotation).Single();
+ compilation = await result.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
+ classOrStructType = classOrStructType.GetSymbolKey().Resolve(compilation, cancellationToken: cancellationToken).Symbol as INamedTypeSymbol;
+ result = Service.ImplementDisposePattern(result, root, classOrStructType, classOrStructDecl.SpanStart, Explicitly);
+
+ // Remove the annotation since we don't need it anymore.
+ root = await result.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
+ classOrStructDecl = root.GetAnnotatedNodes(s_implementingTypeAnnotation).Single();
+ result = await result.ReplaceNodeAsync(
+ classOrStructDecl,
+ classOrStructDecl.WithoutAnnotations(s_implementingTypeAnnotation),
+ cancellationToken).ConfigureAwait(false);
+
+ return result;
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.State.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.State.cs
new file mode 100644
index 0000000000..61b468fc9f
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.State.cs
@@ -0,0 +1,84 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.CodeAnalysis.CodeGeneration;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.ImplementInterface
+{
+ public abstract partial class AbstractImplementInterfaceService
+ {
+ internal protected class State
+ {
+ public SyntaxNode Location { get; }
+ public SyntaxNode ClassOrStructDecl { get; }
+ public INamedTypeSymbol ClassOrStructType { get; }
+ public IEnumerable<INamedTypeSymbol> InterfaceTypes { get; }
+ public SemanticModel Model { get; }
+
+ // The members that are not implemented at all.
+ public IList<Tuple<INamedTypeSymbol, IList<ISymbol>>> UnimplementedMembers { get; private set; }
+
+ // The members that have no explicit implementation.
+ public IList<Tuple<INamedTypeSymbol, IList<ISymbol>>> UnimplementedExplicitMembers { get; private set; }
+
+ public State(SyntaxNode interfaceNode, SyntaxNode classOrStructDecl, INamedTypeSymbol classOrStructType, IEnumerable<INamedTypeSymbol> interfaceTypes, SemanticModel model)
+ {
+ this.Location = interfaceNode;
+ this.ClassOrStructDecl = classOrStructDecl;
+ this.ClassOrStructType = classOrStructType;
+ this.InterfaceTypes = interfaceTypes;
+ this.Model = model;
+ }
+
+ public static State Generate(
+ AbstractImplementInterfaceService service,
+ Document document,
+ SemanticModel model,
+ SyntaxNode interfaceNode,
+ CancellationToken cancellationToken)
+ {
+ SyntaxNode classOrStructDecl;
+ INamedTypeSymbol classOrStructType;
+ IEnumerable<INamedTypeSymbol> interfaceTypes;
+ if (!service.TryInitializeState(document, model, interfaceNode, cancellationToken,
+ out classOrStructDecl, out classOrStructType, out interfaceTypes))
+ {
+ return null;
+ }
+
+ if (!CodeGenerator.CanAdd(document.Project.Solution, classOrStructType, cancellationToken))
+ {
+ return null;
+ }
+
+ var state = new State(interfaceNode, classOrStructDecl, classOrStructType, interfaceTypes, model);
+
+ if (service.CanImplementImplicitly)
+ {
+ state.UnimplementedMembers = state.ClassOrStructType.GetAllUnimplementedMembers(
+ interfaceTypes, cancellationToken);
+
+ state.UnimplementedExplicitMembers = state.ClassOrStructType.GetAllUnimplementedExplicitMembers(
+ interfaceTypes, cancellationToken);
+
+ var allMembersImplemented = state.UnimplementedMembers == null || state.UnimplementedMembers.Count == 0;
+ var allMembersImplementedExplicitly = state.UnimplementedExplicitMembers == null || state.UnimplementedExplicitMembers.Count == 0;
+
+ return !allMembersImplementedExplicitly && !allMembersImplemented ? state : null;
+ }
+ else
+ {
+ state.UnimplementedMembers = state.ClassOrStructType.GetAllUnimplementedExplicitMembers(
+ interfaceTypes, cancellationToken);
+
+ var allMembersImplemented = state.UnimplementedMembers == null || state.UnimplementedMembers.Count == 0;
+ return !allMembersImplemented ? state : null;
+ }
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.cs
new file mode 100644
index 0000000000..bce7ae13ae
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/AbstractImplementInterfaceService.cs
@@ -0,0 +1,109 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.Internal.Log;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.ImplementInterface
+{
+ public abstract partial class AbstractImplementInterfaceService
+ {
+ protected AbstractImplementInterfaceService()
+ {
+ }
+
+ protected abstract bool CanImplementImplicitly { get; }
+ protected abstract bool HasHiddenExplicitImplementation { get; }
+ protected abstract bool TryInitializeState(Document document, SemanticModel model, SyntaxNode interfaceNode, CancellationToken cancellationToken, out SyntaxNode classOrStructDecl, out INamedTypeSymbol classOrStructType, out IEnumerable<INamedTypeSymbol> interfaceTypes);
+ protected abstract bool CanImplementDisposePattern(INamedTypeSymbol symbol, SyntaxNode classDecl);
+ protected abstract Document ImplementDisposePattern(Document document, SyntaxNode root, INamedTypeSymbol symbol, int position, bool explicitly);
+
+ public async Task<Document> ImplementInterfaceAsync(Document document, SyntaxNode node, CancellationToken cancellationToken)
+ {
+ var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
+ var state = State.Generate(this, document, model, node, cancellationToken);
+ if (state == null)
+ {
+ return document;
+ }
+
+ // While implementing just one default action, like in the case of pressing enter after interface name in VB,
+ // choose to implement with the dispose pattern as that's the Dev12 behavior.
+ var action = ShouldImplementDisposePattern(document, state, explicitly: false) ?
+ ImplementInterfaceWithDisposePatternCodeAction.CreateImplementWithDisposePatternCodeAction(this, document, state) :
+ ImplementInterfaceCodeAction.CreateImplementCodeAction(this, document, state);
+
+ return await action.GetUpdatedDocumentAsync(cancellationToken).ConfigureAwait(false);
+ }
+
+ public IEnumerable<CodeAction> GetCodeActions(Document document, SemanticModel model, SyntaxNode node, CancellationToken cancellationToken)
+ {
+ var state = State.Generate(this, document, model, node, cancellationToken);
+ return GetActions(document, state);
+ }
+
+ private IEnumerable<CodeAction> GetActions(Document document, State state)
+ {
+ if (state == null)
+ {
+ yield break;
+ }
+
+ if (state.UnimplementedMembers != null && state.UnimplementedMembers.Count > 0)
+ {
+ yield return ImplementInterfaceCodeAction.CreateImplementCodeAction(this, document, state);
+
+ if (ShouldImplementDisposePattern(document, state, explicitly: false))
+ {
+ yield return ImplementInterfaceWithDisposePatternCodeAction.CreateImplementWithDisposePatternCodeAction(this, document, state);
+ }
+
+ var delegatableMembers = GetDelegatableMembers(state);
+ foreach (var member in delegatableMembers)
+ {
+ yield return ImplementInterfaceCodeAction.CreateImplementThroughMemberCodeAction(this, document, state, member);
+ }
+
+ if (state.ClassOrStructType.IsAbstract)
+ {
+ yield return ImplementInterfaceCodeAction.CreateImplementAbstractlyCodeAction(this, document, state);
+ }
+ }
+
+ if (state.UnimplementedExplicitMembers != null && state.UnimplementedExplicitMembers.Count > 0)
+ {
+ yield return ImplementInterfaceCodeAction.CreateImplementExplicitlyCodeAction(this, document, state);
+
+ if (ShouldImplementDisposePattern(document, state, explicitly: true))
+ {
+ yield return ImplementInterfaceWithDisposePatternCodeAction.CreateImplementExplicitlyWithDisposePatternCodeAction(this, document, state);
+ }
+ }
+ }
+
+ private IList<ISymbol> GetDelegatableMembers(State state)
+ {
+ var fields =
+ state.ClassOrStructType.GetMembers()
+ .OfType<IFieldSymbol>()
+ .Where(f => !f.IsImplicitlyDeclared)
+ .Where(f => f.Type.GetAllInterfacesIncludingThis().Contains(state.InterfaceTypes.First()))
+ .OfType<ISymbol>();
+
+ // Select all properties with zero parameters that also have a getter
+ var properties =
+ state.ClassOrStructType.GetMembers()
+ .OfType<IPropertySymbol>()
+ .Where(p => (!p.IsImplicitlyDeclared) && (p.Parameters.Length == 0) && (p.GetMethod != null))
+ .Where(p => p.Type.GetAllInterfacesIncludingThis().Contains(state.InterfaceTypes.First()))
+ .OfType<ISymbol>();
+
+ return fields.Concat(properties).ToList();
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/CSharpImplementInterfaceService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/CSharpImplementInterfaceService.cs
new file mode 100644
index 0000000000..74c5693047
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ImplementInterface/CSharpImplementInterfaceService.cs
@@ -0,0 +1,170 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Composition;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Formatting;
+using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Simplification;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.ImplementInterface
+{
+ public class CSharpImplementInterfaceService : AbstractImplementInterfaceService
+ {
+ protected override bool TryInitializeState(
+ Document document, SemanticModel model, SyntaxNode node, CancellationToken cancellationToken,
+ out SyntaxNode classOrStructDecl, out INamedTypeSymbol classOrStructType, out IEnumerable<INamedTypeSymbol> interfaceTypes)
+ {
+ if (!cancellationToken.IsCancellationRequested)
+ {
+ var interfaceNode = node as TypeSyntax;
+ if (interfaceNode != null && interfaceNode.Parent is BaseTypeSyntax &&
+ interfaceNode.Parent.IsParentKind(SyntaxKind.BaseList) &&
+ ((BaseTypeSyntax)interfaceNode.Parent).Type == interfaceNode)
+ {
+ if (interfaceNode.Parent.Parent.IsParentKind(SyntaxKind.ClassDeclaration) ||
+ interfaceNode.Parent.Parent.IsParentKind(SyntaxKind.StructDeclaration))
+ {
+ var interfaceSymbolInfo = model.GetSymbolInfo(interfaceNode, cancellationToken);
+ if (interfaceSymbolInfo.CandidateReason != CandidateReason.WrongArity)
+ {
+ var interfaceType = interfaceSymbolInfo.GetAnySymbol() as INamedTypeSymbol;
+ cancellationToken.ThrowIfCancellationRequested();
+
+ if (interfaceType != null && interfaceType.TypeKind == TypeKind.Interface)
+ {
+ classOrStructDecl = interfaceNode.Parent.Parent.Parent as TypeDeclarationSyntax;
+ classOrStructType = model.GetDeclaredSymbol(classOrStructDecl, cancellationToken) as INamedTypeSymbol;
+ interfaceTypes = SpecializedCollections.SingletonEnumerable(interfaceType);
+
+ return interfaceTypes != null && classOrStructType != null;
+ }
+ }
+ }
+ }
+ }
+
+ classOrStructDecl = null;
+ classOrStructType = null;
+ interfaceTypes = null;
+ return false;
+ }
+
+ protected override bool CanImplementImplicitly
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ protected override bool HasHiddenExplicitImplementation
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ private static ClassDeclarationSyntax GetClassDeclarationAt(SyntaxNode root, int position)
+ {
+ var node = root.FindToken(position).Parent.FirstAncestorOrSelf((SyntaxNode n) => n.IsKind(SyntaxKind.ClassDeclaration));
+ return node as ClassDeclarationSyntax;
+ }
+
+ protected override bool CanImplementDisposePattern(INamedTypeSymbol symbol, SyntaxNode classDecl)
+ {
+ // The dispose pattern is only applicable if the implementing type is a class that does not already declare any conflicting
+ // members named 'disposedValue' or 'Dispose' (because we will be generating a 'disposedValue' field and a couple of methods
+ // named 'Dispose' as part of implementing the dispose pattern).
+ return (classDecl != null) &&
+ classDecl.IsKind(SyntaxKind.ClassDeclaration) &&
+ (symbol != null) &&
+ !symbol.GetMembers().Any(m => (m.MetadataName == "Dispose") || (m.MetadataName == "disposedValue"));
+ }
+
+ protected override Document ImplementDisposePattern(Document document, SyntaxNode root, INamedTypeSymbol symbol, int position, bool explicitly)
+ {
+ var classDecl = GetClassDeclarationAt(root, position);
+ Debug.Assert(CanImplementDisposePattern(symbol, classDecl), "ImplementDisposePattern called with bad inputs");
+
+ // Generate the IDisposable boilerplate code. The generated code cannot be one giant resource string
+ // because of the need to parse, format, and simplify the result; during pseudo-localized builds, resource
+ // strings are given a special prefix and suffix that will break the parser, hence the requirement to
+ // localize the comments individually.
+ var code = string.Format (@"
+ #region IDisposable Support
+ private bool disposedValue = false; // {0}
+
+ {1}void Dispose(bool disposing)
+ {{
+ if (!disposedValue)
+ {{
+ if (disposing)
+ {{
+ // {2}
+ }}
+
+ // {3}
+ // {4}
+
+ disposedValue = true;
+ }}
+ }}
+
+ // {5}
+ // ~{6}() {{
+ // // {7}
+ // Dispose(false);
+ // }}
+
+ // {8}
+ {9}Dispose()
+ {{
+ // {10}
+ Dispose(true);
+ // {11}
+ // GC.SuppressFinalize(this);
+ }}
+ #endregion
+ ",
+ Resources.ToDetectRedundantCalls,
+ (symbol.IsSealed ? "" : "protected virtual "),
+ Resources.DisposeManagedStateTodo,
+ Resources.FreeUnmanagedResourcesTodo,
+ Resources.SetLargeFieldsToNullTodo,
+ Resources.OverrideAFinalizerTodo,
+ classDecl.Identifier.Value,
+ Resources.DoNotChangeThisCodeUseDispose,
+ Resources.ThisCodeAddedToCorrectlyImplementDisposable,
+ (explicitly ? "void System.IDisposable." : "public void "),
+ Resources.DoNotChangeThisCodeUseDispose,
+ Resources.UncommentTheFollowingIfFinalizerOverriddenTodo
+ );
+
+ var decls = SyntaxFactory.ParseSyntaxTree(code)
+ .GetRoot().DescendantNodes().OfType<MemberDeclarationSyntax>()
+ .Select(decl => decl.WithAdditionalAnnotations(Formatter.Annotation, Simplifier.Annotation))
+ .ToArray();
+
+ // Append #endregion to the trailing trivia of the last declaration being generated.
+ decls[decls.Length - 1] = decls[decls.Length - 1].WithAppendedTrailingTrivia(
+ SyntaxFactory.TriviaList(
+ SyntaxFactory.Trivia(SyntaxFactory.EndRegionDirectiveTrivia(true)),
+ SyntaxFactory.CarriageReturnLineFeed));
+
+ // Ensure that open and close brace tokens are generated in case they are missing.
+ var newNode = classDecl.EnsureOpenAndCloseBraceTokens().AddMembers(decls);
+
+ return document.WithSyntaxRoot(root.ReplaceNode(classDecl, newNode));
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/CSharpIndentEngine.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/CSharpIndentEngine.cs
new file mode 100644
index 0000000000..fc0f170a7f
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/CSharpIndentEngine.cs
@@ -0,0 +1,537 @@
+//
+// CSharpIndentEngine.cs
+//
+// Author:
+// Matej Miklečić <matej.miklecic@gmail.com>
+//
+// Copyright (c) 2013 Matej Miklečić (matej.miklecic@gmail.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis.Options;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Formatting;
+using Microsoft.CodeAnalysis.Text;
+
+namespace ICSharpCode.NRefactory6.CSharp
+{
+ /// <summary>
+ /// Indentation engine based on a state machine.
+ /// Supports only pushing new chars to the end.
+ /// </summary>
+ /// <remarks>
+ /// Represents the context for transitions between <see cref="IndentState"/>.
+ /// Delegates the responsibility for pushing a new char to the current
+ /// state and changes between states depending on the pushed chars.
+ /// </remarks>
+ public class CSharpIndentEngine : IStateMachineIndentEngine
+ {
+ #region Properties
+
+ /// <summary>
+ /// Formatting options.
+ /// </summary>
+ internal readonly OptionSet options;
+
+ /// <summary>
+ /// Represents the new line character.
+ /// </summary>
+ internal readonly char newLineChar;
+
+ /// <summary>
+ /// The current indentation state.
+ /// </summary>
+ internal IndentState currentState;
+
+ /// <summary>
+ /// Stores conditional symbols of #define directives.
+ /// </summary>
+ internal HashSet<string> conditionalSymbols;
+
+ /// <summary>
+ /// Stores custom conditional symbols.
+ /// </summary>
+ internal HashSet<string> customConditionalSymbols;
+
+ /// <summary>
+ /// Stores the results of evaluations of the preprocessor if/elif directives
+ /// in the current block (between #if and #endif).
+ /// </summary>
+ internal CloneableStack<bool> ifDirectiveEvalResults = new CloneableStack<bool> ();
+
+ /// <summary>
+ /// Stores the indentation levels of the if directives in the current block.
+ /// </summary>
+ internal CloneableStack<Indent> ifDirectiveIndents = new CloneableStack<Indent>();
+
+ /// <summary>
+ /// Stores the last sequence of characters that can form a
+ /// valid keyword or variable name.
+ /// </summary>
+ internal StringBuilder wordToken;
+
+ /// <summary>
+ /// Stores the previous sequence of chars that formed a
+ /// valid keyword or variable name.
+ /// </summary>
+ internal string previousKeyword;
+
+ #endregion
+
+ #region IDocumentIndentEngine
+
+ /// <inheritdoc />
+ public string ThisLineIndent
+ {
+ get
+ {
+ // OPTION: IndentBlankLines
+ // remove the indentation of this line if isLineStart is true
+// if (!textEditorOptions.IndentBlankLines && isLineStart)
+// {
+// return string.Empty;
+// }
+
+ return currentState.ThisLineIndent.IndentString;
+ }
+ }
+
+ /// <inheritdoc />
+ public string NextLineIndent
+ {
+ get
+ {
+ return currentState.NextLineIndent.IndentString;
+ }
+ }
+
+ /// <inheritdoc />
+ public string CurrentIndent
+ {
+ get
+ {
+ return currentIndent.ToString();
+ }
+ }
+
+ /// <inheritdoc />
+ /// <remarks>
+ /// This is set depending on the current <see cref="Location"/> and
+ /// can change its value until the <see cref="newLineChar"/> char is
+ /// pushed. If this is true, that doesn't necessarily mean that the
+ /// current line has an incorrect indent (this can be determined
+ /// only at the end of the current line).
+ /// </remarks>
+ public bool NeedsReindent
+ {
+ get
+ {
+ // return true if it's the first column of the line and it has an indent
+ if (previousChar == newLineChar)
+ {
+ return ThisLineIndent.Length > 0;
+ }
+
+ // ignore incorrect indentations when there's only ws on this line
+ if (isLineStart)
+ {
+ return false;
+ }
+
+ return ThisLineIndent != CurrentIndent.ToString();
+ }
+ }
+
+ /// <inheritdoc />
+ public int Offset
+ {
+ get
+ {
+ return offset;
+ }
+ }
+
+// /// <inheritdoc />
+// public TextLocation Location
+// {
+// get
+// {
+// return new TextLocation(line, column);
+// }
+// }
+
+ /// <inheritdoc />
+ public bool EnableCustomIndentLevels
+ {
+ get;
+ set;
+ }
+
+ #endregion
+
+ #region Fields
+
+ /// <summary>
+ /// Represents the number of pushed chars.
+ /// </summary>
+ internal int offset = 0;
+
+ /// <summary>
+ /// The current line number.
+ /// </summary>
+ internal int line = 1;
+
+ /// <summary>
+ /// The current column number.
+ /// </summary>
+ /// <remarks>
+ /// One char can take up multiple columns (e.g. \t).
+ /// </remarks>
+ internal int column = 1;
+
+ /// <summary>
+ /// True if <see cref="char.IsWhiteSpace(char)"/> is true for all
+ /// chars at the current line.
+ /// </summary>
+ internal bool isLineStart = true;
+
+ /// <summary>
+ /// True if <see cref="isLineStart"/> was true before the current
+ /// <see cref="wordToken"/>.
+ /// </summary>
+ internal bool isLineStartBeforeWordToken = true;
+
+ /// <summary>
+ /// Current char that's being pushed.
+ /// </summary>
+ internal char currentChar = '\0';
+
+ /// <summary>
+ /// Last non-whitespace char that has been pushed.
+ /// </summary>
+ internal char previousChar = '\0';
+
+ /// <summary>
+ /// Previous new line char
+ /// </summary>
+ internal char previousNewline = '\0';
+
+ /// <summary>
+ /// Current indent level on this line.
+ /// </summary>
+ internal StringBuilder currentIndent = new StringBuilder();
+
+ /// <summary>
+ /// True if this line began in <see cref="VerbatimStringState"/>.
+ /// </summary>
+ internal bool lineBeganInsideVerbatimString = false;
+
+ /// <summary>
+ /// True if this line began in <see cref="MultiLineCommentState"/>.
+ /// </summary>
+ internal bool lineBeganInsideMultiLineComment = false;
+
+ #endregion
+
+ #region Constructors
+
+ /// <summary>
+ /// Creates a new CSharpIndentEngine instance.
+ /// </summary>
+ /// <param name="document">
+ /// An instance of <see cref="SourceText"/> which is being parsed.
+ /// </param>
+ /// <param name="formattingOptions">
+ /// C# formatting options.
+ /// </param>
+ public CSharpIndentEngine(OptionSet formattingOptions)
+ {
+ this.options = formattingOptions;
+
+ this.currentState = new GlobalBodyState(this);
+
+ this.conditionalSymbols = new HashSet<string>();
+ this.customConditionalSymbols = new HashSet<string>();
+ this.wordToken = new StringBuilder();
+ this.previousKeyword = string.Empty;
+ this.newLineChar = formattingOptions.GetOption(FormattingOptions.NewLine, LanguageNames.CSharp)[0];
+ }
+
+ /// <summary>
+ /// Creates a new CSharpIndentEngine instance from the given prototype.
+ /// </summary>
+ /// <param name="prototype">
+ /// An CSharpIndentEngine instance.
+ /// </param>
+ public CSharpIndentEngine(CSharpIndentEngine prototype)
+ {
+ this.options = prototype.options;
+
+ this.newLineChar = prototype.newLineChar;
+ this.currentState = prototype.currentState.Clone(this);
+ this.conditionalSymbols = new HashSet<string>(prototype.conditionalSymbols);
+ this.customConditionalSymbols = new HashSet<string>(prototype.customConditionalSymbols);
+
+ this.wordToken = new StringBuilder(prototype.wordToken.ToString());
+ this.previousKeyword = string.Copy(prototype.previousKeyword);
+
+ this.offset = prototype.offset;
+ this.line = prototype.line;
+ this.column = prototype.column;
+ this.isLineStart = prototype.isLineStart;
+ this.isLineStartBeforeWordToken = prototype.isLineStartBeforeWordToken;
+ this.currentChar = prototype.currentChar;
+ this.previousChar = prototype.previousChar;
+ this.previousNewline = prototype.previousNewline;
+ this.currentIndent = new StringBuilder(prototype.CurrentIndent.ToString());
+ this.lineBeganInsideMultiLineComment = prototype.lineBeganInsideMultiLineComment;
+ this.lineBeganInsideVerbatimString = prototype.lineBeganInsideVerbatimString;
+ this.ifDirectiveEvalResults = prototype.ifDirectiveEvalResults.Clone();
+ this.ifDirectiveIndents = prototype.ifDirectiveIndents.Clone();
+
+ this.EnableCustomIndentLevels = prototype.EnableCustomIndentLevels;
+ }
+
+ #endregion
+
+ #region IClonable
+
+ object ICloneable.Clone()
+ {
+ return Clone();
+ }
+
+ /// <inheritdoc />
+ IDocumentIndentEngine IDocumentIndentEngine.Clone()
+ {
+ return Clone();
+ }
+
+ public IStateMachineIndentEngine Clone()
+ {
+ return new CSharpIndentEngine(this);
+ }
+
+ #endregion
+
+ #region Methods
+
+ /// <inheritdoc />
+ public void Push(char ch)
+ {
+ // append this char to the wordbuf if it can form a valid keyword, otherwise check
+ // if the last sequence of chars form a valid keyword and reset the wordbuf.
+ if ((wordToken.Length == 0 ? char.IsLetter(ch) : char.IsLetterOrDigit(ch)) || ch == '_')
+ {
+ wordToken.Append(ch);
+ }
+ else if (wordToken.Length > 0)
+ {
+ currentState.CheckKeyword(wordToken.ToString());
+ previousKeyword = wordToken.ToString();
+ wordToken.Length = 0;
+ isLineStartBeforeWordToken = false;
+ }
+
+ var isNewLine = NewLine.IsNewLine(ch);
+ if (!isNewLine) {
+ currentState.Push(currentChar = ch);
+ offset++;
+ previousNewline = '\0';
+ // ignore whitespace and newline chars
+ var isWhitespace = currentChar == ' ' || currentChar == '\t';
+ if (!isWhitespace)
+ {
+ previousChar = currentChar;
+ isLineStart = false;
+ }
+
+ if (isLineStart)
+ {
+ currentIndent.Append(ch);
+ }
+
+ if (ch == '\t')
+ {
+ var indentSize = options.GetOption(FormattingOptions.IndentationSize, LanguageNames.CSharp);
+ var nextTabStop = (column - 1 + indentSize) / indentSize;
+ column = 1 + nextTabStop * indentSize;
+ }
+ else
+ {
+ column++;
+ }
+ } else {
+ if (ch == NewLine.LF && previousNewline == NewLine.CR) {
+ offset++;
+ return;
+ }
+ currentState.Push(currentChar = newLineChar);
+ offset++;
+
+ previousNewline = ch;
+ // there can be more than one chars that determine the EOL,
+ // the engine uses only one of them defined with newLineChar
+ if (currentChar != newLineChar)
+ {
+ return;
+ }
+ currentIndent.Length = 0;
+ isLineStart = true;
+ isLineStartBeforeWordToken = true;
+ column = 1;
+ line++;
+
+ lineBeganInsideMultiLineComment = IsInsideMultiLineComment;
+ lineBeganInsideVerbatimString = IsInsideVerbatimString;
+ }
+ }
+
+ /// <inheritdoc />
+ public void Reset()
+ {
+ currentState = new GlobalBodyState(this);
+ conditionalSymbols.Clear();
+ ifDirectiveEvalResults.Clear();
+ ifDirectiveIndents.Clear();
+
+ offset = 0;
+ line = 1;
+ column = 1;
+ isLineStart = true;
+ currentChar = '\0';
+ previousChar = '\0';
+ currentIndent.Length = 0;
+ lineBeganInsideMultiLineComment = false;
+ lineBeganInsideVerbatimString = false;
+ }
+
+ /// <inheritdoc />
+ public void Update(SourceText sourceText, int offset)
+ {
+ if (Offset > offset)
+ {
+ Reset();
+ }
+
+ while (Offset < offset)
+ {
+ Push(sourceText[Offset]);
+ }
+ }
+
+ /// <summary>
+ /// Defines the conditional symbol.
+ /// </summary>
+ /// <param name="defineSymbol">The symbol to define.</param>
+ public void DefineSymbol(string defineSymbol)
+ {
+ if (!customConditionalSymbols.Contains(defineSymbol))
+ customConditionalSymbols.Add(defineSymbol);
+ }
+
+ /// <summary>
+ /// Removes the symbol.
+ /// </summary>
+ /// <param name="undefineSymbol">The symbol to undefine.</param>
+ public void RemoveSymbol(string undefineSymbol)
+ {
+ if (customConditionalSymbols.Contains(undefineSymbol))
+ customConditionalSymbols.Remove(undefineSymbol);
+ }
+ #endregion
+
+ #region IStateMachineIndentEngine
+
+ public bool IsInsidePreprocessorDirective
+ {
+ get { return currentState is PreProcessorState; }
+ }
+
+ public bool IsInsidePreprocessorComment
+ {
+ get { return currentState is PreProcessorCommentState; }
+ }
+
+ public bool IsInsideStringLiteral
+ {
+ get { return currentState is StringLiteralState; }
+ }
+
+ public bool IsInsideVerbatimString
+ {
+ get { return currentState is VerbatimStringState; }
+ }
+
+ public bool IsInsideCharacter
+ {
+ get { return currentState is CharacterState; }
+ }
+
+ public bool IsInsideString
+ {
+ get { return IsInsideStringLiteral || IsInsideVerbatimString || IsInsideCharacter; }
+ }
+
+ public bool IsInsideLineComment
+ {
+ get { return currentState is LineCommentState; }
+ }
+
+ public bool IsInsideMultiLineComment
+ {
+ get { return currentState is MultiLineCommentState; }
+ }
+
+ public bool IsInsideDocLineComment
+ {
+ get { return currentState is DocCommentState; }
+ }
+
+ public bool IsInsideComment
+ {
+ get { return IsInsideLineComment || IsInsideMultiLineComment || IsInsideDocLineComment; }
+ }
+
+ public bool IsInsideOrdinaryComment
+ {
+ get { return IsInsideLineComment || IsInsideMultiLineComment; }
+ }
+
+ public bool IsInsideOrdinaryCommentOrString
+ {
+ get { return IsInsideOrdinaryComment || IsInsideString; }
+ }
+
+ public bool LineBeganInsideVerbatimString
+ {
+ get { return lineBeganInsideVerbatimString; }
+ }
+
+ public bool LineBeganInsideMultiLineComment
+ {
+ get { return lineBeganInsideMultiLineComment; }
+ }
+
+ #endregion
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/CacheIndentEngine.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/CacheIndentEngine.cs
new file mode 100644
index 0000000000..9cb2abb6dc
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/CacheIndentEngine.cs
@@ -0,0 +1,623 @@
+//
+// CacheIndentEngine.cs
+//
+// Author:
+// Matej Miklečić <matej.miklecic@gmail.com>
+//
+// Copyright (c) 2013 Matej Miklečić (matej.miklecic@gmail.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+
+namespace ICSharpCode.NRefactory6.CSharp
+{
+ /// <summary>
+ /// Represents a decorator of an IStateMachineIndentEngine instance that provides
+ /// logic for reseting and updating the engine on text changed events.
+ /// </summary>
+ /// <remarks>
+ /// The decorator is based on periodical caching of the engine's state and
+ /// delegating all logic behind indentation to the currently active engine.
+ /// </remarks>
+ public class CacheIndentEngine : IStateMachineIndentEngine
+ {
+
+ #region Properties
+
+ IStateMachineIndentEngine currentEngine;
+ Stack<IStateMachineIndentEngine> cachedEngines = new Stack<IStateMachineIndentEngine>();
+
+ #endregion
+
+ #region Constructors
+
+ /// <summary>
+ /// Creates a new CacheIndentEngine instance.
+ /// </summary>
+ /// <param name="decoratedEngine">
+ /// An instance of <see cref="IStateMachineIndentEngine"/> to which the
+ /// logic for indentation will be delegated.
+ /// </param>
+ /// <param name="cacheRate">
+ /// The number of chars between caching.
+ /// </param>
+ public CacheIndentEngine(IStateMachineIndentEngine decoratedEngine, int cacheRate = 2000)
+ {
+ this.currentEngine = decoratedEngine;
+ }
+
+ /// <summary>
+ /// Creates a new CacheIndentEngine instance from the given prototype.
+ /// </summary>
+ /// <param name="prototype">
+ /// A CacheIndentEngine instance.
+ /// </param>
+ public CacheIndentEngine(CacheIndentEngine prototype)
+ {
+ this.currentEngine = prototype.currentEngine.Clone();
+ }
+
+ #endregion
+
+ #region IDocumentIndentEngine
+
+ /// <inheritdoc />
+ public string ThisLineIndent {
+ get { return currentEngine.ThisLineIndent; }
+ }
+
+ /// <inheritdoc />
+ public string NextLineIndent {
+ get { return currentEngine.NextLineIndent; }
+ }
+
+ /// <inheritdoc />
+ public string CurrentIndent {
+ get { return currentEngine.CurrentIndent; }
+ }
+
+ /// <inheritdoc />
+ public bool NeedsReindent {
+ get { return currentEngine.NeedsReindent; }
+ }
+
+ /// <inheritdoc />
+ public int Offset {
+ get { return currentEngine.Offset; }
+ }
+
+// /// <inheritdoc />
+// public TextLocation Location {
+// get { return currentEngine.Location; }
+// }
+
+ /// <inheritdoc />
+ public bool EnableCustomIndentLevels
+ {
+ get { return currentEngine.EnableCustomIndentLevels; }
+ set { currentEngine.EnableCustomIndentLevels = value; }
+ }
+
+ /// <inheritdoc />
+ public void Push(char ch)
+ {
+ currentEngine.Push(ch);
+ }
+
+ /// <inheritdoc />
+ public void Reset()
+ {
+ currentEngine.Reset();
+ cachedEngines.Clear();
+ }
+
+ /// <summary>
+ /// Resets the engine to offset. Clears all cached engines after the given offset.
+ /// </summary>
+ public void ResetEngineToPosition(SourceText sourceText, int offset)
+ {
+ // We are already there
+ if (currentEngine.Offset <= offset)
+ return;
+
+ bool gotCachedEngine = false;
+ while (cachedEngines.Count > 0) {
+ var topEngine = cachedEngines.Peek();
+ if (topEngine.Offset <= offset) {
+ currentEngine = topEngine.Clone();
+ gotCachedEngine = true;
+ break;
+ } else {
+ cachedEngines.Pop();
+ }
+ }
+ if (!gotCachedEngine)
+ currentEngine.Reset();
+ }
+
+ /// <inheritdoc />
+ /// <remarks>
+ /// If the <paramref name="position"/> is negative, the engine will
+ /// update to: document.TextLength + (offset % document.TextLength+1)
+ /// Otherwise it will update to: offset % document.TextLength+1
+ /// </remarks>
+ public void Update(SourceText sourceText, int position)
+ {
+ const int BUFFER_SIZE = 2000;
+
+ if (currentEngine.Offset == position) {
+ //positions match, nothing to be done
+ return;
+ } else if (currentEngine.Offset > position) {
+ //moving backwards, so reset from previous saved location
+ ResetEngineToPosition(sourceText, position);
+ }
+
+ // get the engine caught up
+ int nextSave = (cachedEngines.Count == 0) ? BUFFER_SIZE : cachedEngines.Peek().Offset + BUFFER_SIZE;
+ if (currentEngine.Offset + 1 == position) {
+ char ch = sourceText[currentEngine.Offset];
+ currentEngine.Push(ch);
+ if (currentEngine.Offset == nextSave)
+ cachedEngines.Push(currentEngine.Clone());
+ } else {
+ //bulk copy characters in case buffer is unmanaged
+ //(faster if we reduce managed/unmanaged transitions)
+ while (currentEngine.Offset < position) {
+ int endCut = currentEngine.Offset + BUFFER_SIZE;
+ if (endCut > position)
+ endCut = position;
+ string buffer = sourceText.GetSubText(TextSpan.FromBounds(currentEngine.Offset, endCut)).ToString();
+ foreach (char ch in buffer) {
+ currentEngine.Push(ch);
+ //ConsoleWrite ("pushing character '{0}'", ch);
+ if (currentEngine.Offset == nextSave) {
+ cachedEngines.Push(currentEngine.Clone());
+ nextSave += BUFFER_SIZE;
+ }
+ }
+ }
+ }
+ }
+
+ //public IStateMachineIndentEngine GetEngine(int offset)
+ //{
+ // ResetEngineToPosition(offset);
+ // return currentEngine;
+ //}
+
+ #endregion
+
+ #region IClonable
+
+ /// <inheritdoc />
+ public IStateMachineIndentEngine Clone()
+ {
+ return new CacheIndentEngine(this);
+ }
+
+ /// <inheritdoc />
+ IDocumentIndentEngine IDocumentIndentEngine.Clone()
+ {
+ return Clone();
+ }
+
+ object ICloneable.Clone()
+ {
+ return Clone();
+ }
+
+ #endregion
+
+ #region IStateMachineIndentEngine
+
+ public bool IsInsidePreprocessorDirective {
+ get { return currentEngine.IsInsidePreprocessorDirective; }
+ }
+
+ public bool IsInsidePreprocessorComment {
+ get { return currentEngine.IsInsidePreprocessorComment; }
+ }
+
+ public bool IsInsideStringLiteral {
+ get { return currentEngine.IsInsideStringLiteral; }
+ }
+
+ public bool IsInsideVerbatimString {
+ get { return currentEngine.IsInsideVerbatimString; }
+ }
+
+ public bool IsInsideCharacter {
+ get { return currentEngine.IsInsideCharacter; }
+ }
+
+ public bool IsInsideString {
+ get { return currentEngine.IsInsideString; }
+ }
+
+ public bool IsInsideLineComment {
+ get { return currentEngine.IsInsideLineComment; }
+ }
+
+ public bool IsInsideMultiLineComment {
+ get { return currentEngine.IsInsideMultiLineComment; }
+ }
+
+ public bool IsInsideDocLineComment {
+ get { return currentEngine.IsInsideDocLineComment; }
+ }
+
+ public bool IsInsideComment {
+ get { return currentEngine.IsInsideComment; }
+ }
+
+ public bool IsInsideOrdinaryComment {
+ get { return currentEngine.IsInsideOrdinaryComment; }
+ }
+
+ public bool IsInsideOrdinaryCommentOrString {
+ get { return currentEngine.IsInsideOrdinaryCommentOrString; }
+ }
+
+ public bool LineBeganInsideVerbatimString {
+ get { return currentEngine.LineBeganInsideVerbatimString; }
+ }
+
+ public bool LineBeganInsideMultiLineComment {
+ get { return currentEngine.LineBeganInsideMultiLineComment; }
+ }
+
+ #endregion
+
+ }
+ /*
+/ // <summary>
+ /// Represents a decorator of an IStateMachineIndentEngine instance that provides
+ /// logic for reseting and updating the engine on text changed events.
+ /// </summary>
+ /// <remarks>
+ /// The decorator is based on periodical caching of the engine's state and
+ /// delegating all logic behind indentation to the currently active engine.
+ /// </remarks>
+ public class CacheIndentEngine : IStateMachineIndentEngine
+ {
+ #region Properties
+
+ /// <summary>
+ /// Represents the cache interval in number of chars pushed to the engine.
+ /// </summary>
+ /// <remarks>
+ /// When this many new chars are pushed to the engine, the currently active
+ /// engine gets cloned and added to the end of <see cref="cachedEngines"/>.
+ /// </remarks>
+ readonly int cacheRate;
+
+ /// <summary>
+ /// Determines how much memory to reserve on initialization for the
+ /// cached engines.
+ /// </summary>
+ const int cacheCapacity = 25;
+
+ /// <summary>
+ /// Currently active engine.
+ /// </summary>
+ /// <remarks>
+ /// Should be equal to the last engine in <see cref="cachedEngines"/>.
+ /// </remarks>
+ IStateMachineIndentEngine currentEngine;
+
+ /// <summary>
+ /// List of cached engines sorted ascending by
+ /// <see cref="IDocumentIndentEngine.Offset"/>.
+ /// </summary>
+ IStateMachineIndentEngine[] cachedEngines;
+
+ /// <summary>
+ /// The index of the last cached engine in cachedEngines.
+ /// </summary>
+ /// <remarks>
+ /// Should be equal to: currentEngine.Offset / CacheRate
+ /// </remarks>
+ int lastCachedEngine;
+
+ #endregion
+
+ #region Constructors
+
+ /// <summary>
+ /// Creates a new CacheIndentEngine instance.
+ /// </summary>
+ /// <param name="decoratedEngine">
+ /// An instance of <see cref="IStateMachineIndentEngine"/> to which the
+ /// logic for indentation will be delegated.
+ /// </param>
+ /// <param name="cacheRate">
+ /// The number of chars between caching.
+ /// </param>
+ public CacheIndentEngine(IStateMachineIndentEngine decoratedEngine, int cacheRate = 2000)
+ {
+ this.cachedEngines = new IStateMachineIndentEngine[cacheCapacity];
+
+ this.cachedEngines[0] = decoratedEngine.Clone();
+ this.currentEngine = this.cachedEngines[0].Clone();
+ this.cacheRate = cacheRate;
+ }
+
+ /// <summary>
+ /// Creates a new CacheIndentEngine instance from the given prototype.
+ /// </summary>
+ /// <param name="prototype">
+ /// A CacheIndentEngine instance.
+ /// </param>
+ public CacheIndentEngine(CacheIndentEngine prototype)
+ {
+ this.cachedEngines = new IStateMachineIndentEngine[prototype.cachedEngines.Length];
+ Array.Copy(prototype.cachedEngines, this.cachedEngines, prototype.cachedEngines.Length);
+
+ this.lastCachedEngine = prototype.lastCachedEngine;
+ this.currentEngine = prototype.currentEngine.Clone();
+ this.cacheRate = prototype.cacheRate;
+ }
+
+ #endregion
+
+ #region Methods
+
+ /// <summary>
+ /// Performs caching of the <see cref="CacheIndentEngine.currentEngine"/>.
+ /// </summary>
+ void cache()
+ {
+ if (currentEngine.Offset % cacheRate != 0)
+ {
+ throw new Exception("The current engine's offset is not divisable with the cacheRate.");
+ }
+
+ // determine the new current engine from cachedEngines
+ lastCachedEngine = currentEngine.Offset / cacheRate;
+
+ if (cachedEngines.Length < lastCachedEngine + 1)
+ {
+ Array.Resize(ref cachedEngines, lastCachedEngine * 2);
+ }
+
+ cachedEngines[lastCachedEngine] = currentEngine.Clone();
+ }
+
+ #endregion
+
+ #region IDocumentIndentEngine
+
+ /// <inheritdoc />
+ public IDocument Document
+ {
+ get { return currentEngine.Document; }
+ }
+
+ /// <inheritdoc />
+ public string ThisLineIndent
+ {
+ get { return currentEngine.ThisLineIndent; }
+ }
+
+ /// <inheritdoc />
+ public string NextLineIndent
+ {
+ get { return currentEngine.NextLineIndent; }
+ }
+
+ /// <inheritdoc />
+ public string CurrentIndent
+ {
+ get { return currentEngine.CurrentIndent; }
+ }
+
+ /// <inheritdoc />
+ public bool NeedsReindent
+ {
+ get { return currentEngine.NeedsReindent; }
+ }
+
+ /// <inheritdoc />
+ public int Offset
+ {
+ get { return currentEngine.Offset; }
+ }
+
+ /// <inheritdoc />
+ public TextLocation Location
+ {
+ get { return currentEngine.Location; }
+ }
+
+ /// <inheritdoc />
+ public void Push(char ch)
+ {
+ currentEngine.Push(ch);
+
+ if (currentEngine.Offset % cacheRate == 0)
+ {
+ cache();
+ }
+ }
+
+ /// <inheritdoc />
+ public void Reset()
+ {
+ currentEngine = cachedEngines[lastCachedEngine = 0];
+ }
+
+ /// <inheritdoc />
+ /// <remarks>
+ /// If the <paramref name="offset"/> is negative, the engine will
+ /// update to: document.TextLength + (offset % document.TextLength+1)
+ /// Otherwise it will update to: offset % document.TextLength+1
+ /// </remarks>
+ public void Update(int offset)
+ {
+ // map the given offset to the [0, document.TextLength] interval
+ // using modulo arithmetics
+ offset %= Document.TextLength + 1;
+ if (offset < 0)
+ {
+ offset += Document.TextLength + 1;
+ }
+
+ // check if the engine has to be updated to some previous offset
+ if (currentEngine.Offset > offset)
+ {
+ // replace the currentEngine with the first one whose offset
+ // is less then the given <paramref name="offset"/>
+ lastCachedEngine = offset / cacheRate;
+ currentEngine = cachedEngines[lastCachedEngine].Clone();
+ }
+
+ // update the engine to the given offset
+ while (Offset < offset)
+ {
+ Push(Document.GetCharAt(Offset));
+ }
+ }
+
+ public IStateMachineIndentEngine GetEngine(int offset)
+ {
+ // map the given offset to the [0, document.TextLength] interval
+ // using modulo arithmetics
+ offset %= Document.TextLength + 1;
+ if (offset < 0)
+ {
+ offset += Document.TextLength + 1;
+ }
+
+ // check if the engine has to be updated to some previous offset
+ if (currentEngine.Offset > offset)
+ {
+ // replace the currentEngine with the first one whose offset
+ // is less then the given <paramref name="offset"/>
+ lastCachedEngine = offset / cacheRate;
+ return cachedEngines[lastCachedEngine].Clone();
+ }
+
+ return currentEngine;
+ }
+
+ #endregion
+
+ #region IClonable
+
+ /// <inheritdoc />
+ public IStateMachineIndentEngine Clone()
+ {
+ return new CacheIndentEngine(this);
+ }
+
+ /// <inheritdoc />
+ IDocumentIndentEngine IDocumentIndentEngine.Clone()
+ {
+ return Clone();
+ }
+
+ object ICloneable.Clone()
+ {
+ return Clone();
+ }
+
+ #endregion
+
+ #region IStateMachineIndentEngine
+
+ public bool IsInsidePreprocessorDirective
+ {
+ get { return currentEngine.IsInsidePreprocessorDirective; }
+ }
+
+ public bool IsInsidePreprocessorComment
+ {
+ get { return currentEngine.IsInsidePreprocessorComment; }
+ }
+
+ public bool IsInsideStringLiteral
+ {
+ get { return currentEngine.IsInsideStringLiteral; }
+ }
+
+ public bool IsInsideVerbatimString
+ {
+ get { return currentEngine.IsInsideVerbatimString; }
+ }
+
+ public bool IsInsideCharacter
+ {
+ get { return currentEngine.IsInsideCharacter; }
+ }
+
+ public bool IsInsideString
+ {
+ get { return currentEngine.IsInsideString; }
+ }
+
+ public bool IsInsideLineComment
+ {
+ get { return currentEngine.IsInsideLineComment; }
+ }
+
+ public bool IsInsideMultiLineComment
+ {
+ get { return currentEngine.IsInsideMultiLineComment; }
+ }
+
+ public bool IsInsideDocLineComment
+ {
+ get { return currentEngine.IsInsideDocLineComment; }
+ }
+
+ public bool IsInsideComment
+ {
+ get { return currentEngine.IsInsideComment; }
+ }
+
+ public bool IsInsideOrdinaryComment
+ {
+ get { return currentEngine.IsInsideOrdinaryComment; }
+ }
+
+ public bool IsInsideOrdinaryCommentOrString
+ {
+ get { return currentEngine.IsInsideOrdinaryCommentOrString; }
+ }
+
+ public bool LineBeganInsideVerbatimString
+ {
+ get { return currentEngine.LineBeganInsideVerbatimString; }
+ }
+
+ public bool LineBeganInsideMultiLineComment
+ {
+ get { return currentEngine.LineBeganInsideMultiLineComment; }
+ }
+
+ #endregion
+ }
+
+ */
+} \ No newline at end of file
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/IDocumentIndentEngine.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/IDocumentIndentEngine.cs
new file mode 100644
index 0000000000..793ff67397
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/IDocumentIndentEngine.cs
@@ -0,0 +1,99 @@
+//
+// IDocumentIndentEngine.cs
+//
+// Author:
+// Matej Miklečić <matej.miklecic@gmail.com>
+//
+// Copyright (c) 2013 Matej Miklečić (matej.miklecic@gmail.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+
+namespace ICSharpCode.NRefactory6.CSharp
+{
+ /// <summary>
+ /// The base interface for all indent engines.
+ /// </summary>
+ public interface IDocumentIndentEngine : ICloneable
+ {
+ /// <summary>
+ /// The indentation string of the current line.
+ /// </summary>
+ string ThisLineIndent { get; }
+
+ /// <summary>
+ /// The indentation string of the next line.
+ /// </summary>
+ string NextLineIndent { get; }
+
+ /// <summary>
+ /// The indent string on the beginning of the current line.
+ /// </summary>
+ string CurrentIndent { get; }
+
+ /// <summary>
+ /// True if the current line needs to be reindented.
+ /// </summary>
+ bool NeedsReindent { get; }
+
+ /// <summary>
+ /// The current offset of the engine.
+ /// </summary>
+ int Offset { get; }
+
+ /// <summary>
+ /// If this is true, the engine should try to adjust its indent
+ /// levels to manual user's corrections, even if they are wrong.
+ /// </summary>
+ bool EnableCustomIndentLevels { get; set; }
+
+ /// <summary>
+ /// Pushes a new char into the engine which calculates the new
+ /// indentation levels.
+ /// </summary>
+ /// <param name="ch">
+ /// A new character.
+ /// </param>
+ void Push(char ch);
+
+ /// <summary>
+ /// Resets the engine.
+ /// </summary>
+ void Reset();
+
+ /// <summary>
+ /// Updates the engine to the given offset.
+ /// </summary>
+ /// <param name="offset">
+ /// Valid offset in <see cref="Document"/>.
+ /// </param>
+ void Update(SourceText sourceText, int offset);
+
+ /// <summary>
+ /// Clones the engine and preserves the current state.
+ /// </summary>
+ /// <returns>
+ /// An indentical clone which can operate without interference
+ /// with this engine.
+ /// </returns>
+ new IDocumentIndentEngine Clone();
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/IStateMachineIndentEngine.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/IStateMachineIndentEngine.cs
new file mode 100644
index 0000000000..fa6a456f41
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/IStateMachineIndentEngine.cs
@@ -0,0 +1,60 @@
+//
+// IStateMachineIndentEngine.cs
+//
+// Author:
+// Matej Miklečić <matej.miklecic@gmail.com>
+//
+// Copyright (c) 2013 Matej Miklečić (matej.miklecic@gmail.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+namespace ICSharpCode.NRefactory6.CSharp
+{
+ public interface IStateMachineIndentEngine : IDocumentIndentEngine
+ {
+ bool IsInsidePreprocessorDirective { get; }
+
+ bool IsInsidePreprocessorComment { get; }
+
+ bool IsInsideStringLiteral { get; }
+
+ bool IsInsideVerbatimString { get; }
+
+ bool IsInsideCharacter { get; }
+
+ bool IsInsideString { get; }
+
+ bool IsInsideLineComment { get; }
+
+ bool IsInsideMultiLineComment { get; }
+
+ bool IsInsideDocLineComment { get; }
+
+ bool IsInsideComment { get; }
+
+ bool IsInsideOrdinaryComment { get; }
+
+ bool IsInsideOrdinaryCommentOrString { get; }
+
+ bool LineBeganInsideVerbatimString { get; }
+
+ bool LineBeganInsideMultiLineComment { get; }
+
+ new IStateMachineIndentEngine Clone();
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/ITextPasteHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/ITextPasteHandler.cs
new file mode 100644
index 0000000000..c93541e68c
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/ITextPasteHandler.cs
@@ -0,0 +1,52 @@
+// ITextPasteHandler.cs
+//
+// Author:
+// Mike Krüger <mkrueger@novell.com>
+//
+// Copyright (c) 2008 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using Microsoft.CodeAnalysis.Text;
+
+namespace ICSharpCode.NRefactory6.CSharp
+{
+ /// <summary>
+ /// The text paste handler can do formattings to a text that is about to be pasted
+ /// into the text document.
+ /// </summary>
+ public interface ITextPasteHandler
+ {
+ /// <summary>
+ /// Formats plain text that is inserted at a specified offset.
+ /// </summary>
+ /// <returns>
+ /// The text that will get inserted at that position.
+ /// </returns>
+ /// <param name="offset">The offset where the text will be inserted.</param>
+ /// <param name="text">The text to be inserted.</param>
+ /// <param name="copyData">Additional data in case the text was copied from a Mono.TextEditor.</param>
+ string FormatPlainText(SourceText sourceText, int offset, string text, byte[] copyData);
+
+ /// <summary>
+ /// Gets the copy data for a specific segment inside the document. This can contain additional information.
+ /// </summary>
+ /// <param name="segment">The text segment that is about to be copied.</param>
+ byte[] GetCopyData(SourceText sourceText, TextSpan segment);
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/IndentState.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/IndentState.cs
new file mode 100644
index 0000000000..d1f864dccf
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/IndentState.cs
@@ -0,0 +1,2018 @@
+//
+// IndentState.cs
+//
+// Author:
+// Matej Miklečić <matej.miklecic@gmail.com>
+//
+// Copyright (c) 2013 Matej Miklečić (matej.miklecic@gmail.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis.CSharp.Formatting;
+using Microsoft.CodeAnalysis.Formatting;
+using Microsoft.CodeAnalysis.Options;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp
+{
+ #region IndentState
+
+ /// <summary>
+ /// The base class for all indentation states.
+ /// Each state defines the logic for indentation based on chars that
+ /// are pushed to it.
+ /// </summary>
+ public abstract class IndentState : ICloneable
+ {
+ #region Properties
+
+ /// <summary>
+ /// The indentation engine using this state.
+ /// </summary>
+ public CSharpIndentEngine Engine;
+
+ /// <summary>
+ /// The parent state.
+ /// This state can use the indentation levels of its parent.
+ /// When this state exits, the engine returns to the parent.
+ /// </summary>
+ public IndentState Parent;
+
+ /// <summary>
+ /// The indentation of the current line.
+ /// This is set when the state is created and will be changed to
+ /// <see cref="NextLineIndent"/> when the <see cref="CSharpIndentEngine.newLineChar"/>
+ /// is pushed.
+ /// </summary>
+ public Indent ThisLineIndent;
+
+ /// <summary>
+ /// The indentation of the next line.
+ /// This is set when the state is created and can change depending
+ /// on the pushed chars.
+ /// </summary>
+ public Indent NextLineIndent;
+
+ #endregion
+
+ #region Constructors
+
+ protected IndentState()
+ {
+ }
+
+ /// <summary>
+ /// Creates a new indentation state that is a copy of the given
+ /// prototype.
+ /// </summary>
+ /// <param name="prototype">
+ /// The prototype state.
+ /// </param>
+ /// <param name="engine">
+ /// The engine of the new state.
+ /// </param>
+ protected IndentState(IndentState prototype, CSharpIndentEngine engine)
+ {
+ Engine = engine;
+ Parent = prototype.Parent != null ? prototype.Parent.Clone(engine) : null;
+
+ ThisLineIndent = prototype.ThisLineIndent.Clone();
+ NextLineIndent = prototype.NextLineIndent.Clone();
+ }
+
+ #endregion
+
+ #region IClonable
+
+ object ICloneable.Clone()
+ {
+ return Clone(Engine);
+ }
+
+ public abstract IndentState Clone(CSharpIndentEngine engine);
+
+ #endregion
+
+ #region Methods
+
+ internal void Initialize (CSharpIndentEngine engine, IndentState parent = null)
+ {
+ Parent = parent;
+ Engine = engine;
+
+ InitializeState();
+ }
+
+ /// <summary>
+ /// Initializes the state:
+ /// - sets the default indentation levels.
+ /// </summary>
+ /// <remarks>
+ /// Each state can override this method if it needs a different
+ /// logic for setting up the default indentations.
+ /// </remarks>
+ public virtual void InitializeState()
+ {
+ ThisLineIndent = new Indent(Engine.options);
+ NextLineIndent = ThisLineIndent.Clone();
+ }
+
+ /// <summary>
+ /// Actions performed when this state exits.
+ /// </summary>
+ public virtual void OnExit()
+ {
+ if (Parent != null)
+ {
+ // if a state exits on the newline character, it has to push
+ // it back to its parent (and so on recursively if the parent
+ // state also exits). Otherwise, the parent state wouldn't
+ // know that the engine isn't on the same line anymore.
+ if (Engine.currentChar == Engine.newLineChar)
+ {
+ Parent.Push(Engine.newLineChar);
+ }
+
+ // when a state exits the engine stays on the same line and this
+ // state has to override the Parent.ThisLineIndent.
+ Parent.ThisLineIndent = ThisLineIndent.Clone();
+ }
+ }
+
+ /// <summary>
+ /// Changes the current state of the <see cref="CSharpIndentEngine"/> using the current
+ /// state as the parent for the new one.
+ /// </summary>
+ /// <typeparam name="T">
+ /// The type of the new state. Must be assignable from <see cref="IndentState"/>.
+ /// </typeparam>
+ public void ChangeState<T>()
+ where T : IndentState, new ()
+ {
+ var t = new T();
+ t.Initialize(Engine, Engine.currentState);
+ Engine.currentState = t;
+ }
+
+ /// <summary>
+ /// Exits this state by setting the current state of the
+ /// <see cref="CSharpIndentEngine"/> to this state's parent.
+ /// </summary>
+ public void ExitState()
+ {
+ OnExit();
+ Engine.currentState = Engine.currentState.Parent ?? new GlobalBodyState(Engine);
+ }
+
+ /// <summary>
+ /// Common logic behind the push method.
+ /// Each state can override this method and implement its own logic.
+ /// </summary>
+ /// <param name="ch">
+ /// The current character that's being pushed.
+ /// </param>
+ public virtual void Push(char ch)
+ {
+ // replace ThisLineIndent with NextLineIndent if the newLineChar is pushed
+ if (ch == Engine.newLineChar)
+ {
+ var delta = Engine.options.GetOption(FormattingOptions.IndentationSize, LanguageNames.CSharp);
+ while (NextLineIndent.CurIndent - ThisLineIndent.CurIndent > delta &&
+ NextLineIndent.PopIf(IndentType.Continuation));
+ ThisLineIndent = NextLineIndent.Clone();
+
+ }
+ }
+
+ /// <summary>
+ /// When derived, checks if the given sequence of chars form
+ /// a valid keyword or variable name, depending on the state.
+ /// </summary>
+ /// <param name="keyword">
+ /// A possible keyword.
+ /// </param>
+ public virtual void CheckKeyword(string keyword)
+ { }
+
+ /// <summary>
+ /// When derived, checks if the given sequence of chars form
+ /// a valid keyword or variable name, depending on the state.
+ /// </summary>
+ /// <param name="keyword">
+ /// A possible keyword.
+ /// </param>
+ /// <remarks>
+ /// This method should be called from <see cref="Push(char)"/>.
+ /// It is left to derived classes to call this method because of
+ /// performance issues.
+ /// </remarks>
+ public virtual void CheckKeywordOnPush(string keyword)
+ { }
+
+ #endregion
+ }
+
+ #endregion
+
+ #region Null state
+
+ /// <summary>
+ /// Null state.
+ /// </summary>
+ /// <remarks>
+ /// Doesn't define any transitions to new states.
+ /// </remarks>
+ public class NullState : IndentState
+ {
+ public NullState()
+ { }
+
+ public NullState(NullState prototype, CSharpIndentEngine engine)
+ : base(prototype, engine)
+ { }
+
+ public override void Push(char ch)
+ { }
+
+ public override IndentState Clone(CSharpIndentEngine engine)
+ {
+ return new NullState(this, engine);
+ }
+ }
+
+ #endregion
+
+ #region Brackets body states
+
+ #region Brackets body base
+
+ /// <summary>
+ /// The base for all brackets body states.
+ /// </summary>
+ /// <remarks>
+ /// Represents a block of code between a pair of brackets.
+ /// </remarks>
+ public abstract class BracketsBodyBaseState : IndentState
+ {
+
+ /// <summary>
+ /// When derived in a concrete bracket body state, represents
+ /// the closed bracket character pair.
+ /// </summary>
+ public abstract char ClosedBracket { get; }
+
+ protected BracketsBodyBaseState()
+ { }
+
+ protected BracketsBodyBaseState(BracketsBodyBaseState prototype, CSharpIndentEngine engine)
+ : base(prototype, engine)
+ { }
+
+ public override void Push(char ch)
+ {
+ base.Push(ch);
+ switch (ch) {
+ case '#':
+ if (Engine.isLineStart)
+ ChangeState<PreProcessorState>();
+ break;
+ case '/':
+ if (Engine.previousChar == '/')
+ ChangeState<LineCommentState>();
+ break;
+ case '*':
+ if (Engine.previousChar == '/')
+ ChangeState<MultiLineCommentState>();
+ break;
+ case '"':
+ if (Engine.previousChar == '@')
+ {
+ ChangeState<VerbatimStringState>();
+ }
+ else
+ {
+ ChangeState<StringLiteralState>();
+ }
+ break;
+ case '\'':
+ ChangeState<CharacterState>();
+ break;
+ case '{':
+ ChangeState<BracesBodyState>();
+ break;
+ case '(':
+ ChangeState<ParenthesesBodyState>();
+ break;
+ case '[':
+ ChangeState<SquareBracketsBodyState>();
+ break;
+ default:
+ if (ch == ClosedBracket)
+ ExitState();
+ break;
+ }
+ }
+ }
+
+ #endregion
+
+ #region Braces body state
+
+ /// <summary>
+ /// Braces body state.
+ /// </summary>
+ /// <remarks>
+ /// Represents a block of code between { and }.
+ /// </remarks>
+ public class BracesBodyState : BracketsBodyBaseState
+ {
+ /// <summary>
+ /// Type of the current block body.
+ /// </summary>
+ public Body CurrentBody;
+
+ /// <summary>
+ /// Type of the next block body.
+ /// Same as <see cref="CurrentBody"/> if none of the
+ /// <see cref="Body"/> keywords have been read.
+ /// </summary>
+ public Body NextBody;
+
+ /// <summary>
+ /// Type of the current statement.
+ /// </summary>
+ public Statement CurrentStatement
+ {
+ get
+ {
+ return currentStatement;
+ }
+ set
+ {
+ // clear NestedIfStatementLevels if this statement breaks the sequence
+ if (currentStatement == Statement.None && value != Statement.Else)
+ {
+ NestedIfStatementLevels.Clear();
+ }
+
+ currentStatement = value;
+ }
+ }
+ Statement currentStatement;
+
+ /// <summary>
+ /// Contains indent levels of nested if statements.
+ /// </summary>
+ internal CloneableStack<Indent> NestedIfStatementLevels = new CloneableStack<Indent>();
+
+ /// <summary>
+ /// Contains the indent level of the last statement or body keyword.
+ /// </summary>
+ public Indent LastBlockIndent;
+
+ /// <summary>
+ /// True if the engine is on the right side of the equal operator '='.
+ /// </summary>
+ public bool IsRightHandExpression;
+
+ /// <summary>
+ /// True if the '=' char has been pushed and it's not
+ /// a part of a relational operator (&gt;=, &lt;=, !=, ==).
+ /// </summary>
+ public bool IsEqualCharPushed;
+
+ /// <summary>
+ /// The indentation of the previous line.
+ /// </summary>
+ public int PreviousLineIndent;
+
+ /// <summary>
+ /// True if the dot member (e.g. method invocation) indentation has
+ /// been handled in the current statement.
+ /// </summary>
+ public bool IsMemberReferenceDotHandled;
+
+ public override char ClosedBracket
+ {
+ get { return '}'; }
+ }
+
+ public BracesBodyState()
+ {
+ }
+
+ public BracesBodyState(BracesBodyState prototype, CSharpIndentEngine engine)
+ : base(prototype, engine)
+ {
+ CurrentBody = prototype.CurrentBody;
+ NextBody = prototype.NextBody;
+ CurrentStatement = prototype.CurrentStatement;
+ NestedIfStatementLevels = prototype.NestedIfStatementLevels.Clone();
+ IsRightHandExpression = prototype.IsRightHandExpression;
+ IsEqualCharPushed = prototype.IsEqualCharPushed;
+ IsMemberReferenceDotHandled = prototype.IsMemberReferenceDotHandled;
+ LastBlockIndent = prototype.LastBlockIndent;
+ PreviousLineIndent = prototype.PreviousLineIndent;
+ }
+
+ public override void Push(char ch)
+ {
+ // handle IsRightHandExpression property
+ if (IsEqualCharPushed)
+ {
+ if (IsRightHandExpression)
+ {
+ if (ch == Engine.newLineChar)
+ {
+ NextLineIndent.RemoveAlignment();
+ NextLineIndent.Push(IndentType.Continuation);
+ }
+ }
+ // ignore "==" and "=>" operators
+ else if (ch != '=' && ch != '>')
+ {
+ IsRightHandExpression = true;
+
+ if (ch == Engine.newLineChar)
+ {
+ NextLineIndent.Push(IndentType.Continuation);
+ }
+ else
+ {
+ NextLineIndent.SetAlignment(Engine.column - NextLineIndent.CurIndent);
+ }
+ }
+
+ IsEqualCharPushed = ch == ' ' || ch == '\t';
+ }
+
+ if (ch == ';' || (ch == ',' && IsRightHandExpression))
+ {
+ OnStatementExit();
+ }
+ else if (ch == '=' && !(Engine.previousChar == '=' || Engine.previousChar == '<' || Engine.previousChar == '>' || Engine.previousChar == '!'))
+ {
+ IsEqualCharPushed = true;
+ }
+ else if (ch == '.' && !IsMemberReferenceDotHandled)
+ {
+ // OPTION: CSharpFormattingOptions.AlignToMemberReferenceDot
+ if (true /*Engine.options.AlignToMemberReferenceDot*/ && !Engine.isLineStart)
+ {
+ IsMemberReferenceDotHandled = true;
+ NextLineIndent.RemoveAlignment();
+ NextLineIndent.SetAlignment(Engine.column - NextLineIndent.CurIndent - 1, true);
+ }
+ else if (Engine.isLineStart)
+ {
+ IsMemberReferenceDotHandled = true;
+
+ ThisLineIndent.RemoveAlignment();
+ while (ThisLineIndent.CurIndent > PreviousLineIndent &&
+ ThisLineIndent.PopIf(IndentType.Continuation)) ;
+ ThisLineIndent.Push(IndentType.Continuation);
+ NextLineIndent = ThisLineIndent.Clone();
+ }
+ }
+ else if (ch == ':' && Engine.isLineStart && !IsRightHandExpression)
+ {
+ // try to capture ': base(...)', ': this(...)' and inherit statements when they are on a new line
+ ThisLineIndent.Push(IndentType.Continuation);
+ }
+ else if (ch == Engine.newLineChar)
+ {
+ PreviousLineIndent = ThisLineIndent.CurIndent;
+ }
+
+ if (Engine.wordToken.ToString() == "else")
+ {
+ CheckKeywordOnPush("else");
+ }
+
+ base.Push(ch);
+ }
+
+ public override void InitializeState()
+ {
+ ThisLineIndent = Parent.ThisLineIndent.Clone();
+ NextLineIndent = Parent.NextLineIndent.Clone();
+
+ // OPTION: IDocumentIndentEngine.EnableCustomIndentLevels
+ var parent = Parent as BracesBodyState;
+ if (parent == null || parent.LastBlockIndent == null || !Engine.EnableCustomIndentLevels)
+ {
+ NextLineIndent.RemoveAlignment();
+ NextLineIndent.PopIf(IndentType.Continuation);
+ }
+ else
+ {
+ NextLineIndent = parent.LastBlockIndent.Clone();
+ }
+
+ if (Engine.isLineStart)
+ {
+ NextLineIndent.RemoveAlignment();
+ NextLineIndent.PopIf(IndentType.Continuation);
+
+ ThisLineIndent = NextLineIndent.Clone();
+ }
+
+ CurrentBody = extractBody(Parent);
+ NextBody = Body.None;
+ CurrentStatement = Statement.None;
+
+ AddIndentation(CurrentBody);
+ }
+
+ public override IndentState Clone(CSharpIndentEngine engine)
+ {
+ return new BracesBodyState(this, engine);
+ }
+
+ public override void OnExit()
+ {
+ if (Parent is BracesBodyState)
+ {
+ ((BracesBodyState)Parent).OnStatementExit();
+ }
+
+ if (Engine.isLineStart)
+ {
+ ThisLineIndent.RemoveAlignment();
+ ThisLineIndent.PopTry();
+ /*BraceStyle style;
+ if (TryGetBraceStyle(this.CurrentBody, out style)) {
+ if (style == BraceStyle.NextLineShifted ||
+ style == BraceStyle.NextLineShifted2||
+ style == BraceStyle.BannerStyle) {
+ ThisLineIndent.Push(IndentType.Block);
+ }
+ }*/
+ }
+
+ base.OnExit();
+ }
+
+ /// <summary>
+ /// Actions performed when the current statement exits.
+ /// </summary>
+ public virtual void OnStatementExit()
+ {
+ IsRightHandExpression = false;
+ IsMemberReferenceDotHandled = false;
+
+ NextLineIndent.RemoveAlignment();
+ NextLineIndent.PopWhile(IndentType.Continuation);
+
+ CurrentStatement = Statement.None;
+ NextBody = Body.None;
+ LastBlockIndent = null;
+ }
+
+ #region Helpers
+
+ /// <summary>
+ /// Types of braces bodies.
+ /// </summary>
+ public enum Body
+ {
+ None,
+ Namespace,
+ Class,
+ Struct,
+ Interface,
+ Enum,
+ Switch,
+ Case,
+ Try,
+ Catch,
+ Finally
+ }
+
+ /// <summary>
+ /// Types of statements.
+ /// </summary>
+ public enum Statement
+ {
+ None,
+ If,
+ Else,
+ Do,
+ While,
+ For,
+ Foreach,
+ Lock,
+ Using,
+ Return
+ }
+
+ static readonly Dictionary<string, Body> bodies = new Dictionary<string, Body>
+ {
+ { "namespace", Body.Namespace },
+ { "class", Body.Class },
+ { "struct", Body.Struct },
+ { "interface", Body.Interface },
+ { "enum", Body.Enum },
+ { "switch", Body.Switch },
+ { "try", Body.Try },
+ { "catch", Body.Catch },
+ { "finally", Body.Finally },
+ };
+
+ static readonly Dictionary<string, Statement> statements = new Dictionary<string, Statement>
+ {
+ { "if", Statement.If },
+ // { "else", Statement.Else }, // should be handled in CheckKeywordAtPush
+ { "do", Statement.Do },
+ { "while", Statement.While },
+ { "for", Statement.For },
+ { "foreach", Statement.Foreach },
+ { "lock", Statement.Lock },
+ { "using", Statement.Using },
+ { "return", Statement.Return },
+ };
+
+ static readonly HashSet<string> blocks = new HashSet<string>
+ {
+ "namespace",
+ "class",
+ "struct",
+ "interface",
+ "enum",
+ "switch",
+ "try",
+ "catch",
+ "finally",
+ "if",
+ "else",
+ "do",
+ "while",
+ "for",
+ "foreach",
+ "lock",
+ "using",
+ };
+
+ readonly string[] caseDefaultKeywords = {
+ "case",
+ "default"
+ };
+
+ readonly string[] classStructKeywords = {
+ "class",
+ "struct"
+ };
+
+ /// <summary>
+ /// Checks if the given string is a keyword and sets the
+ /// <see cref="NextBody"/> and the <see cref="CurrentStatement"/>
+ /// variables appropriately.
+ /// </summary>
+ /// <param name="keyword">
+ /// A possible keyword.
+ /// </param>
+ /// <remarks>
+ /// This method is called from <see cref="Push(char)"/>
+ /// </remarks>
+ public override void CheckKeywordOnPush(string keyword)
+ {
+ if (keyword == "else")
+ {
+ CurrentStatement = Statement.Else;
+
+ // OPTION: CSharpFormattingOptions.AlignElseInIfStatements
+ if (true && NestedIfStatementLevels.Count > 0)
+ {
+ ThisLineIndent = NestedIfStatementLevels.Pop().Clone();
+ NextLineIndent = ThisLineIndent.Clone();
+ }
+
+ NextLineIndent.Push(IndentType.Continuation);
+ }
+
+ if (blocks.Contains(keyword) && Engine.NeedsReindent)
+ {
+ LastBlockIndent = Indent.ConvertFrom(Engine.CurrentIndent, ThisLineIndent, Engine.options);
+ }
+ }
+
+ /// <summary>
+ /// Checks if the given string is a keyword and sets the
+ /// <see cref="NextBody"/> and the <see cref="CurrentStatement"/>
+ /// variables appropriately.
+ /// </summary>
+ /// <param name="keyword">
+ /// A possible keyword.
+ /// </param>
+ public override void CheckKeyword(string keyword)
+ {
+ if (bodies.ContainsKey(keyword))
+ {
+ var isKeywordTemplateConstraint =
+ classStructKeywords.Contains(keyword) &&
+ (NextBody == Body.Class || NextBody == Body.Struct || NextBody == Body.Interface);
+
+ if (!isKeywordTemplateConstraint)
+ {
+ NextBody = bodies[keyword];
+ }
+ }
+ else if (caseDefaultKeywords.Contains(keyword) && CurrentBody == Body.Switch && Engine.isLineStartBeforeWordToken)
+ {
+ ChangeState<SwitchCaseState>();
+ }
+ else if (keyword == "where" && Engine.isLineStartBeforeWordToken)
+ {
+ // try to capture where (generic type constraint)
+ ThisLineIndent.Push(IndentType.Continuation);
+ }
+ else if (statements.ContainsKey(keyword))
+ {
+ Statement previousStatement = CurrentStatement;
+ CurrentStatement = statements[keyword];
+
+ // return if this is a using declaration or alias
+ if (CurrentStatement == Statement.Using &&
+ (this is GlobalBodyState || CurrentBody == Body.Namespace))
+ {
+ return;
+ }
+ // OPTION: CSharpFormattingOptions.AlignEmbeddedIfStatements
+ if (true /*Engine.options.AlignEmbeddedStatements*/ &&
+ previousStatement == Statement.If &&
+ CurrentStatement == Statement.If)
+ {
+ ThisLineIndent.PopIf(IndentType.Continuation);
+ NextLineIndent.PopIf(IndentType.Continuation);
+ }
+
+ // OPTION: CSharpFormattingOptions.AlignEmbeddedStatements
+ if (true /*Engine.options.AlignEmbeddedStatements*/ &&
+ previousStatement == Statement.Lock &&
+ CurrentStatement == Statement.Lock)
+ {
+ ThisLineIndent.PopIf(IndentType.Continuation);
+ NextLineIndent.PopIf(IndentType.Continuation);
+ }
+
+ // OPTION: CSharpFormattingOptions.AlignEmbeddedUsingStatements
+ if (true /*Engine.options.AlignEmbeddedStatements*/ &&
+ previousStatement == Statement.Using &&
+ CurrentStatement == Statement.Using)
+ {
+ ThisLineIndent.PopIf(IndentType.Continuation);
+ NextLineIndent.PopIf(IndentType.Continuation);
+ }
+
+ // only add continuation for 'else' in 'else if' statement.
+ if (!(CurrentStatement == Statement.If && previousStatement == Statement.Else && !Engine.isLineStartBeforeWordToken))
+ {
+ NextLineIndent.Push(IndentType.Continuation);
+ }
+
+ if (CurrentStatement == Statement.If)
+ {
+ NestedIfStatementLevels.Push(ThisLineIndent);
+ }
+ }
+
+ if (blocks.Contains(keyword) && Engine.NeedsReindent)
+ {
+ LastBlockIndent = Indent.ConvertFrom(Engine.CurrentIndent, ThisLineIndent, Engine.options);
+ }
+ }
+
+// /// <summary>
+// /// Pushes a new level of indentation depending on the given
+// /// <paramref name="braceStyle"/>.
+// /// </summary>
+// void AddIndentation(BraceStyle braceStyle)
+// {
+// switch (braceStyle)
+// {
+// case BraceStyle.NextLineShifted:
+// ThisLineIndent.Push(IndentType.Block);
+// NextLineIndent.Push(IndentType.Block);
+// break;
+// case BraceStyle.DoNotChange:
+// case BraceStyle.EndOfLine:
+// case BraceStyle.EndOfLineWithoutSpace:
+// case BraceStyle.NextLine:
+// case BraceStyle.BannerStyle:
+// NextLineIndent.Push(IndentType.Block);
+// break;
+// case BraceStyle.NextLineShifted2:
+// ThisLineIndent.Push(IndentType.Block);
+// NextLineIndent.Push(IndentType.DoubleBlock);
+// break;
+// }
+// }
+
+// bool TryGetBraceStyle (Body body, out BraceStyle style)
+// {
+// style = BraceStyle.DoNotChange;
+// switch (body)
+// {
+// case Body.None:
+// if (!Engine.options.IndentBlocks)
+// return false;
+// style = Engine.options.StatementBraceStyle;
+// return true;
+// case Body.Namespace:
+// if (!Engine.options.IndentNamespaceBody)
+// return false;
+// style = Engine.options.NamespaceBraceStyle;
+// return true;
+// case Body.Class:
+// if (!Engine.options.IndentClassBody)
+// return false;
+// style = Engine.options.ClassBraceStyle;
+// return true;
+// case Body.Struct:
+// if (!Engine.options.IndentStructBody)
+// return false;
+// style = Engine.options.StructBraceStyle;
+// return true;
+// case Body.Interface:
+// if (!Engine.options.IndentInterfaceBody)
+// return false;
+// style = Engine.options.InterfaceBraceStyle;
+// return true;
+// case Body.Enum:
+// if (!Engine.options.IndentEnumBody)
+// return false;
+// style = Engine.options.EnumBraceStyle;
+// return true;
+// case Body.Switch:
+// if (!Engine.options.IndentSwitchBody)
+// return false;
+// style = Engine.options.StatementBraceStyle;
+// return true;
+// case Body.Try:
+// case Body.Catch:
+// case Body.Finally:
+// style = Engine.options.StatementBraceStyle;
+// return true;
+// }
+// return false;
+// }
+
+ /// <summary>
+ /// Pushes a new level of indentation depending on the given
+ /// <paramref name="body"/>.
+ /// </summary>
+ void AddIndentation(Body body)
+ {
+ NextLineIndent.Push(IndentType.Block);
+
+// BraceStyle style;
+// if (TryGetBraceStyle (body, out style)) {
+// AddIndentation(style);
+// } else {
+// NextLineIndent.Push(IndentType.Empty);
+// }
+ }
+
+ /// <summary>
+ /// Extracts the <see cref="CurrentBody"/> from the given state.
+ /// </summary>
+ /// <returns>
+ /// The correct <see cref="Body"/> type for this state.
+ /// </returns>
+ static Body extractBody(IndentState state)
+ {
+ if (state != null && state is BracesBodyState)
+ {
+ return ((BracesBodyState)state).NextBody;
+ }
+
+ return Body.None;
+ }
+
+ #endregion
+ }
+
+ #endregion
+
+ #region Global body state
+
+ /// <summary>
+ /// Global body state.
+ /// </summary>
+ /// <remarks>
+ /// Represents the global space of the program.
+ /// </remarks>
+ public class GlobalBodyState : BracesBodyState
+ {
+ public override char ClosedBracket
+ {
+ get { return '\0'; }
+ }
+
+ public GlobalBodyState()
+ { }
+
+
+ public GlobalBodyState(CSharpIndentEngine engine)
+ {
+ Initialize (engine, null);
+ }
+
+ public GlobalBodyState(GlobalBodyState prototype, CSharpIndentEngine engine)
+ : base(prototype, engine)
+ { }
+
+ public override IndentState Clone(CSharpIndentEngine engine)
+ {
+ return new GlobalBodyState(this, engine);
+ }
+
+ public override void InitializeState()
+ {
+ ThisLineIndent = new Indent(Engine.options);
+ NextLineIndent = ThisLineIndent.Clone();
+ }
+ }
+
+ #endregion
+
+ #region Switch-case body state
+
+ /// <summary>
+ /// Switch-case statement state.
+ /// </summary>
+ /// <remarks>
+ /// Represents the block of code in one switch case (including default).
+ /// </remarks>
+ public class SwitchCaseState : BracesBodyState
+ {
+ public SwitchCaseState()
+ { }
+
+ public SwitchCaseState(SwitchCaseState prototype, CSharpIndentEngine engine)
+ : base(prototype, engine)
+ { }
+
+ public override void Push(char ch)
+ {
+ // on ClosedBracket both this state (a case or a default statement)
+ // and also the whole switch block (handled in the base class) must exit.
+ if (ch == ClosedBracket)
+ {
+ ExitState();
+ if (Parent is BracesBodyState)
+ Parent.OnExit();
+ }
+
+ base.Push(ch);
+ }
+
+ public override void InitializeState()
+ {
+ ThisLineIndent = Parent.ThisLineIndent.Clone();
+ NextLineIndent = ThisLineIndent.Clone();
+
+ // remove all continuations and extra spaces
+ ThisLineIndent.RemoveAlignment();
+ ThisLineIndent.PopWhile(IndentType.Continuation);
+
+ NextLineIndent.RemoveAlignment();
+ NextLineIndent.PopWhile(IndentType.Continuation);
+
+
+ if (Engine.options.GetOption(CSharpFormattingOptions.IndentSwitchCaseSection))
+ {
+ NextLineIndent.Push(IndentType.Block);
+ }
+ else
+ {
+ NextLineIndent.Push(IndentType.Empty);
+ }
+ }
+
+ static readonly string[] caseDefaultKeywords = {
+ "case",
+ "default"
+ };
+
+ static readonly string[] breakContinueReturnGotoKeywords = {
+ "break",
+ "continue",
+ "return",
+ "goto"
+ };
+
+ public override void CheckKeyword(string keyword)
+ {
+ if (caseDefaultKeywords.Contains(keyword) && Engine.isLineStartBeforeWordToken)
+ {
+ ExitState();
+ ChangeState<SwitchCaseState>();
+ }
+ else if (breakContinueReturnGotoKeywords.Contains(keyword) && Engine.isLineStartBeforeWordToken)
+ {
+ // OPTION: Engine.formattingOptions.IndentBreakStatements
+ if (true/*!Engine.options.IndentBreakStatements*/)
+ {
+ ThisLineIndent = Parent.ThisLineIndent.Clone();
+ }
+ }
+
+ base.CheckKeyword(keyword);
+ }
+
+
+ public override void OnExit()
+ {
+ //Parent.OnExit();
+ }
+
+ public override IndentState Clone(CSharpIndentEngine engine)
+ {
+ return new SwitchCaseState(this, engine);
+ }
+ }
+
+ #endregion
+
+ #region Parentheses body state
+
+ /// <summary>
+ /// Parentheses body state.
+ /// </summary>
+ /// <remarks>
+ /// Represents a block of code between ( and ).
+ /// </remarks>
+ public class ParenthesesBodyState : BracketsBodyBaseState
+ {
+ /// <summary>
+ /// True if any char has been pushed.
+ /// </summary>
+ public bool IsSomethingPushed;
+
+ public override char ClosedBracket
+ {
+ get { return ')'; }
+ }
+
+ public ParenthesesBodyState()
+ { }
+
+ public ParenthesesBodyState(ParenthesesBodyState prototype, CSharpIndentEngine engine)
+ : base(prototype, engine)
+ {
+ IsSomethingPushed = prototype.IsSomethingPushed;
+ }
+
+ public override void Push(char ch)
+ {
+ if (ch == Engine.newLineChar)
+ {
+ if (!Engine.options.GetOption(CSharpFormattingOptions.NewLinesForBracesInAnonymousMethods)) {
+ if (NextLineIndent.PopIf(IndentType.Continuation)) {
+ NextLineIndent.Push(IndentType.Block);
+ }
+ }
+ }
+ else if (!IsSomethingPushed)
+ {
+ // OPTION: CSharpFormattingOptions.AlignToFirstMethodCallArgument
+ if (true /* Engine.options.AlignToFirstMethodCallArgument*/)
+ {
+ NextLineIndent.PopTry();
+ // align the next line at the beginning of the open bracket
+ NextLineIndent.ExtraSpaces = Math.Max(0, Engine.column - NextLineIndent.CurIndent - 1);
+ }
+ }
+
+ base.Push(ch);
+ IsSomethingPushed = true;
+ }
+
+ public override void InitializeState()
+ {
+ ThisLineIndent = Parent.ThisLineIndent.Clone();
+ NextLineIndent = ThisLineIndent.Clone();
+ NextLineIndent.Push(IndentType.Continuation);
+ }
+
+ public override IndentState Clone(CSharpIndentEngine engine)
+ {
+ return new ParenthesesBodyState(this, engine);
+ }
+
+ public override void OnExit()
+ {
+ if (Engine.isLineStart)
+ {
+ if (ThisLineIndent.ExtraSpaces > 0)
+ {
+ ThisLineIndent.ExtraSpaces--;
+ }
+ else
+ {
+ ThisLineIndent.PopTry();
+ }
+ }
+
+ base.OnExit();
+ }
+ }
+
+ #endregion
+
+ #region Square brackets body state
+
+ /// <summary>
+ /// Square brackets body state.
+ /// </summary>
+ /// <remarks>
+ /// Represents a block of code between [ and ].
+ /// </remarks>
+ public class SquareBracketsBodyState : BracketsBodyBaseState
+ {
+ /// <summary>
+ /// True if any char has been pushed.
+ /// </summary>
+ public bool IsSomethingPushed;
+
+ public override char ClosedBracket
+ {
+ get { return ']'; }
+ }
+
+ public SquareBracketsBodyState()
+ { }
+
+ public SquareBracketsBodyState(SquareBracketsBodyState prototype, CSharpIndentEngine engine)
+ : base(prototype, engine)
+ {
+ IsSomethingPushed = prototype.IsSomethingPushed;
+ }
+
+ public override void Push(char ch)
+ {
+ if (ch == Engine.newLineChar)
+ {
+ if (NextLineIndent.PopIf(IndentType.Continuation))
+ {
+ NextLineIndent.Push(IndentType.Block);
+ }
+ }
+ else if (!IsSomethingPushed)
+ {
+ // OPTION: CSharpFormattingOptions.AlignToFirstIndexerArgument
+ if (true /*Engine.options.AlignToFirstIndexerArgument*/)
+ {
+ NextLineIndent.PopTry();
+ // align the next line at the beginning of the open bracket
+ NextLineIndent.ExtraSpaces = Math.Max(0, Engine.column - NextLineIndent.CurIndent - 1);
+ }
+ }
+
+ base.Push(ch);
+ IsSomethingPushed = true;
+ }
+
+ public override void InitializeState()
+ {
+ ThisLineIndent = Parent.ThisLineIndent.Clone();
+ NextLineIndent = ThisLineIndent.Clone();
+ NextLineIndent.Push(IndentType.Continuation);
+ }
+
+ public override IndentState Clone(CSharpIndentEngine engine)
+ {
+ return new SquareBracketsBodyState(this, engine);
+ }
+
+ public override void OnExit()
+ {
+ if (Engine.isLineStart)
+ {
+ if (ThisLineIndent.ExtraSpaces > 0)
+ {
+ ThisLineIndent.ExtraSpaces--;
+ }
+ else
+ {
+ ThisLineIndent.PopTry();
+ }
+ }
+
+ base.OnExit();
+ }
+ }
+
+ #endregion
+
+ #endregion
+
+ #region PreProcessor state
+
+ /// <summary>
+ /// PreProcessor directive state.
+ /// </summary>
+ /// <remarks>
+ /// Activated when the '#' char is pushed and the
+ /// <see cref="CSharpIndentEngine.isLineStart"/> is true.
+ /// </remarks>
+ public class PreProcessorState : IndentState
+ {
+ /// <summary>
+ /// The type of the preprocessor directive.
+ /// </summary>
+ public PreProcessorDirective DirectiveType;
+
+ /// <summary>
+ /// If <see cref="DirectiveType"/> is set (not equal to 'None'), this
+ /// stores the expression of the directive.
+ /// </summary>
+ public StringBuilder DirectiveStatement;
+
+ public PreProcessorState()
+ {
+ DirectiveType = PreProcessorDirective.None;
+ DirectiveStatement = new StringBuilder();
+ }
+
+ public PreProcessorState(PreProcessorState prototype, CSharpIndentEngine engine)
+ : base(prototype, engine)
+ {
+ DirectiveType = prototype.DirectiveType;
+ DirectiveStatement = new StringBuilder(prototype.DirectiveStatement.ToString());
+ }
+
+ public override void Push(char ch)
+ {
+ // HACK: if this change would be left for the CheckKeyword method, we will lose
+ // it if the next pushed char is newLineChar since ThisLineIndent will be
+ // immediately replaced with NextLineIndent. As this most likely will
+ // happen, we check for "endregion" on every push.
+ if (Engine.wordToken.ToString() == "endregion")
+ {
+ CheckKeywordOnPush("endregion");
+ }
+
+ base.Push(ch);
+
+ if (DirectiveType != PreProcessorDirective.None)
+ {
+ DirectiveStatement.Append(ch);
+ }
+
+ if (ch == Engine.newLineChar)
+ {
+ ExitState();
+ switch (DirectiveType)
+ {
+ case PreProcessorDirective.If:
+ Engine.ifDirectiveEvalResults.Push(eval(DirectiveStatement.ToString()));
+ if (Engine.ifDirectiveEvalResults.Peek())
+ {
+ // the if/elif directive is true -> continue with the previous state
+ }
+ else
+ {
+ // the if/elif directive is false -> change to a state that will
+ // ignore any chars until #endif or #elif
+ ChangeState<PreProcessorCommentState>();
+ }
+ break;
+ case PreProcessorDirective.Elif:
+ if (Engine.ifDirectiveEvalResults.Count > 0)
+ {
+ if (!Engine.ifDirectiveEvalResults.Peek())
+ {
+ ExitState();
+ Engine.ifDirectiveEvalResults.Pop();
+ goto case PreProcessorDirective.If;
+ }
+ }
+ // previous if was true -> comment
+ ChangeState<PreProcessorCommentState>();
+ break;
+ case PreProcessorDirective.Else:
+ if (Engine.ifDirectiveEvalResults.Count > 0 && Engine.ifDirectiveEvalResults.Peek())
+ {
+ // some if/elif directive was true -> change to a state that will
+ // ignore any chars until #endif
+ ChangeState<PreProcessorCommentState>();
+ }
+ else
+ {
+ // none if/elif directives were true -> exit comment state.
+ if (Engine.currentState is PreProcessorCommentState)
+ ExitState();
+ }
+ break;
+ case PreProcessorDirective.Define:
+ var defineSymbol = DirectiveStatement.ToString().Trim();
+ if (!Engine.conditionalSymbols.Contains(defineSymbol))
+ {
+ Engine.conditionalSymbols.Add(defineSymbol);
+ }
+ break;
+ case PreProcessorDirective.Undef:
+ var undefineSymbol = DirectiveStatement.ToString().Trim();
+ if (Engine.conditionalSymbols.Contains(undefineSymbol))
+ {
+ Engine.conditionalSymbols.Remove(undefineSymbol);
+ }
+ break;
+ case PreProcessorDirective.Endif:
+ // marks the end of this block
+ if (Engine.currentState is PreProcessorCommentState)
+ ExitState();
+ Engine.ifDirectiveEvalResults.Pop();
+ Engine.ifDirectiveIndents.Pop();
+ break;
+ case PreProcessorDirective.Region:
+ case PreProcessorDirective.Pragma:
+ case PreProcessorDirective.Warning:
+ case PreProcessorDirective.Error:
+ case PreProcessorDirective.Line:
+ // continue with the previous state
+ break;
+ }
+ }
+ }
+
+ public override void InitializeState()
+ {
+ // OPTION: IndentPreprocessorDirectives
+ if (true /*Engine.options.IndentPreprocessorDirectives*/)
+ {
+ if (Engine.ifDirectiveIndents.Count > 0)
+ {
+ ThisLineIndent = Engine.ifDirectiveIndents.Peek().Clone();
+ }
+ else
+ {
+ ThisLineIndent = Parent.ThisLineIndent.Clone();
+ }
+ }
+// else
+// {
+// ThisLineIndent = new Indent(Engine.options);
+// }
+
+ NextLineIndent = Parent.NextLineIndent.Clone();
+ }
+
+ static readonly Dictionary<string, PreProcessorDirective> preProcessorDirectives = new Dictionary<string, PreProcessorDirective>
+ {
+ { "if", PreProcessorDirective.If },
+ { "elif", PreProcessorDirective.Elif },
+ { "else", PreProcessorDirective.Else },
+ { "endif", PreProcessorDirective.Endif },
+ { "region", PreProcessorDirective.Region },
+ { "endregion", PreProcessorDirective.Endregion },
+ { "pragma", PreProcessorDirective.Pragma },
+ { "warning", PreProcessorDirective.Warning },
+ { "error", PreProcessorDirective.Error },
+ { "line", PreProcessorDirective.Line },
+ { "define", PreProcessorDirective.Define },
+ { "undef", PreProcessorDirective.Undef }
+ };
+
+ public override void CheckKeywordOnPush(string keyword)
+ {
+ if (keyword == "endregion")
+ {
+ DirectiveType = PreProcessorDirective.Endregion;
+ ThisLineIndent = Parent.NextLineIndent.Clone();
+ }
+ }
+
+ public override void CheckKeyword(string keyword)
+ {
+ // check if the directive type has already been set
+ if (DirectiveType != PreProcessorDirective.None)
+ {
+ return;
+ }
+
+ if (preProcessorDirectives.ContainsKey(keyword))
+ {
+ DirectiveType = preProcessorDirectives[keyword];
+
+ // adjust the indentation for the region directive
+ if (DirectiveType == PreProcessorDirective.Region)
+ {
+ ThisLineIndent = Parent.NextLineIndent.Clone();
+ }
+ else if (DirectiveType == PreProcessorDirective.If)
+ {
+ Engine.ifDirectiveIndents.Push(ThisLineIndent.Clone());
+ }
+ }
+ }
+
+ public override IndentState Clone(CSharpIndentEngine engine)
+ {
+ return new PreProcessorState(this, engine);
+ }
+
+ /// <summary>
+ /// Types of preprocessor directives.
+ /// </summary>
+ public enum PreProcessorDirective
+ {
+ None,
+ If,
+ Elif,
+ Else,
+ Endif,
+ Region,
+ Endregion,
+ Pragma,
+ Warning,
+ Error,
+ Line,
+ Define,
+ Undef
+ }
+
+ #region Pre processor evaluation (from cs-tokenizer.cs)
+
+ static bool is_identifier_start_character(int c)
+ {
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || Char.IsLetter((char)c);
+ }
+
+ static bool is_identifier_part_character(char c)
+ {
+ if (c >= 'a' && c <= 'z')
+ return true;
+
+ if (c >= 'A' && c <= 'Z')
+ return true;
+
+ if (c == '_' || (c >= '0' && c <= '9'))
+ return true;
+
+ if (c < 0x80)
+ return false;
+
+ return Char.IsLetter(c) || Char.GetUnicodeCategory(c) == UnicodeCategory.ConnectorPunctuation;
+ }
+
+ bool eval_val(string s)
+ {
+ if (s == "true")
+ return true;
+ if (s == "false")
+ return false;
+
+ return Engine.conditionalSymbols != null && Engine.conditionalSymbols.Contains(s) ||
+ Engine.customConditionalSymbols != null && Engine.customConditionalSymbols.Contains(s);
+ }
+
+ bool pp_primary(ref string s)
+ {
+ s = s.Trim();
+ int len = s.Length;
+
+ if (len > 0)
+ {
+ char c = s[0];
+
+ if (c == '(')
+ {
+ s = s.Substring(1);
+ bool val = pp_expr(ref s, false);
+ if (s.Length > 0 && s[0] == ')')
+ {
+ s = s.Substring(1);
+ return val;
+ }
+ return false;
+ }
+
+ if (is_identifier_start_character(c))
+ {
+ int j = 1;
+
+ while (j < len)
+ {
+ c = s[j];
+
+ if (is_identifier_part_character(c))
+ {
+ j++;
+ continue;
+ }
+ bool v = eval_val(s.Substring(0, j));
+ s = s.Substring(j);
+ return v;
+ }
+ bool vv = eval_val(s);
+ s = "";
+ return vv;
+ }
+ }
+ return false;
+ }
+
+ bool pp_unary(ref string s)
+ {
+ s = s.Trim();
+ int len = s.Length;
+
+ if (len > 0)
+ {
+ if (s[0] == '!')
+ {
+ if (len > 1 && s[1] == '=')
+ {
+ return false;
+ }
+ s = s.Substring(1);
+ return !pp_primary(ref s);
+ }
+ else
+ return pp_primary(ref s);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ bool pp_eq(ref string s)
+ {
+ bool va = pp_unary(ref s);
+
+ s = s.Trim();
+ int len = s.Length;
+ if (len > 0)
+ {
+ if (s[0] == '=')
+ {
+ if (len > 2 && s[1] == '=')
+ {
+ s = s.Substring(2);
+ return va == pp_unary(ref s);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else if (s[0] == '!' && len > 1 && s[1] == '=')
+ {
+ s = s.Substring(2);
+
+ return va != pp_unary(ref s);
+
+ }
+ }
+
+ return va;
+
+ }
+
+ bool pp_and(ref string s)
+ {
+ bool va = pp_eq(ref s);
+
+ s = s.Trim();
+ int len = s.Length;
+ if (len > 0)
+ {
+ if (s[0] == '&')
+ {
+ if (len > 2 && s[1] == '&')
+ {
+ s = s.Substring(2);
+ return (va & pp_and(ref s));
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+ return va;
+ }
+
+ //
+ // Evaluates an expression for `#if' or `#elif'
+ //
+ bool pp_expr(ref string s, bool isTerm)
+ {
+ bool va = pp_and(ref s);
+ s = s.Trim();
+ int len = s.Length;
+ if (len > 0)
+ {
+ char c = s[0];
+
+ if (c == '|')
+ {
+ if (len > 2 && s[1] == '|')
+ {
+ s = s.Substring(2);
+ return va | pp_expr(ref s, isTerm);
+ }
+ else
+ {
+
+ return false;
+ }
+ }
+ if (isTerm)
+ {
+ return false;
+ }
+ }
+
+ return va;
+ }
+
+ bool eval(string s)
+ {
+ bool v = pp_expr(ref s, true);
+ s = s.Trim();
+ if (s.Length != 0)
+ {
+ return false;
+ }
+
+ return v;
+ }
+
+ #endregion
+ }
+
+ #endregion
+
+ #region PreProcessorComment state
+
+ /// <summary>
+ /// PreProcessor comment state.
+ /// </summary>
+ /// <remarks>
+ /// Activates when the #if or #elif directive is false and ignores
+ /// all pushed chars until the next '#'.
+ /// </remarks>
+ public class PreProcessorCommentState : IndentState
+ {
+ public PreProcessorCommentState()
+ { }
+
+ public PreProcessorCommentState(PreProcessorCommentState prototype, CSharpIndentEngine engine)
+ : base(prototype, engine)
+ { }
+
+ public override void Push(char ch)
+ {
+ base.Push(ch);
+
+ if (ch == '#' && Engine.isLineStart)
+ {
+ ChangeState<PreProcessorState>();
+ }
+ }
+
+ public override void InitializeState()
+ {
+ // OPTION: IndentPreprocessorDirectives
+ if (true/*Engine.options.IndentPreprocessorDirectives*/ &&
+ Engine.ifDirectiveIndents.Count > 0)
+ {
+ ThisLineIndent = Engine.ifDirectiveIndents.Peek().Clone();
+ NextLineIndent = ThisLineIndent.Clone();
+ }
+ else
+ {
+ ThisLineIndent = Parent.NextLineIndent.Clone();
+ NextLineIndent = ThisLineIndent.Clone();
+ }
+ }
+
+ public override IndentState Clone(CSharpIndentEngine engine)
+ {
+ return new PreProcessorCommentState(this, engine);
+ }
+ }
+
+ #endregion
+
+ #region LineComment state
+
+ /// <summary>
+ /// Single-line comment state.
+ /// </summary>
+ public class LineCommentState : IndentState
+ {
+ /// <summary>
+ /// It's possible that this should be the DocComment state:
+ /// check if the first next pushed char is equal to '/'.
+ /// </summary>
+ public bool CheckForDocComment = true;
+
+ public LineCommentState()
+ {
+ /* if (engine.formattingOptions.KeepCommentsAtFirstColumn && engine.column == 2)
+ ThisLineIndent.Reset();*/
+ }
+
+ public LineCommentState(LineCommentState prototype, CSharpIndentEngine engine)
+ : base(prototype, engine)
+ {
+ CheckForDocComment = prototype.CheckForDocComment;
+ }
+
+ public override void Push(char ch)
+ {
+ base.Push(ch);
+
+ if (ch == Engine.newLineChar)
+ {
+ // to handle cases like //\n/*
+ // Otherwise line 2 would be treated as line comment.
+ Engine.previousChar = '\0';
+ ExitState();
+ }
+ else if (ch == '/' && CheckForDocComment)
+ {
+ // wrong state, should be DocComment.
+ ExitState();
+ ChangeState<DocCommentState>();
+ }
+
+ CheckForDocComment = false;
+ }
+
+ public override void InitializeState()
+ {
+ ThisLineIndent = Parent.ThisLineIndent.Clone();
+ NextLineIndent = Parent.NextLineIndent.Clone();
+ }
+
+ public override IndentState Clone(CSharpIndentEngine engine)
+ {
+ return new LineCommentState(this, engine);
+ }
+ }
+
+ #endregion
+
+ #region DocComment state
+
+ /// <summary>
+ /// XML documentation comment state.
+ /// </summary>
+ public class DocCommentState : IndentState
+ {
+ public DocCommentState()
+ { }
+
+ public DocCommentState(DocCommentState prototype, CSharpIndentEngine engine)
+ : base(prototype, engine)
+ { }
+
+ public override void Push(char ch)
+ {
+ base.Push(ch);
+
+ if (ch == Engine.newLineChar)
+ {
+ ExitState();
+ }
+ }
+
+ public override void InitializeState()
+ {
+ ThisLineIndent = Parent.ThisLineIndent.Clone();
+ NextLineIndent = Parent.NextLineIndent.Clone();
+ }
+
+ public override IndentState Clone(CSharpIndentEngine engine)
+ {
+ return new DocCommentState(this, engine);
+ }
+ }
+
+ #endregion
+
+ #region MultiLineComment state
+
+ /// <summary>
+ /// Multi-line comment state.
+ /// </summary>
+ public class MultiLineCommentState : IndentState
+ {
+ /// <summary>
+ /// True if any char has been pushed to this state.
+ /// </summary>
+ /// <remarks>
+ /// Needed to resolve an issue when the first pushed char is '/'.
+ /// The state would falsely exit on this sequence of chars '/*/',
+ /// since it only checks if the last two chars are '/' and '*'.
+ /// </remarks>
+ public bool IsAnyCharPushed;
+
+ public MultiLineCommentState()
+ { }
+
+ public MultiLineCommentState(MultiLineCommentState prototype, CSharpIndentEngine engine)
+ : base(prototype, engine)
+ {
+ IsAnyCharPushed = prototype.IsAnyCharPushed;
+ }
+
+ public override void Push(char ch)
+ {
+ base.Push(ch);
+
+ if (ch == '/' && Engine.previousChar == '*' && IsAnyCharPushed)
+ {
+ ExitState();
+ }
+
+ IsAnyCharPushed = true;
+ }
+
+ public override void InitializeState()
+ {
+ ThisLineIndent = Parent.ThisLineIndent.Clone();
+ NextLineIndent = ThisLineIndent.Clone();
+ }
+
+ public override IndentState Clone(CSharpIndentEngine engine)
+ {
+ return new MultiLineCommentState(this, engine);
+ }
+ }
+
+ #endregion
+
+ #region StringLiteral state
+
+ /// <summary>
+ /// StringLiteral state.
+ /// </summary>
+ public class StringLiteralState : IndentState
+ {
+ /// <summary>
+ /// True if the next char is escaped with '\'.
+ /// </summary>
+ public bool IsEscaped;
+
+ public StringLiteralState()
+ { }
+
+ public StringLiteralState(StringLiteralState prototype, CSharpIndentEngine engine)
+ : base(prototype, engine)
+ {
+ IsEscaped = prototype.IsEscaped;
+ }
+
+ public override void Push(char ch)
+ {
+ base.Push(ch);
+
+ if (ch == Engine.newLineChar || (!IsEscaped && ch == '"')) {
+ ExitState();
+ } else {
+ IsEscaped = ch == '\\' && !IsEscaped;
+ }
+ }
+
+ public override void InitializeState()
+ {
+ ThisLineIndent = Parent.ThisLineIndent.Clone();
+ NextLineIndent = Parent.NextLineIndent.Clone();
+ }
+
+ public override IndentState Clone(CSharpIndentEngine engine)
+ {
+ return new StringLiteralState(this, engine);
+ }
+ }
+
+ #endregion
+
+ #region Verbatim string state
+
+ /// <summary>
+ /// Verbatim string state.
+ /// </summary>
+ public class VerbatimStringState : IndentState
+ {
+ /// <summary>
+ /// True if there is an odd number of '"' in a row.
+ /// </summary>
+ public bool IsEscaped;
+
+ public VerbatimStringState()
+ { }
+
+ public VerbatimStringState(VerbatimStringState prototype, CSharpIndentEngine engine)
+ : base(prototype, engine)
+ {
+ IsEscaped = prototype.IsEscaped;
+ }
+
+ public override void Push(char ch)
+ {
+ base.Push(ch);
+
+ if (IsEscaped && ch != '"')
+ {
+ ExitState();
+ // the char has been pushed to the wrong state, push it back
+ Engine.currentState.Push(ch);
+ }
+
+ IsEscaped = ch == '"' && !IsEscaped;
+ }
+
+ public override void InitializeState()
+ {
+ ThisLineIndent = Parent.ThisLineIndent.Clone();
+ NextLineIndent = new Indent(Engine.options);
+ }
+
+ public override IndentState Clone(CSharpIndentEngine engine)
+ {
+ return new VerbatimStringState(this, engine);
+ }
+ }
+
+ #endregion
+
+ #region Character state
+
+ /// <summary>
+ /// Character state.
+ /// </summary>
+ public class CharacterState : IndentState
+ {
+ /// <summary>
+ /// True if the next char is escaped with '\'.
+ /// </summary>
+ public bool IsEscaped;
+
+ public CharacterState()
+ { }
+
+ public CharacterState(CharacterState prototype, CSharpIndentEngine engine)
+ : base(prototype, engine)
+ {
+ IsEscaped = prototype.IsEscaped;
+ }
+
+ public override void Push(char ch)
+ {
+ base.Push(ch);
+
+ if (ch == Engine.newLineChar)
+ {
+ ExitState();
+ }
+ else if (!IsEscaped && ch == '\'')
+ {
+ ExitState();
+ }
+
+ IsEscaped = ch == '\\' && !IsEscaped;
+ }
+
+ public override void InitializeState()
+ {
+ ThisLineIndent = Parent.ThisLineIndent.Clone();
+ NextLineIndent = Parent.NextLineIndent.Clone();
+ }
+
+ public override IndentState Clone(CSharpIndentEngine engine)
+ {
+ return new CharacterState(this, engine);
+ }
+ }
+
+ #endregion
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/NullIStateMachineIndentEngine.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/NullIStateMachineIndentEngine.cs
new file mode 100644
index 0000000000..d4f32a1f80
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/NullIStateMachineIndentEngine.cs
@@ -0,0 +1,207 @@
+//
+// NullIStateMachineIndentEngine.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2013 Xamarin Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+
+namespace ICSharpCode.NRefactory6.CSharp
+{
+ /// <summary>
+ /// An empty IStateMachineIndentEngine implementation that does nothing.
+ /// </summary>
+ public sealed class NullIStateMachineIndentEngine : IStateMachineIndentEngine
+ {
+ int offset;
+
+ public NullIStateMachineIndentEngine()
+ {
+ }
+
+ #region IStateMachineIndentEngine implementation
+ public IStateMachineIndentEngine Clone()
+ {
+ return new NullIStateMachineIndentEngine() { offset = this.offset };
+ }
+
+ bool IStateMachineIndentEngine.IsInsidePreprocessorDirective {
+ get {
+ return false;
+ }
+ }
+
+ bool IStateMachineIndentEngine.IsInsidePreprocessorComment {
+ get {
+ return false;
+ }
+ }
+
+ bool IStateMachineIndentEngine.IsInsideStringLiteral {
+ get {
+ return false;
+ }
+ }
+
+ bool IStateMachineIndentEngine.IsInsideVerbatimString {
+ get {
+ return false;
+ }
+ }
+
+ bool IStateMachineIndentEngine.IsInsideCharacter {
+ get {
+ return false;
+ }
+ }
+
+ bool IStateMachineIndentEngine.IsInsideString {
+ get {
+ return false;
+ }
+ }
+
+ bool IStateMachineIndentEngine.IsInsideLineComment {
+ get {
+ return false;
+ }
+ }
+
+ bool IStateMachineIndentEngine.IsInsideMultiLineComment {
+ get {
+ return false;
+ }
+ }
+
+ bool IStateMachineIndentEngine.IsInsideDocLineComment {
+ get {
+ return false;
+ }
+ }
+
+ bool IStateMachineIndentEngine.IsInsideComment {
+ get {
+ return false;
+ }
+ }
+
+ bool IStateMachineIndentEngine.IsInsideOrdinaryComment {
+ get {
+ return false;
+ }
+ }
+
+ bool IStateMachineIndentEngine.IsInsideOrdinaryCommentOrString {
+ get {
+ return false;
+ }
+ }
+
+ bool IStateMachineIndentEngine.LineBeganInsideVerbatimString {
+ get {
+ return false;
+ }
+ }
+
+ bool IStateMachineIndentEngine.LineBeganInsideMultiLineComment {
+ get {
+ return false;
+ }
+ }
+ #endregion
+
+ #region IDocumentIndentEngine implementation
+ void IDocumentIndentEngine.Push(char ch)
+ {
+ offset++;
+ }
+
+ void IDocumentIndentEngine.Reset()
+ {
+ this.offset = 0;
+ }
+
+ void IDocumentIndentEngine.Update(SourceText sourceText, int offset)
+ {
+ this.offset = offset;
+ }
+
+ IDocumentIndentEngine IDocumentIndentEngine.Clone()
+ {
+ return Clone();
+ }
+
+ string IDocumentIndentEngine.ThisLineIndent {
+ get {
+ return "";
+ }
+ }
+
+ string IDocumentIndentEngine.NextLineIndent {
+ get {
+ return "";
+ }
+ }
+
+ string IDocumentIndentEngine.CurrentIndent {
+ get {
+ return "";
+ }
+ }
+
+ bool IDocumentIndentEngine.NeedsReindent {
+ get {
+ return false;
+ }
+ }
+
+ int IDocumentIndentEngine.Offset {
+ get {
+ return offset;
+ }
+ }
+// TextLocation IDocumentIndentEngine.Location {
+// get {
+// return TextLocation.Empty;
+// }
+// }
+
+ /// <inheritdoc />
+ public bool EnableCustomIndentLevels
+ {
+ get { return false; }
+ set { }
+ }
+
+ #endregion
+
+ #region ICloneable implementation
+ object ICloneable.Clone()
+ {
+ return Clone();
+ }
+ #endregion
+ }
+}
+
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/TextPasteIndentEngine.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/TextPasteIndentEngine.cs
new file mode 100644
index 0000000000..221a440012
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/TextPasteIndentEngine.cs
@@ -0,0 +1,677 @@
+//
+// TextPasteIndentEngine.cs
+//
+// Author:
+// Matej Miklečić <matej.miklecic@gmail.com>
+//
+// Copyright (c) 2013 Matej Miklečić (matej.miklecic@gmail.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.CodeAnalysis.Options;
+using Microsoft.CodeAnalysis.Formatting;
+using Microsoft.CodeAnalysis.CSharp.Formatting;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp
+{
+ /// <summary>
+ /// Represents a decorator of an IStateMachineIndentEngine instance
+ /// that provides logic for text paste events.
+ /// </summary>
+ public class TextPasteIndentEngine : IDocumentIndentEngine, ITextPasteHandler
+ {
+
+ #region Properties
+
+ /// <summary>
+ /// An instance of IStateMachineIndentEngine which handles
+ /// the indentation logic.
+ /// </summary>
+ IStateMachineIndentEngine engine;
+ /// <summary>
+ /// Text editor options.
+ /// </summary>
+ internal readonly OptionSet options;
+ #endregion
+
+ #region Constructors
+
+ /// <summary>
+ /// Creates a new TextPasteIndentEngine instance.
+ /// </summary>
+ /// <param name="decoratedEngine">
+ /// An instance of <see cref="IStateMachineIndentEngine"/> to which the
+ /// logic for indentation will be delegated.
+ /// </param>
+ /// <param name = "options"></param>
+ public TextPasteIndentEngine(IStateMachineIndentEngine decoratedEngine, OptionSet options)
+ {
+ this.engine = decoratedEngine;
+ this.options = options;
+ this.engine.EnableCustomIndentLevels = false;
+ }
+
+ #endregion
+
+ #region ITextPasteHandler
+
+ /// <inheritdoc />
+ string ITextPasteHandler.FormatPlainText(SourceText sourceText, int offset, string text, byte[] copyData)
+ {
+ if (copyData != null && copyData.Length == 1) {
+ var strategy = TextPasteUtils.Strategies [(PasteStrategy)copyData [0]];
+ text = strategy.Decode(text);
+ }
+ engine.Update(sourceText, offset);
+
+ if (engine.IsInsideStringLiteral) {
+ int idx = text.IndexOf('"');
+ if (idx > 0) {
+ var o = offset;
+ while (o < sourceText.Length) {
+ char ch = sourceText[o];
+ engine.Push(ch);
+ if (NewLine.IsNewLine(ch))
+ break;
+ o++;
+ if (!engine.IsInsideStringLiteral)
+ return TextPasteUtils.StringLiteralStrategy.Encode(text);
+ }
+ return TextPasteUtils.StringLiteralStrategy.Encode(text.Substring(0, idx)) + text.Substring(idx);
+ }
+ return TextPasteUtils.StringLiteralStrategy.Encode(text);
+
+ } else if (engine.IsInsideVerbatimString) {
+
+ int idx = text.IndexOf('"');
+ if (idx > 0) {
+ var o = offset;
+ while (o < sourceText.Length) {
+ char ch = sourceText[o];
+ engine.Push(ch);
+ o++;
+ if (!engine.IsInsideVerbatimString)
+ return TextPasteUtils.VerbatimStringStrategy.Encode(text);
+ }
+ return TextPasteUtils.VerbatimStringStrategy.Encode(text.Substring(0, idx)) + text.Substring(idx);
+ }
+
+ return TextPasteUtils.VerbatimStringStrategy.Encode(text);
+ }
+ var line = sourceText.Lines.GetLineFromPosition(offset);
+ var pasteAtLineStart = line.Start == offset;
+ var indentedText = new StringBuilder();
+ var curLine = new StringBuilder();
+ var clonedEngine = engine.Clone();
+ bool isNewLine = false, gotNewLine = false;
+ for (int i = 0; i < text.Length; i++) {
+ var ch = text [i];
+ if (clonedEngine.IsInsideVerbatimString || clonedEngine.IsInsideMultiLineComment) {
+ clonedEngine.Push(ch);
+ curLine.Append(ch);
+ continue;
+ }
+
+ var delimiterLength = NewLine.GetDelimiterLength(ch, i + 1 < text.Length ? text[i + 1] : ' ');
+ if (delimiterLength > 0) {
+ isNewLine = true;
+ if (gotNewLine || pasteAtLineStart) {
+ if (curLine.Length > 0 /*|| formattingOptions.EmptyLineFormatting == EmptyLineFormatting.Indent*/)
+ indentedText.Append(clonedEngine.ThisLineIndent);
+ }
+ indentedText.Append(curLine);
+ var newLine = options.GetOption(FormattingOptions.NewLine, LanguageNames.CSharp);
+ indentedText.Append(newLine);
+ curLine.Length = 0;
+ gotNewLine = true;
+ i += delimiterLength - 1;
+ // textEditorOptions.EolMarker[0] is the newLineChar used by the indentation engine.
+ clonedEngine.Push(newLine [0]);
+ } else {
+ if (isNewLine) {
+ if (ch == '\t' || ch == ' ') {
+ clonedEngine.Push(ch);
+ continue;
+ }
+ isNewLine = false;
+ }
+ curLine.Append(ch);
+ clonedEngine.Push(ch);
+ }
+ if (clonedEngine.IsInsideVerbatimString || clonedEngine.IsInsideMultiLineComment &&
+ !(clonedEngine.LineBeganInsideVerbatimString || clonedEngine.LineBeganInsideMultiLineComment)) {
+ if (gotNewLine) {
+ if (curLine.Length > 0 /*|| formattingOptions.EmptyLineFormatting == EmptyLineFormatting.Indent*/)
+ indentedText.Append(clonedEngine.ThisLineIndent);
+ }
+ pasteAtLineStart = false;
+ indentedText.Append(curLine);
+ curLine.Length = 0;
+ gotNewLine = false;
+ continue;
+ }
+ }
+ if (gotNewLine && (!pasteAtLineStart || curLine.Length > 0)) {
+ indentedText.Append(clonedEngine.ThisLineIndent);
+ }
+ if (curLine.Length > 0) {
+ indentedText.Append(curLine);
+ }
+ return indentedText.ToString();
+ }
+
+ /// <inheritdoc />
+ byte[] ITextPasteHandler.GetCopyData(SourceText sourceText, TextSpan segment)
+ {
+ engine.Update(sourceText, segment.Start);
+
+ if (engine.IsInsideStringLiteral) {
+ return new[] { (byte)PasteStrategy.StringLiteral };
+ } else if (engine.IsInsideVerbatimString) {
+ return new[] { (byte)PasteStrategy.VerbatimString };
+ }
+
+ return null;
+ }
+
+ #endregion
+
+ #region IDocumentIndentEngine
+
+ /// <inheritdoc />
+ public string ThisLineIndent {
+ get { return engine.ThisLineIndent; }
+ }
+
+ /// <inheritdoc />
+ public string NextLineIndent {
+ get { return engine.NextLineIndent; }
+ }
+
+ /// <inheritdoc />
+ public string CurrentIndent {
+ get { return engine.CurrentIndent; }
+ }
+
+ /// <inheritdoc />
+ public bool NeedsReindent {
+ get { return engine.NeedsReindent; }
+ }
+
+ /// <inheritdoc />
+ public int Offset {
+ get { return engine.Offset; }
+ }
+
+// /// <inheritdoc />
+// public TextLocation Location {
+// get { return engine.Location; }
+// }
+
+ /// <inheritdoc />
+ public bool EnableCustomIndentLevels {
+ get { return engine.EnableCustomIndentLevels; }
+ set { engine.EnableCustomIndentLevels = value; }
+ }
+
+ /// <inheritdoc />
+ public void Push(char ch)
+ {
+ engine.Push(ch);
+ }
+
+ /// <inheritdoc />
+ public void Reset()
+ {
+ engine.Reset();
+ }
+
+ /// <inheritdoc />
+ public void Update(SourceText sourceText, int offset)
+ {
+ engine.Update(sourceText, offset);
+ }
+
+ #endregion
+
+ #region IClonable
+
+ public IDocumentIndentEngine Clone()
+ {
+ return new TextPasteIndentEngine(engine, options);
+ }
+
+ object ICloneable.Clone()
+ {
+ return Clone();
+ }
+
+ #endregion
+
+ }
+
+ /// <summary>
+ /// Types of text-paste strategies.
+ /// </summary>
+ public enum PasteStrategy : byte
+ {
+ PlainText = 0,
+ StringLiteral = 1,
+ VerbatimString = 2
+ }
+
+ /// <summary>
+ /// Defines some helper methods for dealing with text-paste events.
+ /// </summary>
+ public static class TextPasteUtils
+ {
+ /// <summary>
+ /// Collection of text-paste strategies.
+ /// </summary>
+ public static TextPasteStrategies Strategies = new TextPasteStrategies();
+
+ /// <summary>
+ /// The interface for a text-paste strategy.
+ /// </summary>
+ public interface IPasteStrategy
+ {
+ /// <summary>
+ /// Formats the given text according with this strategy rules.
+ /// </summary>
+ /// <param name="text">
+ /// The text to format.
+ /// </param>
+ /// <returns>
+ /// Formatted text.
+ /// </returns>
+ string Encode(string text);
+
+ /// <summary>
+ /// Converts text formatted according with this strategy rules
+ /// to its original form.
+ /// </summary>
+ /// <param name="text">
+ /// Formatted text to convert.
+ /// </param>
+ /// <returns>
+ /// Original form of the given formatted text.
+ /// </returns>
+ string Decode(string text);
+
+ /// <summary>
+ /// Type of this strategy.
+ /// </summary>
+ PasteStrategy Type { get; }
+ }
+
+ /// <summary>
+ /// Wrapper that discovers all defined text-paste strategies and defines a way
+ /// to easily access them through their <see cref="PasteStrategy"/> type.
+ /// </summary>
+ public sealed class TextPasteStrategies
+ {
+ /// <summary>
+ /// Collection of discovered text-paste strategies.
+ /// </summary>
+ IDictionary<PasteStrategy, IPasteStrategy> strategies;
+
+ /// <summary>
+ /// Uses reflection to find all types derived from <see cref="IPasteStrategy"/>
+ /// and adds an instance of each strategy to <see cref="strategies"/>.
+ /// </summary>
+ public TextPasteStrategies()
+ {
+ strategies = Assembly.GetExecutingAssembly()
+ .GetTypes()
+ .Where(t => typeof(IPasteStrategy).IsAssignableFrom(t) && t.IsClass)
+ .Select(t => (IPasteStrategy)t.GetProperty("Instance").GetValue(null, null))
+ .ToDictionary(s => s.Type);
+ }
+
+ /// <summary>
+ /// Checks if there is a strategy of the given type and returns it.
+ /// </summary>
+ /// <param name="strategy">
+ /// Type of the strategy instance.
+ /// </param>
+ /// <returns>
+ /// A strategy instance of the requested type,
+ /// or <see cref="DefaultStrategy"/> if it wasn't found.
+ /// </returns>
+ public IPasteStrategy this [PasteStrategy strategy] {
+ get {
+ if (strategies.ContainsKey(strategy)) {
+ return strategies [strategy];
+ }
+
+ return DefaultStrategy;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Doesn't do any formatting. Serves as the default strategy.
+ /// </summary>
+ public class PlainTextPasteStrategy : IPasteStrategy
+ {
+
+ #region Singleton
+
+ public static IPasteStrategy Instance {
+ get {
+ return instance ?? (instance = new PlainTextPasteStrategy());
+ }
+ }
+
+ static PlainTextPasteStrategy instance;
+
+ protected PlainTextPasteStrategy()
+ {
+ }
+
+ #endregion
+
+ /// <inheritdoc />
+ public string Encode(string text)
+ {
+ return text;
+ }
+
+ /// <inheritdoc />
+ public string Decode(string text)
+ {
+ return text;
+ }
+
+ /// <inheritdoc />
+ public PasteStrategy Type {
+ get { return PasteStrategy.PlainText; }
+ }
+ }
+
+ /// <summary>
+ /// Escapes chars in the given text so that they don't
+ /// break a valid string literal.
+ /// </summary>
+ public class StringLiteralPasteStrategy : IPasteStrategy
+ {
+
+ #region Singleton
+
+ public static IPasteStrategy Instance {
+ get {
+ return instance ?? (instance = new StringLiteralPasteStrategy());
+ }
+ }
+
+ static StringLiteralPasteStrategy instance;
+
+ protected StringLiteralPasteStrategy()
+ {
+ }
+
+ #endregion
+
+ /// <inheritdoc />
+ public string Encode(string text)
+ {
+ return ConvertString(text);
+ }
+
+ /// <summary>
+ /// Gets the escape sequence for the specified character.
+ /// </summary>
+ /// <remarks>This method does not convert ' or ".</remarks>
+ public static string ConvertChar(char ch)
+ {
+ switch (ch) {
+ case '\\':
+ return "\\\\";
+ case '\0':
+ return "\\0";
+ case '\a':
+ return "\\a";
+ case '\b':
+ return "\\b";
+ case '\f':
+ return "\\f";
+ case '\n':
+ return "\\n";
+ case '\r':
+ return "\\r";
+ case '\t':
+ return "\\t";
+ case '\v':
+ return "\\v";
+ default:
+ if (char.IsControl(ch) || char.IsSurrogate(ch) ||
+ // print all uncommon white spaces as numbers
+ (char.IsWhiteSpace(ch) && ch != ' ')) {
+ return "\\u" + ((int)ch).ToString("x4");
+ } else {
+ return ch.ToString();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Converts special characters to escape sequences within the given string.
+ /// </summary>
+ public static string ConvertString(string str)
+ {
+ StringBuilder sb = new StringBuilder ();
+ foreach (char ch in str) {
+ if (ch == '"') {
+ sb.Append("\\\"");
+ } else {
+ sb.Append(ConvertChar(ch));
+ }
+ }
+ return sb.ToString();
+ }
+
+ /// <inheritdoc />
+ public string Decode(string text)
+ {
+ var result = new StringBuilder();
+ bool isEscaped = false;
+
+ for (int i = 0; i < text.Length; i++) {
+ var ch = text[i];
+ if (isEscaped) {
+ switch (ch) {
+ case 'a':
+ result.Append('\a');
+ break;
+ case 'b':
+ result.Append('\b');
+ break;
+ case 'n':
+ result.Append('\n');
+ break;
+ case 't':
+ result.Append('\t');
+ break;
+ case 'v':
+ result.Append('\v');
+ break;
+ case 'r':
+ result.Append('\r');
+ break;
+ case '\\':
+ result.Append('\\');
+ break;
+ case 'f':
+ result.Append('\f');
+ break;
+ case '0':
+ result.Append(0);
+ break;
+ case '"':
+ result.Append('"');
+ break;
+ case '\'':
+ result.Append('\'');
+ break;
+ case 'x':
+ char r;
+ if (TryGetHex(text, -1, ref i, out r)) {
+ result.Append(r);
+ break;
+ }
+ goto default;
+ case 'u':
+ if (TryGetHex(text, 4, ref i, out r)) {
+ result.Append(r);
+ break;
+ }
+ goto default;
+ case 'U':
+ if (TryGetHex(text, 8, ref i, out r)) {
+ result.Append(r);
+ break;
+ }
+ goto default;
+ default:
+ result.Append('\\');
+ result.Append(ch);
+ break;
+ }
+ isEscaped = false;
+ continue;
+ }
+ if (ch != '\\') {
+ result.Append(ch);
+ }
+ else {
+ isEscaped = true;
+ }
+ }
+
+ return result.ToString();
+ }
+
+ static bool TryGetHex(string text, int count, ref int idx, out char r)
+ {
+ int i;
+ int total = 0;
+ int top = count != -1 ? count : 4;
+
+ for (i = 0; i < top; i++) {
+ int c = text[idx + 1 + i];
+
+ if (c >= '0' && c <= '9')
+ c = (int) c - (int) '0';
+ else if (c >= 'A' && c <= 'F')
+ c = (int) c - (int) 'A' + 10;
+ else if (c >= 'a' && c <= 'f')
+ c = (int) c - (int) 'a' + 10;
+ else {
+ r = '\0';
+ return false;
+ }
+ total = (total * 16) + c;
+ }
+
+ if (top == 8) {
+ if (total > 0x0010FFFF) {
+ r = '\0';
+ return false;
+ }
+
+ if (total >= 0x00010000)
+ total = ((total - 0x00010000) / 0x0400 + 0xD800);
+ }
+ r = (char)total;
+ idx += top;
+ return true;
+ }
+
+ /// <inheritdoc />
+ public PasteStrategy Type {
+ get { return PasteStrategy.StringLiteral; }
+ }
+ }
+
+ /// <summary>
+ /// Escapes chars in the given text so that they don't
+ /// break a valid verbatim string.
+ /// </summary>
+ public class VerbatimStringPasteStrategy : IPasteStrategy
+ {
+
+ #region Singleton
+
+ public static IPasteStrategy Instance {
+ get {
+ return instance ?? (instance = new VerbatimStringPasteStrategy());
+ }
+ }
+
+ static VerbatimStringPasteStrategy instance;
+
+ protected VerbatimStringPasteStrategy()
+ {
+ }
+
+ #endregion
+
+ static readonly Dictionary<char, IEnumerable<char>> encodeReplace = new Dictionary<char, IEnumerable<char>> {
+ { '\"', "\"\"" },
+ };
+
+ /// <inheritdoc />
+ public string Encode(string text)
+ {
+ return string.Concat(text.SelectMany(c => encodeReplace.ContainsKey(c) ? encodeReplace [c] : new[] { c }));
+ }
+
+ /// <inheritdoc />
+ public string Decode(string text)
+ {
+ bool isEscaped = false;
+ return string.Concat(text.Where(c => !(isEscaped = !isEscaped && c == '"')));
+ }
+
+ /// <inheritdoc />
+ public PasteStrategy Type {
+ get { return PasteStrategy.VerbatimString; }
+ }
+ }
+
+ /// <summary>
+ /// The default text-paste strategy.
+ /// </summary>
+ public static IPasteStrategy DefaultStrategy = PlainTextPasteStrategy.Instance;
+ /// <summary>
+ /// String literal text-paste strategy.
+ /// </summary>
+ public static IPasteStrategy StringLiteralStrategy = StringLiteralPasteStrategy.Instance;
+ /// <summary>
+ /// Verbatim string text-paste strategy.
+ /// </summary>
+ public static IPasteStrategy VerbatimStringStrategy = VerbatimStringPasteStrategy.Instance;
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.AbstractIntroduceVariableCodeAction.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.AbstractIntroduceVariableCodeAction.cs
new file mode 100644
index 0000000000..358124a430
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.AbstractIntroduceVariableCodeAction.cs
@@ -0,0 +1,134 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.Simplification;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.IntroduceVariable
+{
+ public partial class AbstractIntroduceVariableService<TService, TExpressionSyntax, TTypeSyntax, TTypeDeclarationSyntax, TQueryExpressionSyntax>
+ {
+ internal abstract class AbstractIntroduceVariableCodeAction : CodeAction
+ {
+ private readonly bool _allOccurrences;
+ private readonly bool _isConstant;
+ private readonly bool _isLocal;
+ private readonly bool _isQueryLocal;
+ private readonly TExpressionSyntax _expression;
+ private readonly SemanticDocument _document;
+ private readonly TService _service;
+ private readonly string _title;
+
+ private static Regex s_newlinePattern = new Regex(@"[\r\n]+", RegexOptions.Compiled);
+
+ internal AbstractIntroduceVariableCodeAction(
+ TService service,
+ SemanticDocument document,
+ TExpressionSyntax expression,
+ bool allOccurrences,
+ bool isConstant,
+ bool isLocal,
+ bool isQueryLocal)
+ {
+ _service = service;
+ _document = document;
+ _expression = expression;
+ _allOccurrences = allOccurrences;
+ _isConstant = isConstant;
+ _isLocal = isLocal;
+ _isQueryLocal = isQueryLocal;
+ _title = CreateDisplayText(expression);
+ }
+
+ public override string Title
+ {
+ get { return _title; }
+ }
+
+ protected override async Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken)
+ {
+ var changedDocument = await GetChangedDocumentCoreAsync(cancellationToken).ConfigureAwait(false);
+ return await Simplifier.ReduceAsync(changedDocument, cancellationToken: cancellationToken).ConfigureAwait(false);
+ }
+
+ private async Task<Document> GetChangedDocumentCoreAsync(CancellationToken cancellationToken)
+ {
+ if (_isQueryLocal)
+ {
+ return await _service.IntroduceQueryLocalAsync(_document, _expression, _allOccurrences, cancellationToken).ConfigureAwait(false);
+ }
+ else if (_isLocal)
+ {
+ return await _service.IntroduceLocalAsync(_document, _expression, _allOccurrences, _isConstant, cancellationToken).ConfigureAwait(false);
+ }
+ else
+ {
+ return await IntroduceFieldAsync(cancellationToken).ConfigureAwait(false);
+ }
+ }
+
+ private async Task<Document> IntroduceFieldAsync(CancellationToken cancellationToken)
+ {
+ var result = await _service.IntroduceFieldAsync(_document, _expression, _allOccurrences, _isConstant, cancellationToken).ConfigureAwait(false);
+ return result.Item1;
+ }
+
+ private string CreateDisplayText(TExpressionSyntax expression)
+ {
+ var singleLineExpression = expression.ConvertToSingleLine();
+ var nodeString = singleLineExpression.ToFullString().Trim();
+
+ // prevent the display string from spanning multiple lines
+ nodeString = s_newlinePattern.Replace(nodeString, " ");
+
+ // prevent the display string from being too long
+ const int MaxLength = 40;
+ if (nodeString.Length > MaxLength)
+ {
+ nodeString = nodeString.Substring(0, MaxLength) + "...";
+ }
+
+ return CreateDisplayText(nodeString);
+ }
+
+ private string CreateDisplayText(string nodeString)
+ {
+ // Indexed by: allOccurrences, isConstant, isLocal
+ var formatStrings = new string[2, 2, 2]
+ {
+ {
+ { Resources.IntroduceFieldFor, Resources.IntroduceLocalFor },
+ { Resources.IntroduceConstantFor, Resources.IntroduceLocalConstantFor }
+ },
+ {
+ { Resources.IntroduceFieldForAllOccurrences, Resources.IntroduceLocalForAllOccurrences },
+ { Resources.IntroduceConstantForAllOccurrences, Resources.IntroduceLocalConstantForAll }
+ }
+ };
+
+ var formatString = _isQueryLocal
+ ? _allOccurrences
+ ? Resources.IntroduceQueryVariableForAll
+ : Resources.IntroduceQueryVariableFor
+ : formatStrings[_allOccurrences ? 1 : 0, _isConstant ? 1 : 0, _isLocal ? 1 : 0];
+ return string.Format(formatString, nodeString);
+ }
+
+ protected ITypeSymbol GetExpressionType(
+ CancellationToken cancellationToken)
+ {
+ var semanticModel = _document.SemanticModel;
+ var typeInfo = semanticModel.GetTypeInfo(_expression, cancellationToken);
+
+ return typeInfo.Type ?? typeInfo.ConvertedType ?? semanticModel.Compilation.GetSpecialType(SpecialType.System_Object);
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.CodeAction.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.CodeAction.cs
new file mode 100644
index 0000000000..35f2203940
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.CodeAction.cs
@@ -0,0 +1,28 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.Simplification;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.IntroduceVariable
+{
+ public partial class AbstractIntroduceVariableService<TService, TExpressionSyntax, TTypeSyntax, TTypeDeclarationSyntax, TQueryExpressionSyntax>
+ {
+ private class IntroduceVariableCodeAction : AbstractIntroduceVariableCodeAction
+ {
+ internal IntroduceVariableCodeAction(
+ TService service,
+ SemanticDocument document,
+ TExpressionSyntax expression,
+ bool allOccurrences,
+ bool isConstant,
+ bool isLocal,
+ bool isQueryLocal)
+ : base(service, document, expression, allOccurrences, isConstant, isLocal, isQueryLocal)
+ {
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.IntroduceVariableAllOccurrenceCodeAction.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.IntroduceVariableAllOccurrenceCodeAction.cs
new file mode 100644
index 0000000000..00d1748536
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.IntroduceVariableAllOccurrenceCodeAction.cs
@@ -0,0 +1,39 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CaseCorrection;
+using Microsoft.CodeAnalysis.Formatting;
+using Microsoft.CodeAnalysis.Simplification;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.IntroduceVariable
+{
+ public partial class AbstractIntroduceVariableService<TService, TExpressionSyntax, TTypeSyntax, TTypeDeclarationSyntax, TQueryExpressionSyntax>
+ {
+ private class IntroduceVariableAllOccurrenceCodeAction : AbstractIntroduceVariableCodeAction
+ {
+ internal IntroduceVariableAllOccurrenceCodeAction(
+ TService service,
+ SemanticDocument document,
+ TExpressionSyntax expression,
+ bool allOccurrences,
+ bool isConstant,
+ bool isLocal,
+ bool isQueryLocal)
+ : base(service, document, expression, allOccurrences, isConstant, isLocal, isQueryLocal)
+ {
+ }
+
+ protected override async Task<Document> PostProcessChangesAsync(Document document, CancellationToken cancellationToken)
+ {
+ // TODO: Formatting conversation ? AllowDisjointSpanMerging not supported in nuget roslyn right now.
+ var optionSet = document.Project.Solution.Workspace.Options;//.WithChangedOption(FormattingOptions.AllowDisjointSpanMerging, true);
+ document = await Simplifier.ReduceAsync(document, Simplifier.Annotation, cancellationToken: cancellationToken).ConfigureAwait(false);
+ document = await Formatter.FormatAsync(document, Formatter.Annotation, cancellationToken: cancellationToken).ConfigureAwait(false);
+ document = await CaseCorrector.CaseCorrectAsync(document, CaseCorrector.Annotation, cancellationToken).ConfigureAwait(false);
+ return document;
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State.cs
new file mode 100644
index 0000000000..0dc173010e
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State.cs
@@ -0,0 +1,271 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Shared.Utilities;
+using Microsoft.CodeAnalysis.Text;
+using Roslyn.Utilities;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.IntroduceVariable
+{
+ public partial class AbstractIntroduceVariableService<TService, TExpressionSyntax, TTypeSyntax, TTypeDeclarationSyntax, TQueryExpressionSyntax>
+ {
+ private partial class State
+ {
+ public SemanticDocument Document { get; private set; }
+ public TExpressionSyntax Expression { get; private set; }
+
+ public bool InAttributeContext { get; private set; }
+ public bool InBlockContext { get; private set; }
+ public bool InConstructorInitializerContext { get; private set; }
+ public bool InFieldContext { get; private set; }
+ public bool InParameterContext { get; private set; }
+ public bool InQueryContext { get; private set; }
+ public bool InExpressionBodiedMemberContext { get; private set; }
+
+ public bool IsConstant { get; private set; }
+
+ private SemanticMap _semanticMap;
+ private readonly TService _service;
+
+ public State(TService service, SemanticDocument document)
+ {
+ _service = service;
+ this.Document = document;
+ }
+
+ public static State Generate(
+ TService service,
+ SemanticDocument document,
+ TextSpan textSpan,
+ CancellationToken cancellationToken)
+ {
+ var state = new State(service, document);
+ if (!state.TryInitialize(textSpan, cancellationToken))
+ {
+ return null;
+ }
+
+ return state;
+ }
+
+ private bool TryInitialize(
+ TextSpan textSpan,
+ CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return false;
+ }
+
+ var tree = this.Document.SyntaxTree;
+
+ this.Expression = this.GetExpressionUnderSpan(tree, textSpan, cancellationToken);
+ if (this.Expression == null)
+ {
+ return false;
+ }
+
+ var containingType = this.Expression.AncestorsAndSelf()
+ .Select(n => this.Document.SemanticModel.GetDeclaredSymbol(n, cancellationToken))
+ .OfType<INamedTypeSymbol>()
+ .FirstOrDefault();
+
+ containingType = containingType ?? this.Document.SemanticModel.Compilation.ScriptClass;
+
+ if (containingType == null || containingType.TypeKind == TypeKind.Interface)
+ {
+ return false;
+ }
+
+ if (!CanIntroduceVariable(cancellationToken))
+ {
+ return false;
+ }
+
+ this.IsConstant = this.Document.SemanticModel.GetConstantValue(this.Expression, cancellationToken).HasValue;
+
+ // Note: the ordering of these clauses are important. They go, generally, from
+ // innermost to outermost order.
+ if (IsInQueryContext(cancellationToken))
+ {
+ if (CanGenerateInto<TQueryExpressionSyntax>(cancellationToken))
+ {
+ this.InQueryContext = true;
+ return true;
+ }
+
+ return false;
+ }
+
+ if (IsInConstructorInitializerContext(cancellationToken))
+ {
+ if (CanGenerateInto<TTypeDeclarationSyntax>(cancellationToken))
+ {
+ this.InConstructorInitializerContext = true;
+ return true;
+ }
+
+ return false;
+ }
+
+ var enclosingBlocks = _service.GetContainingExecutableBlocks(this.Expression);
+ if (enclosingBlocks.Any())
+ {
+ // If we're inside a block, then don't even try the other options (like field,
+ // constructor initializer, etc.). This is desirable behavior. If we're in a
+ // block in a field, then we're in a lambda, and we want to offer to generate
+ // a local, and not a field.
+ if (IsInBlockContext(cancellationToken))
+ {
+ this.InBlockContext = true;
+ return true;
+ }
+
+ return false;
+ }
+
+ // The ordering of checks is important here. If we are inside a block within an Expression
+ // bodied member, we should treat it as if we are in block context.
+ // For example, in such a scenario we should generate inside the block, instead of rewriting
+ // a concise expression bodied member to its equivalent that has a body with a block.
+ // For this reason, block should precede expression bodied member check.
+ if (_service.IsInExpressionBodiedMember(this.Expression))
+ {
+ if (CanGenerateInto<TTypeDeclarationSyntax>(cancellationToken))
+ {
+ this.InExpressionBodiedMemberContext = true;
+ return true;
+ }
+
+ return false;
+ }
+
+ if (CanGenerateInto<TTypeDeclarationSyntax>(cancellationToken))
+ {
+ if (IsInParameterContext(cancellationToken))
+ {
+ this.InParameterContext = true;
+ return true;
+ }
+ else if (IsInFieldContext(cancellationToken))
+ {
+ this.InFieldContext = true;
+ return true;
+ }
+ else if (IsInAttributeContext(cancellationToken))
+ {
+ this.InAttributeContext = true;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public SemanticMap GetSemanticMap(CancellationToken cancellationToken)
+ {
+ _semanticMap = _semanticMap ?? this.Document.SemanticModel.GetSemanticMap(this.Expression, cancellationToken);
+ return _semanticMap;
+ }
+
+ private TExpressionSyntax GetExpressionUnderSpan(SyntaxTree tree, TextSpan textSpan, CancellationToken cancellationToken)
+ {
+ var root = tree.GetRoot(cancellationToken);
+ var startToken = root.FindToken(textSpan.Start);
+ var stopToken = root.FindToken(textSpan.End);
+
+ if (textSpan.End <= stopToken.SpanStart)
+ {
+ stopToken = stopToken.GetPreviousToken(includeSkipped: true);
+ }
+
+ if (startToken.RawKind == 0 || stopToken.RawKind == 0)
+ {
+ return null;
+ }
+
+ var containingExpressions1 = startToken.GetAncestors<TExpressionSyntax>().ToList();
+ var containingExpressions2 = stopToken.GetAncestors<TExpressionSyntax>().ToList();
+
+ var commonExpression = containingExpressions1.FirstOrDefault(containingExpressions2.Contains);
+ if (commonExpression == null)
+ {
+ return null;
+ }
+
+ if (!(textSpan.Start >= commonExpression.FullSpan.Start &&
+ textSpan.Start <= commonExpression.SpanStart))
+ {
+ return null;
+ }
+
+ if (!(textSpan.End >= commonExpression.Span.End &&
+ textSpan.End <= commonExpression.FullSpan.End))
+ {
+ return null;
+ }
+
+ return commonExpression;
+ }
+
+ private bool CanIntroduceVariable(
+ CancellationToken cancellationToken)
+ {
+ // Don't generate a variable for an expression that's the only expression in a
+ // statement. Otherwise we'll end up with something like "v;" which is not
+ // legal in C#.
+ if (!_service.CanIntroduceVariableFor(this.Expression))
+ {
+ return false;
+ }
+
+ if (this.Expression is TTypeSyntax)
+ {
+ return false;
+ }
+
+ // Even though we're creating a variable, we still ask if we can be replaced with an
+ // RValue and not an LValue. This is because introduction of a local adds a *new* LValue
+ // location, and we want to ensure that any writes will still happen to the *original*
+ // LValue location. i.e. if you have: "a[1] = b" then you don't want to change that to
+ // "var c = a[1]; c = b", as that write is no longer happening into the right LValue.
+ //
+ // In essense, this says "i can be replaced with an expression as long as i'm not being
+ // written to".
+ return this.Document.SemanticModel.CanReplaceWithRValue(this.Expression, cancellationToken);
+ }
+
+ private bool CanGenerateInto<TSyntax>(CancellationToken cancellationToken)
+ where TSyntax : SyntaxNode
+ {
+ if (this.Document.SemanticModel.Compilation.ScriptClass != null)
+ {
+ return true;
+ }
+
+ var syntax = this.Expression.GetAncestor<TSyntax>();
+ return syntax != null && !syntax.OverlapsHiddenPosition(cancellationToken);
+ }
+
+ private bool IsInTypeDeclarationOrValidCompilationUnit()
+ {
+ if (this.Expression.GetAncestorOrThis<TTypeDeclarationSyntax>() != null)
+ {
+ return true;
+ }
+
+ // If we're interactive/script, we can generate into the compilation unit.
+ if (this.Document.Document.SourceCodeKind != SourceCodeKind.Regular)
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State_Attribute.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State_Attribute.cs
new file mode 100644
index 0000000000..ed36767c83
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State_Attribute.cs
@@ -0,0 +1,25 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.IntroduceVariable
+{
+ public partial class AbstractIntroduceVariableService<TService, TExpressionSyntax, TTypeSyntax, TTypeDeclarationSyntax, TQueryExpressionSyntax>
+ {
+ private partial class State
+ {
+ private bool IsInAttributeContext(
+ CancellationToken cancellationToken)
+ {
+ if (!_service.IsInAttributeArgumentInitializer(this.Expression))
+ {
+ return false;
+ }
+
+ // Have to make sure we're on or inside a type decl so that we have some place to
+ // put the result.
+ return IsInTypeDeclarationOrValidCompilationUnit();
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State_Block.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State_Block.cs
new file mode 100644
index 0000000000..5a480a5bf1
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State_Block.cs
@@ -0,0 +1,38 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.IntroduceVariable
+{
+ public partial class AbstractIntroduceVariableService<TService, TExpressionSyntax, TTypeSyntax, TTypeDeclarationSyntax, TQueryExpressionSyntax>
+ {
+ private partial class State
+ {
+ private bool IsInBlockContext(
+ CancellationToken cancellationToken)
+ {
+ if (!this.IsInTypeDeclarationOrValidCompilationUnit())
+ {
+ return false;
+ }
+
+ // If refer to a query property, then we use the query context instead.
+ var bindingMap = GetSemanticMap(cancellationToken);
+ if (bindingMap.AllReferencedSymbols.Any(s => s is IRangeVariableSymbol))
+ {
+ return false;
+ }
+
+ var type = GetTypeSymbol(this.Document, this.Expression, cancellationToken, objectAsDefault: false);
+ if (type == null || type.SpecialType == SpecialType.System_Void)
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State_ConstructorInitializer.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State_ConstructorInitializer.cs
new file mode 100644
index 0000000000..d9d736cbad
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State_ConstructorInitializer.cs
@@ -0,0 +1,43 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.IntroduceVariable
+{
+ public partial class AbstractIntroduceVariableService<TService, TExpressionSyntax, TTypeSyntax, TTypeDeclarationSyntax, TQueryExpressionSyntax>
+ {
+ private partial class State
+ {
+ private bool IsInConstructorInitializerContext(
+ CancellationToken cancellationToken)
+ {
+ // Note: if we're in a lambda that has a block body, then we don't ever get here
+ // because of the early check for IsInBlockContext.
+ if (!_service.IsInConstructorInitializer(this.Expression))
+ {
+ return false;
+ }
+
+ var bindingMap = GetSemanticMap(cancellationToken);
+
+ // Can't extract out if a parameter is referenced.
+ if (bindingMap.AllReferencedSymbols.OfType<IParameterSymbol>().Any())
+ {
+ return false;
+ }
+
+ // Can't extract out an anonymous type used in a constructor initializer.
+ var info = this.Document.SemanticModel.GetTypeInfo(this.Expression, cancellationToken);
+ if (info.Type.ContainsAnonymousType())
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State_Field.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State_Field.cs
new file mode 100644
index 0000000000..1782195b7c
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State_Field.cs
@@ -0,0 +1,50 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.IntroduceVariable
+{
+ public partial class AbstractIntroduceVariableService<TService, TExpressionSyntax, TTypeSyntax, TTypeDeclarationSyntax, TQueryExpressionSyntax>
+ {
+ private partial class State
+ {
+ private bool IsInFieldContext(
+ CancellationToken cancellationToken)
+ {
+ // Note: if we're in a lambda that has a block body, then we don't ever get here
+ // because of the early check for IsInBlockContext.
+ if (!_service.IsInFieldInitializer(this.Expression))
+ {
+ return false;
+ }
+
+ if (!IsInTypeDeclarationOrValidCompilationUnit())
+ {
+ return false;
+ }
+
+ // if the expression in the field references any parameters then that means it was
+ // either an expression inside a lambda in the field, or it was an expression in a
+ // query inside the field. Either of which cannot be extracted out further by this
+ // fix.
+ var bindingMap = GetSemanticMap(cancellationToken);
+ if (bindingMap.AllReferencedSymbols.OfType<IParameterSymbol>().Any())
+ {
+ return false;
+ }
+
+ // Can't extract out an anonymous type used in a field initializer.
+ var info = this.Document.SemanticModel.GetTypeInfo(this.Expression, cancellationToken);
+ if (info.Type.ContainsAnonymousType())
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State_Parameter.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State_Parameter.cs
new file mode 100644
index 0000000000..8b45d848f9
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State_Parameter.cs
@@ -0,0 +1,36 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.IntroduceVariable
+{
+ public partial class AbstractIntroduceVariableService<TService, TExpressionSyntax, TTypeSyntax, TTypeDeclarationSyntax, TQueryExpressionSyntax>
+ {
+ private partial class State
+ {
+ private bool IsInParameterContext(
+ CancellationToken cancellationToken)
+ {
+ if (!_service.IsInParameterInitializer(this.Expression))
+ {
+ return false;
+ }
+
+ // The default value for a parameter is a constant. So we always allow it unless it
+ // happens to capture one of the method's type parameters.
+ var bindingMap = this.GetSemanticMap(cancellationToken);
+ if (bindingMap.AllReferencedSymbols.OfType<ITypeParameterSymbol>()
+ .Where(tp => tp.TypeParameterKind == TypeParameterKind.Method)
+ .Any())
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State_Query.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State_Query.cs
new file mode 100644
index 0000000000..14397a41c4
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.State_Query.cs
@@ -0,0 +1,37 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.IntroduceVariable
+{
+ public partial class AbstractIntroduceVariableService<TService, TExpressionSyntax, TTypeSyntax, TTypeDeclarationSyntax, TQueryExpressionSyntax>
+ {
+ private partial class State
+ {
+ private bool IsInQueryContext(
+ CancellationToken cancellationToken)
+ {
+ if (!_service.IsInNonFirstQueryClause(this.Expression))
+ {
+ return false;
+ }
+
+ var semanticMap = GetSemanticMap(cancellationToken);
+ if (!semanticMap.AllReferencedSymbols.Any(s => s is IRangeVariableSymbol))
+ {
+ return false;
+ }
+
+ var info = this.Document.SemanticModel.GetTypeInfo(this.Expression, cancellationToken);
+ if (info.Type == null || info.Type.SpecialType == SpecialType.System_Void)
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.cs
new file mode 100644
index 0000000000..a8d59347a8
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/AbstractIntroduceVariableService.cs
@@ -0,0 +1,329 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using ICSharpCode.NRefactory6.CSharp.Refactoring;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.CodeRefactorings;
+using Microsoft.CodeAnalysis.Formatting;
+using Microsoft.CodeAnalysis.Internal.Log;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Shared.Utilities;
+using Microsoft.CodeAnalysis.Text;
+using Roslyn.Utilities;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.IntroduceVariable
+{
+ public abstract partial class AbstractIntroduceVariableService<TService, TExpressionSyntax, TTypeSyntax, TTypeDeclarationSyntax, TQueryExpressionSyntax>
+ where TService : AbstractIntroduceVariableService<TService, TExpressionSyntax, TTypeSyntax, TTypeDeclarationSyntax, TQueryExpressionSyntax>
+ where TExpressionSyntax : SyntaxNode
+ where TTypeSyntax : TExpressionSyntax
+ where TTypeDeclarationSyntax : SyntaxNode
+ where TQueryExpressionSyntax : TExpressionSyntax
+ {
+ protected abstract bool IsInNonFirstQueryClause(TExpressionSyntax expression);
+ protected abstract bool IsInFieldInitializer(TExpressionSyntax expression);
+ protected abstract bool IsInParameterInitializer(TExpressionSyntax expression);
+ protected abstract bool IsInConstructorInitializer(TExpressionSyntax expression);
+ protected abstract bool IsInAttributeArgumentInitializer(TExpressionSyntax expression);
+ protected abstract bool IsInExpressionBodiedMember(TExpressionSyntax expression);
+
+ protected abstract IEnumerable<SyntaxNode> GetContainingExecutableBlocks(TExpressionSyntax expression);
+ protected abstract IList<bool> GetInsertionIndices(TTypeDeclarationSyntax destination, CancellationToken cancellationToken);
+
+ protected abstract bool CanIntroduceVariableFor(TExpressionSyntax expression);
+ protected abstract bool CanReplace(TExpressionSyntax expression);
+
+ protected abstract Task<Document> IntroduceQueryLocalAsync(SemanticDocument document, TExpressionSyntax expression, bool allOccurrences, CancellationToken cancellationToken);
+ protected abstract Task<Document> IntroduceLocalAsync(SemanticDocument document, TExpressionSyntax expression, bool allOccurrences, bool isConstant, CancellationToken cancellationToken);
+ protected abstract Task<Tuple<Document, SyntaxNode, int>> IntroduceFieldAsync(SemanticDocument document, TExpressionSyntax expression, bool allOccurrences, bool isConstant, CancellationToken cancellationToken);
+
+ protected virtual bool BlockOverlapsHiddenPosition(SyntaxNode block, CancellationToken cancellationToken)
+ {
+ return block.OverlapsHiddenPosition(cancellationToken);
+ }
+
+ public async Task<IntroduceVariableResult> IntroduceVariableAsync(
+ Document document,
+ TextSpan textSpan,
+ CancellationToken cancellationToken)
+ {
+ var semanticDocument = await SemanticDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false);
+
+ var state = State.Generate((TService)this, semanticDocument, textSpan, cancellationToken);
+ if (state == null)
+ {
+ return IntroduceVariableResult.Failure;
+ }
+
+ var actions = await CreateActionsAsync(state, cancellationToken).ConfigureAwait(false);
+ if (actions.Count == 0)
+ {
+ return IntroduceVariableResult.Failure;
+ }
+
+ return new IntroduceVariableResult(new CodeRefactoring(null, actions));
+ }
+
+ private async Task<List<CodeAction>> CreateActionsAsync(State state, CancellationToken cancellationToken)
+ {
+ var actions = new List<CodeAction>();
+
+ if (state.InQueryContext)
+ {
+ actions.Add(CreateAction(state, allOccurrences: false, isConstant: false, isLocal: false, isQueryLocal: true));
+ actions.Add(CreateAction(state, allOccurrences: true, isConstant: false, isLocal: false, isQueryLocal: true));
+ }
+ else if (state.InParameterContext)
+ {
+ actions.Add(CreateAction(state, allOccurrences: false, isConstant: true, isLocal: false, isQueryLocal: false));
+ actions.Add(CreateAction(state, allOccurrences: true, isConstant: true, isLocal: false, isQueryLocal: false));
+ }
+ else if (state.InFieldContext)
+ {
+ actions.Add(CreateAction(state, allOccurrences: false, isConstant: state.IsConstant, isLocal: false, isQueryLocal: false));
+ actions.Add(CreateAction(state, allOccurrences: true, isConstant: state.IsConstant, isLocal: false, isQueryLocal: false));
+ }
+ else if (state.InConstructorInitializerContext)
+ {
+ actions.Add(CreateAction(state, allOccurrences: false, isConstant: state.IsConstant, isLocal: false, isQueryLocal: false));
+ actions.Add(CreateAction(state, allOccurrences: true, isConstant: state.IsConstant, isLocal: false, isQueryLocal: false));
+ }
+ else if (state.InAttributeContext)
+ {
+ actions.Add(CreateAction(state, allOccurrences: false, isConstant: true, isLocal: false, isQueryLocal: false));
+ actions.Add(CreateAction(state, allOccurrences: true, isConstant: true, isLocal: false, isQueryLocal: false));
+ }
+ else if (state.InBlockContext)
+ {
+ await CreateConstantFieldActionsAsync(state, actions, cancellationToken).ConfigureAwait(false);
+
+ var blocks = this.GetContainingExecutableBlocks(state.Expression);
+ var block = blocks.FirstOrDefault();
+
+ if (!BlockOverlapsHiddenPosition(block, cancellationToken))
+ {
+ actions.Add(CreateAction(state, allOccurrences: false, isConstant: state.IsConstant, isLocal: true, isQueryLocal: false));
+
+ if (blocks.All(b => !BlockOverlapsHiddenPosition(b, cancellationToken)))
+ {
+ actions.Add(CreateAction(state, allOccurrences: true, isConstant: state.IsConstant, isLocal: true, isQueryLocal: false));
+ }
+ }
+ }
+ else if (state.InExpressionBodiedMemberContext)
+ {
+ await CreateConstantFieldActionsAsync(state, actions, cancellationToken).ConfigureAwait(false);
+ actions.Add(CreateAction(state, allOccurrences: false, isConstant: state.IsConstant, isLocal: true, isQueryLocal: false));
+ actions.Add(CreateAction(state, allOccurrences: true, isConstant: state.IsConstant, isLocal: true, isQueryLocal: false));
+ }
+
+ return actions;
+ }
+
+ private async Task CreateConstantFieldActionsAsync(State state, List<CodeAction> actions, CancellationToken cancellationToken)
+ {
+ if (state.IsConstant &&
+ !state.GetSemanticMap(cancellationToken).AllReferencedSymbols.OfType<ILocalSymbol>().Any() &&
+ !state.GetSemanticMap(cancellationToken).AllReferencedSymbols.OfType<IParameterSymbol>().Any())
+ {
+ // If something is a constant, and it doesn't access any other locals constants,
+ // then we prefer to offer to generate a constant field instead of a constant
+ // local.
+ var action1 = CreateAction(state, allOccurrences: false, isConstant: true, isLocal: false, isQueryLocal: false);
+ if (await CanGenerateIntoContainerAsync(state, action1, cancellationToken).ConfigureAwait(false))
+ {
+ actions.Add(action1);
+ }
+
+ var action2 = CreateAction(state, allOccurrences: true, isConstant: true, isLocal: false, isQueryLocal: false);
+ if (await CanGenerateIntoContainerAsync(state, action2, cancellationToken).ConfigureAwait(false))
+ {
+ actions.Add(action2);
+ }
+ }
+ }
+
+ private async Task<bool> CanGenerateIntoContainerAsync(State state, CodeAction action, CancellationToken cancellationToken)
+ {
+ var result = await this.IntroduceFieldAsync(
+ state.Document, state.Expression,
+ allOccurrences: false, isConstant: state.IsConstant, cancellationToken: cancellationToken).ConfigureAwait(false);
+
+ SyntaxNode destination = result.Item2;
+ int insertionIndex = result.Item3;
+
+ if (!destination.OverlapsHiddenPosition(cancellationToken))
+ {
+ return true;
+ }
+
+ if (destination is TTypeDeclarationSyntax)
+ {
+ var insertionIndices = this.GetInsertionIndices((TTypeDeclarationSyntax)destination, cancellationToken);
+ if (insertionIndices != null &&
+ insertionIndices.Count > insertionIndex &&
+ insertionIndices[insertionIndex])
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private CodeAction CreateAction(State state, bool allOccurrences, bool isConstant, bool isLocal, bool isQueryLocal)
+ {
+ if (allOccurrences)
+ {
+ return new IntroduceVariableAllOccurrenceCodeAction((TService)this, state.Document, state.Expression, allOccurrences, isConstant, isLocal, isQueryLocal);
+ }
+
+ return new IntroduceVariableCodeAction((TService)this, state.Document, state.Expression, allOccurrences, isConstant, isLocal, isQueryLocal);
+ }
+
+ protected static SyntaxToken GenerateUniqueFieldName(
+ SemanticDocument document,
+ TExpressionSyntax expression,
+ bool isConstant,
+ CancellationToken cancellationToken)
+ {
+ var semanticModel = document.SemanticModel;
+ var baseName = semanticModel.GenerateNameForExpression(expression, isConstant);
+
+ // A field can't conflict with any existing member names.
+ var declaringType = semanticModel.GetEnclosingNamedType(expression.SpanStart, cancellationToken);
+ var reservedNames = declaringType.GetMembers().Select(m => m.Name);
+
+ return NameGenerator.EnsureUniqueness(baseName, reservedNames, true).ToIdentifierToken();
+ }
+
+ protected static SyntaxToken GenerateUniqueLocalName(
+ SemanticDocument document,
+ TExpressionSyntax expression,
+ bool isConstant,
+ CancellationToken cancellationToken)
+ {
+
+ var semanticModel = document.SemanticModel;
+ var baseName = semanticModel.GenerateNameForExpression(expression, capitalize: isConstant);
+ var reservedNames = semanticModel.LookupSymbols(expression.SpanStart).Select(s => s.Name);
+
+ return NameGenerator.EnsureUniqueness(baseName, reservedNames, true).ToIdentifierToken();
+ }
+
+ protected ISet<TExpressionSyntax> FindMatches(
+ SemanticDocument originalDocument,
+ TExpressionSyntax expressionInOriginal,
+ SemanticDocument currentDocument,
+ SyntaxNode withinNodeInCurrent,
+ bool allOccurrences,
+ CancellationToken cancellationToken)
+ {
+ var originalSemanticModel = originalDocument.SemanticModel;
+ var currentSemanticModel = currentDocument.SemanticModel;
+
+ var matches = from nodeInCurrent in withinNodeInCurrent.DescendantNodesAndSelf().OfType<TExpressionSyntax>()
+ where NodeMatchesExpression(originalSemanticModel, currentSemanticModel, expressionInOriginal, nodeInCurrent, allOccurrences, cancellationToken)
+ select nodeInCurrent;
+ return new HashSet<TExpressionSyntax>(matches.OfType<TExpressionSyntax>());
+ }
+
+ private bool NodeMatchesExpression(
+ SemanticModel originalSemanticModel,
+ SemanticModel currentSemanticModel,
+ TExpressionSyntax expressionInOriginal,
+ TExpressionSyntax nodeInCurrent,
+ bool allOccurrences,
+ CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ if (nodeInCurrent == expressionInOriginal)
+ {
+ return true;
+ }
+ else
+ {
+ if (allOccurrences &&
+ this.CanReplace(nodeInCurrent))
+ {
+ return SemanticEquivalence.AreSemanticallyEquivalent(
+ originalSemanticModel, currentSemanticModel, expressionInOriginal, nodeInCurrent);
+ }
+ }
+
+ return false;
+ }
+
+ protected TNode Rewrite<TNode>(
+ SemanticDocument originalDocument,
+ TExpressionSyntax expressionInOriginal,
+ TExpressionSyntax variableName,
+ SemanticDocument currentDocument,
+ TNode withinNodeInCurrent,
+ bool allOccurrences,
+ CancellationToken cancellationToken)
+ where TNode : SyntaxNode
+ {
+ var matches = FindMatches(originalDocument, expressionInOriginal, currentDocument, withinNodeInCurrent, allOccurrences, cancellationToken);
+
+ // Parenthesize the variable, and go and replace anything we find with it.
+ // NOTE: we do not want elastic trivia as we want to just replace the existing code
+ // as is, while preserving the trivia there. We do not want to update it.
+ var replacement = variableName.Parenthesize(includeElasticTrivia: false)
+ .WithAdditionalAnnotations(Formatter.Annotation);
+
+ return RewriteCore(withinNodeInCurrent, replacement, matches);
+ }
+
+ protected abstract TNode RewriteCore<TNode>(
+ TNode node,
+ SyntaxNode replacementNode,
+ ISet<TExpressionSyntax> matches)
+ where TNode : SyntaxNode;
+
+ protected static ITypeSymbol GetTypeSymbol(
+ SemanticDocument document,
+ TExpressionSyntax expression,
+ CancellationToken cancellationToken,
+ bool objectAsDefault = true)
+ {
+ var semanticModel = document.SemanticModel;
+ var typeInfo = semanticModel.GetTypeInfo(expression, cancellationToken);
+
+ if (typeInfo.Type != null)
+ {
+ return typeInfo.Type;
+ }
+
+ if (typeInfo.ConvertedType != null)
+ {
+ return typeInfo.ConvertedType;
+ }
+
+ if (objectAsDefault)
+ {
+ return semanticModel.Compilation.GetSpecialType(SpecialType.System_Object);
+ }
+
+ return null;
+ }
+
+ protected static IEnumerable<IParameterSymbol> GetAnonymousMethodParameters(
+ SemanticDocument document, TExpressionSyntax expression, CancellationToken cancellationToken)
+ {
+ var semanticModel = document.SemanticModel;
+ var semanticMap = semanticModel.GetSemanticMap(expression, cancellationToken);
+
+ var anonymousMethodParameters = semanticMap.AllReferencedSymbols
+ .OfType<IParameterSymbol>()
+ .Where(p => p.ContainingSymbol.IsAnonymousFunction());
+ return anonymousMethodParameters;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/CSharpIntroduceVariableService.Rewriter.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/CSharpIntroduceVariableService.Rewriter.cs
new file mode 100644
index 0000000000..ce17b4ef4a
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/CSharpIntroduceVariableService.Rewriter.cs
@@ -0,0 +1,61 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Simplification;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.IntroduceVariable
+{
+ public partial class CSharpIntroduceVariableService
+ {
+ private class Rewriter : CSharpSyntaxRewriter
+ {
+ private readonly SyntaxAnnotation _replacementAnnotation = new SyntaxAnnotation ();
+ private readonly SyntaxNode _replacementNode;
+ private readonly ISet<ExpressionSyntax> _matches;
+
+ private Rewriter (SyntaxNode replacementNode, ISet<ExpressionSyntax> matches)
+ {
+ _replacementNode = replacementNode;
+ _matches = matches;
+ }
+
+ public override SyntaxNode Visit (SyntaxNode node)
+ {
+ var expression = node as ExpressionSyntax;
+ if (expression != null &&
+ _matches.Contains (expression)) {
+ return _replacementNode
+ .WithLeadingTrivia (expression.GetLeadingTrivia ())
+ .WithTrailingTrivia (expression.GetTrailingTrivia ())
+ .WithAdditionalAnnotations (_replacementAnnotation);
+ }
+
+ return base.Visit (node);
+ }
+
+ public override SyntaxNode VisitParenthesizedExpression (ParenthesizedExpressionSyntax node)
+ {
+ var newNode = base.VisitParenthesizedExpression (node);
+ if (node != newNode &&
+ newNode.IsKind (SyntaxKind.ParenthesizedExpression)) {
+ var parenthesizedExpression = (ParenthesizedExpressionSyntax)newNode;
+ var innerExpression = parenthesizedExpression.OpenParenToken.GetNextToken ().Parent;
+ if (innerExpression.HasAnnotation (_replacementAnnotation)) {
+ return newNode.WithAdditionalAnnotations (Simplifier.Annotation);
+ }
+ }
+
+ return newNode;
+ }
+
+ public static SyntaxNode Visit (SyntaxNode node, SyntaxNode replacementNode, ISet<ExpressionSyntax> matches)
+ {
+ return new Rewriter (replacementNode, matches).Visit ((SyntaxNode)node);
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/CSharpIntroduceVariableService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/CSharpIntroduceVariableService.cs
new file mode 100644
index 0000000000..5b46645500
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/CSharpIntroduceVariableService.cs
@@ -0,0 +1,131 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Composition;
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Symbols;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Host;
+using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.IntroduceVariable
+ {
+ public partial class CSharpIntroduceVariableService :
+ AbstractIntroduceVariableService<CSharpIntroduceVariableService, ExpressionSyntax, TypeSyntax, TypeDeclarationSyntax, QueryExpressionSyntax>
+ {
+ protected override bool IsInNonFirstQueryClause(ExpressionSyntax expression)
+ {
+ var query = expression.GetAncestor<QueryExpressionSyntax>();
+ if (query != null)
+ {
+ // Can't introduce for the first clause in a query.
+ var fromClause = expression.GetAncestor<FromClauseSyntax>();
+ if (fromClause == null || query.FromClause != fromClause)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ protected override bool IsInFieldInitializer(ExpressionSyntax expression)
+ {
+ return expression.GetAncestorOrThis<VariableDeclaratorSyntax>()
+ .GetAncestorOrThis<FieldDeclarationSyntax>() != null;
+ }
+
+ protected override bool IsInParameterInitializer(ExpressionSyntax expression)
+ {
+ return expression.GetAncestorOrThis<EqualsValueClauseSyntax>().IsParentKind(SyntaxKind.Parameter);
+ }
+
+ protected override bool IsInConstructorInitializer(ExpressionSyntax expression)
+ {
+ return expression.GetAncestorOrThis<ConstructorInitializerSyntax>() != null;
+ }
+
+ protected override bool IsInExpressionBodiedMember(ExpressionSyntax expression)
+ {
+ // walk up until we find a nearest enclosing block or arrow expression.
+ for (SyntaxNode node = expression; node != null; node = node.GetParent())
+ {
+ // If we are in an expression bodied member and if the expression has a block body, then,
+ // act as if we're in a block context and not in an expression body context at all.
+ if (node.IsKind(SyntaxKind.Block))
+ {
+ return false;
+ }
+ else if (node.IsKind(SyntaxKind.ArrowExpressionClause))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ protected override bool IsInAttributeArgumentInitializer(ExpressionSyntax expression)
+ {
+ // Don't call the base here. We want to let the user extract a constant if they've
+ // said "Foo(a = 10)"
+ var attributeArgument = expression.GetAncestorOrThis<AttributeArgumentSyntax>();
+ if (attributeArgument != null)
+ {
+ // Can't extract an attribute initializer if it contains an array initializer of any
+ // sort. Also, we can't extract if there's any typeof expression within it.
+ if (!expression.DepthFirstTraversal().Any(n => n.RawKind == (int)SyntaxKind.ArrayCreationExpression) &&
+ !expression.DepthFirstTraversal().Any(n => n.RawKind == (int)SyntaxKind.TypeOfExpression))
+ {
+ var attributeDecl = attributeArgument.GetAncestorOrThis<AttributeListSyntax>();
+
+ // Also can't extract an attribute initializer if the attribute is a global one.
+ if (!attributeDecl.IsParentKind(SyntaxKind.CompilationUnit))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ protected override bool CanIntroduceVariableFor(ExpressionSyntax expression)
+ {
+ if (expression.WalkUpParentheses().IsParentKind(SyntaxKind.ExpressionStatement))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ protected override IEnumerable<SyntaxNode> GetContainingExecutableBlocks(ExpressionSyntax expression)
+ {
+ return expression.GetAncestorsOrThis<BlockSyntax>();
+ }
+
+ protected override IList<bool> GetInsertionIndices(TypeDeclarationSyntax destination, CancellationToken cancellationToken)
+ {
+ return destination.GetInsertionIndices(cancellationToken);
+ }
+
+ protected override bool CanReplace(ExpressionSyntax expression)
+ {
+ return true;
+ }
+
+ protected override TNode RewriteCore<TNode>(
+ TNode node,
+ SyntaxNode replacementNode,
+ ISet<ExpressionSyntax> matches)
+ {
+ return (TNode)Rewriter.Visit(node, replacementNode, matches);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/CSharpIntroduceVariableService_IntroduceField.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/CSharpIntroduceVariableService_IntroduceField.cs
new file mode 100644
index 0000000000..7238f953a6
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/CSharpIntroduceVariableService_IntroduceField.cs
@@ -0,0 +1,198 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Formatting;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Simplification;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.IntroduceVariable
+{
+ public partial class CSharpIntroduceVariableService
+ {
+ protected override Task<Tuple<Document, SyntaxNode, int>> IntroduceFieldAsync(
+ SemanticDocument document,
+ ExpressionSyntax expression,
+ bool allOccurrences,
+ bool isConstant,
+ CancellationToken cancellationToken)
+ {
+ var oldTypeDeclaration = expression.GetAncestorOrThis<TypeDeclarationSyntax>();
+
+ var oldType = oldTypeDeclaration != null
+ ? document.SemanticModel.GetDeclaredSymbol(oldTypeDeclaration, cancellationToken) as INamedTypeSymbol
+ : document.SemanticModel.Compilation.ScriptClass;
+ var newNameToken = (SyntaxToken)GenerateUniqueFieldName(document, expression, isConstant, cancellationToken);
+
+ var newQualifiedName = oldTypeDeclaration != null
+ ? SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.ParseName(oldType.ToDisplayString (ICSharpCode.NRefactory6.CSharp.Completion.DelegateCreationContextHandler.NameFormat)), SyntaxFactory.IdentifierName(newNameToken))
+ : (ExpressionSyntax)SyntaxFactory.IdentifierName(newNameToken);
+
+ newQualifiedName = newQualifiedName.WithAdditionalAnnotations(Simplifier.Annotation);
+
+ var newFieldDeclaration = SyntaxFactory.FieldDeclaration(
+ default(SyntaxList<AttributeListSyntax>),
+ MakeFieldModifiers(isConstant, inScript: oldType.IsScriptClass),
+ SyntaxFactory.VariableDeclaration(
+ GetTypeSymbol(document, expression, cancellationToken).GenerateTypeSyntax(),
+ SyntaxFactory.SingletonSeparatedList(
+ SyntaxFactory.VariableDeclarator(
+ newNameToken.WithAdditionalAnnotations(RenameAnnotation.Create()),
+ null,
+ SyntaxFactory.EqualsValueClause(expression))))).WithAdditionalAnnotations(Formatter.Annotation);
+
+ if (oldTypeDeclaration != null)
+ {
+ var newTypeDeclaration = Rewrite(
+ document, expression, newQualifiedName, document, oldTypeDeclaration, allOccurrences, cancellationToken);
+
+ var insertionIndex = isConstant ?
+ DetermineConstantInsertPosition(oldTypeDeclaration.Members, newTypeDeclaration.Members) :
+ DetermineFieldInsertPosition(oldTypeDeclaration.Members, newTypeDeclaration.Members);
+
+ var finalTypeDeclaration = InsertMember(newTypeDeclaration, newFieldDeclaration, insertionIndex);
+
+ SyntaxNode destination = oldTypeDeclaration;
+ var newRoot = document.Root.ReplaceNode(oldTypeDeclaration, finalTypeDeclaration);
+ return Task.FromResult(Tuple.Create(document.Document.WithSyntaxRoot(newRoot), destination, insertionIndex));
+ }
+ else
+ {
+ var oldCompilationUnit = (CompilationUnitSyntax)document.Root;
+ var newCompilationUnit = Rewrite(
+ document, expression, newQualifiedName, document, oldCompilationUnit, allOccurrences, cancellationToken);
+
+ var insertionIndex = isConstant ?
+ DetermineConstantInsertPosition(oldCompilationUnit.Members, newCompilationUnit.Members) :
+ DetermineFieldInsertPosition(oldCompilationUnit.Members, newCompilationUnit.Members);
+
+ SyntaxNode destination = oldCompilationUnit;
+ var newRoot = newCompilationUnit.WithMembers(newCompilationUnit.Members.Insert(insertionIndex, newFieldDeclaration));
+ return Task.FromResult(Tuple.Create(document.Document.WithSyntaxRoot(newRoot), destination, insertionIndex));
+ }
+ }
+
+ protected static int DetermineConstantInsertPosition(
+ SyntaxList<MemberDeclarationSyntax> oldMembers,
+ SyntaxList<MemberDeclarationSyntax> newMembers)
+ {
+ // 1) Place the constant after the last constant.
+ //
+ // 2) If there is no constant, place it before the first field
+ //
+ // 3) If the first change is before either of those, then place before the first
+ // change
+ //
+ // 4) Otherwise, place it at the start.
+ var index = 0;
+ var lastConstantIndex = oldMembers.LastIndexOf(IsConstantField);
+
+ if (lastConstantIndex >= 0)
+ {
+ index = lastConstantIndex + 1;
+ }
+ else
+ {
+ var firstFieldIndex = oldMembers.IndexOf(member => member is FieldDeclarationSyntax);
+ if (firstFieldIndex >= 0)
+ {
+ index = firstFieldIndex;
+ }
+ }
+
+ var firstChangeIndex = DetermineFirstChange(oldMembers, newMembers);
+ if (firstChangeIndex >= 0)
+ {
+ index = Math.Min(index, firstChangeIndex);
+ }
+
+ return index;
+ }
+
+ protected static int DetermineFieldInsertPosition(
+ SyntaxList<MemberDeclarationSyntax> oldMembers,
+ SyntaxList<MemberDeclarationSyntax> newMembers)
+ {
+ // 1) Place the constant after the last field.
+ //
+ // 2) If there is no field, place it after the last constant
+ //
+ // 3) If the first change is before either of those, then place before the first
+ // change
+ //
+ // 4) Otherwise, place it at the start.
+ var index = 0;
+ var lastFieldIndex = oldMembers.LastIndexOf(member => member is FieldDeclarationSyntax);
+ if (lastFieldIndex >= 0)
+ {
+ index = lastFieldIndex + 1;
+ }
+ else
+ {
+ var lastConstantIndex = oldMembers.LastIndexOf(IsConstantField);
+ if (lastConstantIndex >= 0)
+ {
+ index = lastConstantIndex + 1;
+ }
+ }
+
+ var firstChangeIndex = DetermineFirstChange(oldMembers, newMembers);
+ if (firstChangeIndex >= 0)
+ {
+ index = Math.Min(index, firstChangeIndex);
+ }
+
+ return index;
+ }
+
+ private static bool IsConstantField(MemberDeclarationSyntax member)
+ {
+ var field = member as FieldDeclarationSyntax;
+ return field != null && field.Modifiers.Any(SyntaxKind.ConstKeyword);
+ }
+
+ protected static int DetermineFirstChange(SyntaxList<MemberDeclarationSyntax> oldMembers, SyntaxList<MemberDeclarationSyntax> newMembers)
+ {
+ for (int i = 0; i < oldMembers.Count; i++)
+ {
+ if (!SyntaxFactory.AreEquivalent(oldMembers[i], newMembers[i], topLevel: false))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ protected static TypeDeclarationSyntax InsertMember(
+ TypeDeclarationSyntax typeDeclaration,
+ MemberDeclarationSyntax memberDeclaration,
+ int index)
+ {
+ return typeDeclaration.WithMembers(
+ typeDeclaration.Members.Insert(index, memberDeclaration));
+ }
+
+ private SyntaxTokenList MakeFieldModifiers(bool isConstant, bool inScript)
+ {
+ if (isConstant)
+ {
+ return SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PrivateKeyword), SyntaxFactory.Token(SyntaxKind.ConstKeyword));
+ }
+ else if (inScript)
+ {
+ return SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PrivateKeyword), SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword));
+ }
+ else
+ {
+ return SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PrivateKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword), SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword));
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/CSharpIntroduceVariableService_IntroduceLocal.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/CSharpIntroduceVariableService_IntroduceLocal.cs
new file mode 100644
index 0000000000..728ca0a1df
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/CSharpIntroduceVariableService_IntroduceLocal.cs
@@ -0,0 +1,378 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Formatting;
+using Microsoft.CodeAnalysis.Options;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Simplification;
+using Roslyn.Utilities;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.IntroduceVariable
+{
+ public partial class CSharpIntroduceVariableService
+ {
+ protected override Task<Document> IntroduceLocalAsync(
+ SemanticDocument document,
+ ExpressionSyntax expression,
+ bool allOccurrences,
+ bool isConstant,
+ CancellationToken cancellationToken)
+ {
+ var options = document.Project.Solution.Workspace.Options;
+
+ var newLocalNameToken = (SyntaxToken)GenerateUniqueLocalName(document, expression, isConstant, cancellationToken);
+ var newLocalName = SyntaxFactory.IdentifierName(newLocalNameToken);
+
+ var modifiers = isConstant
+ ? SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.ConstKeyword))
+ : default(SyntaxTokenList);
+
+ var declarationStatement = SyntaxFactory.LocalDeclarationStatement(
+ modifiers,
+ SyntaxFactory.VariableDeclaration(
+ this.GetTypeSyntax(document, expression, isConstant, options, cancellationToken),
+ SyntaxFactory.SingletonSeparatedList(SyntaxFactory.VariableDeclarator(
+ newLocalNameToken.WithAdditionalAnnotations(RenameAnnotation.Create()),
+ null,
+ SyntaxFactory.EqualsValueClause(expression.WithoutTrailingTrivia().WithoutLeadingTrivia())))));
+
+ var anonymousMethodParameters = GetAnonymousMethodParameters(document, expression, cancellationToken);
+ var lambdas = anonymousMethodParameters.SelectMany(p => p.ContainingSymbol.DeclaringSyntaxReferences.Select(r => r.GetSyntax(cancellationToken)).AsEnumerable())
+ .Where(n => n is ParenthesizedLambdaExpressionSyntax || n is SimpleLambdaExpressionSyntax)
+ .ToSet();
+
+ var parentLambda = GetParentLambda(expression, lambdas);
+
+ if (parentLambda != null)
+ {
+ return Task.FromResult(IntroduceLocalDeclarationIntoLambda(
+ document, expression, newLocalName, declarationStatement, parentLambda, allOccurrences, cancellationToken));
+ }
+ else if (IsInExpressionBodiedMember(expression))
+ {
+ return Task.FromResult(RewriteExpressionBodiedMemberAndIntroduceLocalDeclaration(
+ document, expression, newLocalName, declarationStatement, allOccurrences, cancellationToken));
+ }
+ else
+ {
+ return IntroduceLocalDeclarationIntoBlockAsync(
+ document, expression, newLocalName, declarationStatement, allOccurrences, cancellationToken);
+ }
+ }
+
+ private Document IntroduceLocalDeclarationIntoLambda(
+ SemanticDocument document,
+ ExpressionSyntax expression,
+ IdentifierNameSyntax newLocalName,
+ LocalDeclarationStatementSyntax declarationStatement,
+ SyntaxNode oldLambda,
+ bool allOccurrences,
+ CancellationToken cancellationToken)
+ {
+ var oldBody = oldLambda is ParenthesizedLambdaExpressionSyntax
+ ? (ExpressionSyntax)((ParenthesizedLambdaExpressionSyntax)oldLambda).Body
+ : (ExpressionSyntax)((SimpleLambdaExpressionSyntax)oldLambda).Body;
+
+ var rewrittenBody = Rewrite(
+ document, expression, newLocalName, document, oldBody, allOccurrences, cancellationToken);
+
+ var delegateType = document.SemanticModel.GetTypeInfo(oldLambda, cancellationToken).ConvertedType as INamedTypeSymbol;
+
+ var newBody = delegateType != null && delegateType.DelegateInvokeMethod != null && delegateType.DelegateInvokeMethod.ReturnsVoid
+ ? SyntaxFactory.Block(declarationStatement)
+ : SyntaxFactory.Block(declarationStatement, SyntaxFactory.ReturnStatement(rewrittenBody));
+
+ newBody = newBody.WithAdditionalAnnotations(Formatter.Annotation);
+
+ var newLambda = oldLambda is ParenthesizedLambdaExpressionSyntax
+ ? ((ParenthesizedLambdaExpressionSyntax)oldLambda).WithBody(newBody)
+ : (SyntaxNode)((SimpleLambdaExpressionSyntax)oldLambda).WithBody(newBody);
+
+ var newRoot = document.Root.ReplaceNode(oldLambda, newLambda);
+ return document.Document.WithSyntaxRoot(newRoot);
+ }
+
+ private SyntaxNode GetParentLambda(ExpressionSyntax expression, ISet<SyntaxNode> lambdas)
+ {
+ var current = expression;
+ while (current != null)
+ {
+ if (lambdas.Contains(current.Parent))
+ {
+ return current.Parent;
+ }
+
+ current = current.Parent as ExpressionSyntax;
+ }
+
+ return null;
+ }
+
+ private TypeSyntax GetTypeSyntax(SemanticDocument document, ExpressionSyntax expression, bool isConstant, OptionSet options, CancellationToken cancellationToken)
+ {
+ var typeSymbol = GetTypeSymbol(document, expression, cancellationToken);
+ if (typeSymbol.ContainsAnonymousType())
+ {
+ return SyntaxFactory.IdentifierName("var");
+ }
+
+ if (!isConstant && true /*options.GetOption(CSharpCodeStyleOptions.UseVarWhenDeclaringLocals) */&& CanUseVar(typeSymbol))
+ {
+ return SyntaxFactory.IdentifierName("var");
+ }
+
+ return typeSymbol.GenerateTypeSyntax();
+ }
+
+ private bool CanUseVar(ITypeSymbol typeSymbol)
+ {
+ return typeSymbol.TypeKind != TypeKind.Delegate && !typeSymbol.IsErrorType();
+ }
+
+ private static async Task<Tuple<SemanticDocument, ISet<ExpressionSyntax>>> ComplexifyParentingStatements(
+ SemanticDocument semanticDocument,
+ ISet<ExpressionSyntax> matches,
+ CancellationToken cancellationToken)
+ {
+ // First, track the matches so that we can get back to them later.
+ var newRoot = semanticDocument.Root.TrackNodes(matches);
+ var newDocument = semanticDocument.Document.WithSyntaxRoot(newRoot);
+ var newSemanticDocument = await SemanticDocument.CreateAsync(newDocument, cancellationToken).ConfigureAwait(false);
+ var newMatches = newSemanticDocument.Root.GetCurrentNodes(matches.AsEnumerable()).ToSet();
+
+ // Next, expand the topmost parenting expression of each match, being careful
+ // not to expand the matches themselves.
+ var topMostExpressions = newMatches
+ .Select(m => m.AncestorsAndSelf().OfType<ExpressionSyntax>().Last())
+ .Distinct();
+
+ newRoot = await newSemanticDocument.Root
+ .ReplaceNodesAsync(
+ topMostExpressions,
+ computeReplacementAsync: async (oldNode, newNode, ct) =>
+ {
+ return await Simplifier
+ .ExpandAsync(
+ oldNode,
+ newSemanticDocument.Document,
+ expandInsideNode: node =>
+ {
+ var expression = node as ExpressionSyntax;
+ return expression == null
+ || !newMatches.Contains(expression);
+ },
+ cancellationToken: ct)
+ .ConfigureAwait(false);
+ },
+ cancellationToken: cancellationToken)
+ .ConfigureAwait(false);
+
+ newDocument = newSemanticDocument.Document.WithSyntaxRoot(newRoot);
+ newSemanticDocument = await SemanticDocument.CreateAsync(newDocument, cancellationToken).ConfigureAwait(false);
+ newMatches = newSemanticDocument.Root.GetCurrentNodes(matches.AsEnumerable()).ToSet();
+
+ return Tuple.Create(newSemanticDocument, newMatches);
+ }
+
+ private Document RewriteExpressionBodiedMemberAndIntroduceLocalDeclaration(
+ SemanticDocument document,
+ ExpressionSyntax expression,
+ NameSyntax newLocalName,
+ LocalDeclarationStatementSyntax declarationStatement,
+ bool allOccurrences,
+ CancellationToken cancellationToken)
+ {
+ var oldBody = expression.GetAncestorOrThis<ArrowExpressionClauseSyntax>();
+ var oldParentingNode = oldBody.Parent;
+ var leadingTrivia = oldBody.GetLeadingTrivia()
+ .AddRange(oldBody.ArrowToken.TrailingTrivia);
+
+ var newStatement = Rewrite(document, expression, newLocalName, document, oldBody.Expression, allOccurrences, cancellationToken);
+ var newBody = SyntaxFactory.Block(declarationStatement, SyntaxFactory.ReturnStatement(newStatement))
+ .WithLeadingTrivia(leadingTrivia)
+ .WithTrailingTrivia(oldBody.GetTrailingTrivia())
+ .WithAdditionalAnnotations(Formatter.Annotation);
+
+ SyntaxNode newParentingNode = null;
+ if (oldParentingNode is BasePropertyDeclarationSyntax)
+ {
+ var getAccessor = SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration, newBody);
+ var accessorList = SyntaxFactory.AccessorList(SyntaxFactory.List(new[] { getAccessor }));
+
+ newParentingNode = ((BasePropertyDeclarationSyntax)oldParentingNode).RemoveNode(oldBody, SyntaxRemoveOptions.KeepNoTrivia);
+
+ if (newParentingNode.IsKind(SyntaxKind.PropertyDeclaration))
+ {
+ var propertyDeclaration = ((PropertyDeclarationSyntax)newParentingNode);
+ newParentingNode = propertyDeclaration
+ .WithAccessorList(accessorList)
+ .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None))
+ .WithTrailingTrivia(propertyDeclaration.SemicolonToken.TrailingTrivia);
+ }
+ else if (newParentingNode.IsKind(SyntaxKind.IndexerDeclaration))
+ {
+ var indexerDeclaration = ((IndexerDeclarationSyntax)newParentingNode);
+ newParentingNode = indexerDeclaration
+ .WithAccessorList(accessorList)
+ .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None))
+ .WithTrailingTrivia(indexerDeclaration.SemicolonToken.TrailingTrivia);
+ }
+ }
+ else if (oldParentingNode is BaseMethodDeclarationSyntax)
+ {
+ newParentingNode = ((BaseMethodDeclarationSyntax)oldParentingNode)
+ .RemoveNode(oldBody, SyntaxRemoveOptions.KeepNoTrivia)
+ .WithBody(newBody);
+
+ if (newParentingNode.IsKind(SyntaxKind.MethodDeclaration))
+ {
+ var methodDeclaration = ((MethodDeclarationSyntax)newParentingNode);
+ newParentingNode = methodDeclaration
+ .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None))
+ .WithTrailingTrivia(methodDeclaration.SemicolonToken.TrailingTrivia);
+ }
+ else if (newParentingNode.IsKind(SyntaxKind.OperatorDeclaration))
+ {
+ var operatorDeclaration = ((OperatorDeclarationSyntax)newParentingNode);
+ newParentingNode = operatorDeclaration
+ .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None))
+ .WithTrailingTrivia(operatorDeclaration.SemicolonToken.TrailingTrivia);
+ }
+ else if (newParentingNode.IsKind(SyntaxKind.ConversionOperatorDeclaration))
+ {
+ var conversionOperatorDeclaration = ((ConversionOperatorDeclarationSyntax)newParentingNode);
+ newParentingNode = conversionOperatorDeclaration
+ .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None))
+ .WithTrailingTrivia(conversionOperatorDeclaration.SemicolonToken.TrailingTrivia);
+ }
+ }
+
+ var newRoot = document.Root.ReplaceNode(oldParentingNode, newParentingNode);
+ return document.Document.WithSyntaxRoot(newRoot);
+ }
+
+ private async Task<Document> IntroduceLocalDeclarationIntoBlockAsync(
+ SemanticDocument document,
+ ExpressionSyntax expression,
+ NameSyntax newLocalName,
+ LocalDeclarationStatementSyntax declarationStatement,
+ bool allOccurrences,
+ CancellationToken cancellationToken)
+ {
+ declarationStatement = declarationStatement.WithAdditionalAnnotations(Formatter.Annotation);
+
+ var oldOutermostBlock = expression.GetAncestorsOrThis<BlockSyntax>().LastOrDefault();
+ var matches = FindMatches(document, expression, document, oldOutermostBlock, allOccurrences, cancellationToken);
+ Debug.Assert(matches.Contains(expression));
+
+ var complexified = await ComplexifyParentingStatements(document, matches, cancellationToken).ConfigureAwait(false);
+ document = complexified.Item1;
+ matches = complexified.Item2;
+
+ // Our original expression should have been one of the matches, which were tracked as part
+ // of complexification, so we can retrieve the latest version of the expression here.
+ expression = document.Root.GetCurrentNodes(expression).First();
+
+ var innermostStatements = new HashSet<StatementSyntax>(
+ matches.Select(expr => expr.GetAncestorOrThis<StatementSyntax>()));
+
+ if (innermostStatements.Count == 1)
+ {
+ // If there was only one match, or all the matches came from the same
+ // statement, then we want to place the declaration right above that
+ // statement. Note: we special case this because the statement we are going
+ // to go above might not be in a block and we may have to generate it
+ return IntroduceLocalForSingleOccurrenceIntoBlock(
+ document, expression, newLocalName, declarationStatement, allOccurrences, cancellationToken);
+ }
+
+ var oldInnerMostCommonBlock = matches.FindInnermostCommonBlock();
+ var allAffectedStatements = new HashSet<StatementSyntax>(matches.SelectMany(expr => expr.GetAncestorsOrThis<StatementSyntax>()));
+ var firstStatementAffectedInBlock = oldInnerMostCommonBlock.Statements.First(allAffectedStatements.Contains);
+
+ var firstStatementAffectedIndex = oldInnerMostCommonBlock.Statements.IndexOf(firstStatementAffectedInBlock);
+
+ var newInnerMostBlock = Rewrite(
+ document, expression, newLocalName, document, oldInnerMostCommonBlock, allOccurrences, cancellationToken);
+
+ var statements = new List<StatementSyntax>();
+ statements.AddRange(newInnerMostBlock.Statements.Take(firstStatementAffectedIndex));
+ statements.Add(declarationStatement);
+ statements.AddRange(newInnerMostBlock.Statements.Skip(firstStatementAffectedIndex));
+
+ var finalInnerMostBlock = newInnerMostBlock.WithStatements(
+ SyntaxFactory.List<StatementSyntax>(statements));
+
+ var newRoot = document.Root.ReplaceNode(oldInnerMostCommonBlock, finalInnerMostBlock);
+ return document.Document.WithSyntaxRoot(newRoot);
+ }
+
+ private Document IntroduceLocalForSingleOccurrenceIntoBlock(
+ SemanticDocument document,
+ ExpressionSyntax expression,
+ NameSyntax localName,
+ LocalDeclarationStatementSyntax localDeclaration,
+ bool allOccurrences,
+ CancellationToken cancellationToken)
+ {
+ var oldStatement = expression.GetAncestorOrThis<StatementSyntax>();
+ var newStatement = Rewrite(
+ document, expression, localName, document, oldStatement, allOccurrences, cancellationToken);
+
+ if (oldStatement.IsParentKind(SyntaxKind.Block))
+ {
+ var oldBlock = oldStatement.Parent as BlockSyntax;
+ var statementIndex = oldBlock.Statements.IndexOf(oldStatement);
+
+ var newBlock = oldBlock.WithStatements(CreateNewStatementList(
+ oldBlock.Statements, localDeclaration, newStatement, statementIndex));
+
+ var newRoot = document.Root.ReplaceNode(oldBlock, newBlock);
+ return document.Document.WithSyntaxRoot(newRoot);
+ }
+ else if (oldStatement.IsParentKind(SyntaxKind.SwitchSection))
+ {
+ var oldSwitchSection = oldStatement.Parent as SwitchSectionSyntax;
+ var statementIndex = oldSwitchSection.Statements.IndexOf(oldStatement);
+
+ var newSwitchSection = oldSwitchSection.WithStatements(CreateNewStatementList(
+ oldSwitchSection.Statements, localDeclaration, newStatement, statementIndex));
+
+ var newRoot = document.Root.ReplaceNode(oldSwitchSection, newSwitchSection);
+ return document.Document.WithSyntaxRoot(newRoot);
+ }
+ else
+ {
+ // we need to introduce a block to put the original statement, along with
+ // the statement we're generating
+ var newBlock = SyntaxFactory.Block(localDeclaration, newStatement).WithAdditionalAnnotations(Formatter.Annotation);
+
+ var newRoot = document.Root.ReplaceNode(oldStatement, newBlock);
+ return document.Document.WithSyntaxRoot(newRoot);
+ }
+ }
+
+ private static SyntaxList<StatementSyntax> CreateNewStatementList(
+ SyntaxList<StatementSyntax> oldStatements,
+ LocalDeclarationStatementSyntax localDeclaration,
+ StatementSyntax newStatement,
+ int statementIndex)
+ {
+ return oldStatements.Take(statementIndex)
+ .Concat(localDeclaration.WithLeadingTrivia(oldStatements.Skip(statementIndex).First().GetLeadingTrivia()))
+ .Concat(newStatement.WithoutLeadingTrivia())
+ .Concat(oldStatements.Skip(statementIndex + 1))
+ .ToSyntaxList();
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/CSharpIntroduceVariableService_IntroduceQueryLocal.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/CSharpIntroduceVariableService_IntroduceQueryLocal.cs
new file mode 100644
index 0000000000..4bf767422f
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/CSharpIntroduceVariableService_IntroduceQueryLocal.cs
@@ -0,0 +1,107 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Formatting;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Roslyn.Utilities;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.IntroduceVariable
+{
+ public partial class CSharpIntroduceVariableService
+ {
+ private static bool IsAnyQueryClause(SyntaxNode node)
+ {
+ return node is QueryClauseSyntax || node is SelectOrGroupClauseSyntax;
+ }
+
+ protected override Task<Document> IntroduceQueryLocalAsync(
+ SemanticDocument document, ExpressionSyntax expression, bool allOccurrences, CancellationToken cancellationToken)
+ {
+ var newLocalNameToken = (SyntaxToken)GenerateUniqueLocalName(document, expression, isConstant: false, cancellationToken: cancellationToken);
+ var newLocalName = SyntaxFactory.IdentifierName(newLocalNameToken);
+
+ var letClause = SyntaxFactory.LetClause(
+ newLocalNameToken.WithAdditionalAnnotations(RenameAnnotation.Create()),
+ expression).WithAdditionalAnnotations(Formatter.Annotation);
+
+ var oldOutermostQuery = expression.GetAncestorsOrThis<QueryExpressionSyntax>().LastOrDefault();
+ var matches = FindMatches(document, expression, document, oldOutermostQuery, allOccurrences, cancellationToken);
+ var innermostClauses = new HashSet<SyntaxNode>(
+ matches.Select(expr => expr.GetAncestorsOrThis<SyntaxNode>().First(IsAnyQueryClause)));
+
+ if (innermostClauses.Count == 1)
+ {
+ // If there was only one match, or all the matches came from the same
+ // statement, hten we want to place the declaration right above that
+ // statement. Note: we special case this because the statement we are going
+ // to go above might not be in a block and we may have to generate it
+ return Task.FromResult(IntroduceQueryLocalForSingleOccurrence(
+ document, expression, newLocalName, letClause, allOccurrences, cancellationToken));
+ }
+
+ var oldInnerMostCommonQuery = matches.FindInnermostCommonNode<QueryExpressionSyntax>();
+ var newInnerMostQuery = Rewrite(
+ document, expression, newLocalName, document, oldInnerMostCommonQuery, allOccurrences, cancellationToken);
+
+ var allAffectedClauses = new HashSet<SyntaxNode>(matches.SelectMany(expr => expr.GetAncestorsOrThis<SyntaxNode>().Where(IsAnyQueryClause)));
+
+ var oldClauses = oldInnerMostCommonQuery.GetAllClauses();
+ var newClauses = newInnerMostQuery.GetAllClauses();
+
+ var firstClauseAffectedInQuery = oldClauses.First(allAffectedClauses.Contains);
+ var firstClauseAffectedIndex = oldClauses.IndexOf(firstClauseAffectedInQuery);
+
+ var finalClauses = newClauses.Take(firstClauseAffectedIndex)
+ .Concat(letClause)
+ .Concat(newClauses.Skip(firstClauseAffectedIndex)).ToList();
+
+ var finalQuery = newInnerMostQuery.WithAllClauses(finalClauses);
+ var newRoot = document.Root.ReplaceNode(oldInnerMostCommonQuery, finalQuery);
+
+ return Task.FromResult(document.Document.WithSyntaxRoot(newRoot));
+ }
+
+ private Document IntroduceQueryLocalForSingleOccurrence(
+ SemanticDocument document,
+ ExpressionSyntax expression,
+ NameSyntax newLocalName,
+ LetClauseSyntax letClause,
+ bool allOccurrences,
+ CancellationToken cancellationToken)
+ {
+ var oldClause = expression.GetAncestors<SyntaxNode>().First(IsAnyQueryClause);
+ var newClause = Rewrite(
+ document, expression, newLocalName, document, oldClause, allOccurrences, cancellationToken);
+
+ var oldQuery = (QueryBodySyntax)oldClause.Parent;
+ var newQuery = GetNewQuery(oldQuery, oldClause, newClause, letClause);
+
+ var newRoot = document.Root.ReplaceNode(oldQuery, newQuery);
+ return document.Document.WithSyntaxRoot(newRoot);
+ }
+
+ private static QueryBodySyntax GetNewQuery(
+ QueryBodySyntax oldQuery,
+ SyntaxNode oldClause,
+ SyntaxNode newClause,
+ LetClauseSyntax letClause)
+ {
+ var oldClauses = oldQuery.GetAllClauses();
+ var oldClauseIndex = oldClauses.IndexOf(oldClause);
+
+ var newClauses = oldClauses.Take(oldClauseIndex)
+ .Concat(letClause)
+ .Concat(newClause)
+ .Concat(oldClauses.Skip(oldClauseIndex + 1)).ToList();
+ return oldQuery.WithAllClauses(newClauses);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/IntroduceVariableResult.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/IntroduceVariableResult.cs
new file mode 100644
index 0000000000..cb90794993
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IntroduceVariable/IntroduceVariableResult.cs
@@ -0,0 +1,33 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading;
+using ICSharpCode.NRefactory6.CSharp.Refactoring;
+using Microsoft.CodeAnalysis.CodeRefactorings;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.IntroduceVariable
+{
+ public class IntroduceVariableResult
+ {
+ public static readonly IntroduceVariableResult Failure = new IntroduceVariableResult(null);
+
+ private readonly CodeRefactoring _codeRefactoring;
+
+ public IntroduceVariableResult(CodeRefactoring codeRefactoring)
+ {
+ _codeRefactoring = codeRefactoring;
+ }
+
+ public bool ContainsChanges
+ {
+ get
+ {
+ return _codeRefactoring != null;
+ }
+ }
+
+ public CodeRefactoring GetCodeRefactoring(CancellationToken cancellationToken)
+ {
+ return _codeRefactoring;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/OrganizeImports/CSharpOrganizeImportsService.Rewriter.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/OrganizeImports/CSharpOrganizeImportsService.Rewriter.cs
new file mode 100644
index 0000000000..340d1fd1d8
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/OrganizeImports/CSharpOrganizeImportsService.Rewriter.cs
@@ -0,0 +1,87 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.OrganizeImports
+{
+ public partial class CSharpOrganizeImportsService
+ {
+ private class Rewriter : CSharpSyntaxRewriter
+ {
+ private readonly bool _placeSystemNamespaceFirst;
+ public readonly IList<TextChange> TextChanges = new List<TextChange>();
+
+ public Rewriter(bool placeSystemNamespaceFirst)
+ {
+ _placeSystemNamespaceFirst = placeSystemNamespaceFirst;
+ }
+
+ public override SyntaxNode VisitCompilationUnit(CompilationUnitSyntax node)
+ {
+ node = (CompilationUnitSyntax)base.VisitCompilationUnit(node);
+
+ SyntaxList<ExternAliasDirectiveSyntax> organizedExternAliasList;
+ SyntaxList<UsingDirectiveSyntax> organizedUsingList;
+ UsingsAndExternAliasesOrganizer.Organize(
+ node.Externs, node.Usings, _placeSystemNamespaceFirst,
+ out organizedExternAliasList, out organizedUsingList);
+
+ var result = node.WithExterns(organizedExternAliasList).WithUsings(organizedUsingList);
+ if (node != result)
+ {
+ AddTextChange(node.Externs, organizedExternAliasList);
+ AddTextChange(node.Usings, organizedUsingList);
+ }
+
+ return result;
+ }
+
+ public override SyntaxNode VisitNamespaceDeclaration(NamespaceDeclarationSyntax node)
+ {
+ node = (NamespaceDeclarationSyntax)base.VisitNamespaceDeclaration(node);
+
+ SyntaxList<ExternAliasDirectiveSyntax> organizedExternAliasList;
+ SyntaxList<UsingDirectiveSyntax> organizedUsingList;
+ UsingsAndExternAliasesOrganizer.Organize(
+ node.Externs, node.Usings, _placeSystemNamespaceFirst,
+ out organizedExternAliasList, out organizedUsingList);
+
+ var result = node.WithExterns(organizedExternAliasList).WithUsings(organizedUsingList);
+ if (node != result)
+ {
+ AddTextChange(node.Externs, organizedExternAliasList);
+ AddTextChange(node.Usings, organizedUsingList);
+ }
+
+ return result;
+ }
+
+ private void AddTextChange<TSyntax>(SyntaxList<TSyntax> list, SyntaxList<TSyntax> organizedList)
+ where TSyntax : SyntaxNode
+ {
+ if (list.Count > 0)
+ {
+ this.TextChanges.Add(new TextChange(GetTextSpan(list), GetNewText(organizedList)));
+ }
+ }
+
+ private string GetNewText<TSyntax>(SyntaxList<TSyntax> organizedList)
+ where TSyntax : SyntaxNode
+ {
+ return string.Join(string.Empty, organizedList.Select(t => t.ToFullString()));
+ }
+
+ private TextSpan GetTextSpan<TSyntax>(SyntaxList<TSyntax> list)
+ where TSyntax : SyntaxNode
+ {
+ return TextSpan.FromBounds(list.First().FullSpan.Start, list.Last().FullSpan.End);
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/OrganizeImports/CSharpOrganizeImportsService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/OrganizeImports/CSharpOrganizeImportsService.cs
new file mode 100644
index 0000000000..956af82e90
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/OrganizeImports/CSharpOrganizeImportsService.cs
@@ -0,0 +1,56 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Composition;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.Host;
+using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.OrganizeImports
+{
+ public partial class CSharpOrganizeImportsService
+ {
+ public async Task<Document> OrganizeImportsAsync(Document document, bool placeSystemNamespaceFirst, CancellationToken cancellationToken)
+ {
+ var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
+ var rewriter = new Rewriter(placeSystemNamespaceFirst);
+ var newRoot = rewriter.Visit(root);
+
+ return document.WithSyntaxRoot(newRoot);
+ }
+
+ public string OrganizeImportsDisplayStringWithAccelerator
+ {
+ get
+ {
+ return Resources.OrganizeUsingsWithAccelerator;
+ }
+ }
+
+ public string SortImportsDisplayStringWithAccelerator
+ {
+ get
+ {
+ return Resources.SortUsingsWithAccelerator;
+ }
+ }
+
+ public string RemoveUnusedImportsDisplayStringWithAccelerator
+ {
+ get
+ {
+ return Resources.RemoveUnnecessaryUsingsWithAccelerator;
+ }
+ }
+
+ public string SortAndRemoveUnusedImportsDisplayStringWithAccelerator
+ {
+ get
+ {
+ return Resources.RemoveAndSortUsingsWithAccelerator;
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ParameterHinting/IParameterHintingData.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ParameterHinting/IParameterHintingData.cs
new file mode 100644
index 0000000000..16f5eb956f
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ParameterHinting/IParameterHintingData.cs
@@ -0,0 +1,229 @@
+//
+// IParameterDataProvider.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2011 Xamarin Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using Microsoft.CodeAnalysis;
+using System.Collections.Immutable;
+using System.Linq;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ /// <summary>
+ /// Provides intellisense information for a collection of parametrized members.
+ /// </summary>
+ public interface IParameterHintingData
+ {
+ /// <summary>
+ /// Gets the symbol for which the parameter should be created.
+ /// </summary>
+ ISymbol Symbol {
+ get;
+ }
+
+ int ParameterCount {
+ get;
+ }
+
+ bool IsParameterListAllowed {
+ get;
+ }
+
+ string GetParameterName (int currentParameter);
+ }
+
+ public class ParameterHintingData : IParameterHintingData
+ {
+ public ISymbol Symbol {
+ get;
+ private set;
+ }
+
+ public ParameterHintingData(IMethodSymbol symbol)
+ {
+ this.Symbol = symbol;
+ }
+
+ public ParameterHintingData(IPropertySymbol symbol)
+ {
+ this.Symbol = symbol;
+ }
+
+ static ImmutableArray<IParameterSymbol> GetParameterList (IParameterHintingData data)
+ {
+ var ms = data.Symbol as IMethodSymbol;
+ if (ms != null)
+ return ms.Parameters;
+
+ var ps = data.Symbol as IPropertySymbol;
+ if (ps != null)
+ return ps.Parameters;
+
+ return ImmutableArray<IParameterSymbol>.Empty;
+ }
+
+ public string GetParameterName (int currentParameter)
+ {
+ var list = GetParameterList (this);
+ if (currentParameter < 0 || currentParameter >= list.Length)
+ throw new ArgumentOutOfRangeException ("currentParameter");
+ return list [currentParameter].Name;
+ }
+
+ public int ParameterCount {
+ get {
+ return GetParameterList(this).Length;
+ }
+ }
+
+ public bool IsParameterListAllowed {
+ get {
+ var param = GetParameterList(this).LastOrDefault();
+ return param != null && param.IsParams;
+ }
+ }
+ }
+
+ public class DelegateParameterHintingData : IParameterHintingData
+ {
+ readonly IMethodSymbol invocationMethod;
+
+ public ISymbol Symbol {
+ get;
+ private set;
+ }
+
+ public DelegateParameterHintingData(ITypeSymbol symbol)
+ {
+ this.Symbol = symbol;
+ this.invocationMethod = symbol.GetDelegateInvokeMethod();
+ }
+
+ public string GetParameterName (int currentParameter)
+ {
+ var list = invocationMethod.Parameters;
+ if (currentParameter < 0 || currentParameter >= list.Length)
+ throw new ArgumentOutOfRangeException ("currentParameter");
+ return list [currentParameter].Name;
+ }
+
+ public int ParameterCount {
+ get {
+ return invocationMethod.Parameters.Length;
+ }
+ }
+
+ public bool IsParameterListAllowed {
+ get {
+ var param = invocationMethod.Parameters.LastOrDefault();
+ return param != null && param.IsParams;
+ }
+ }
+ }
+
+ public class ArrayParameterHintingData : IParameterHintingData
+ {
+ readonly IArrayTypeSymbol arrayType;
+
+ public ISymbol Symbol {
+ get {
+ return arrayType;
+ }
+ }
+
+ public ArrayParameterHintingData(IArrayTypeSymbol arrayType)
+ {
+ this.arrayType = arrayType;
+ }
+
+ public string GetParameterName (int currentParameter)
+ {
+ return null;
+ }
+
+ public int ParameterCount {
+ get {
+ return arrayType.Rank;
+ }
+ }
+
+ public bool IsParameterListAllowed {
+ get {
+ return false;
+ }
+ }
+ }
+
+ public class TypeParameterHintingData : IParameterHintingData
+ {
+ public ISymbol Symbol {
+ get;
+ private set;
+ }
+
+ public TypeParameterHintingData(IMethodSymbol symbol)
+ {
+ this.Symbol = symbol;
+ }
+
+ public TypeParameterHintingData(INamedTypeSymbol symbol)
+ {
+ this.Symbol = symbol;
+ }
+
+ static ImmutableArray<ITypeParameterSymbol> GetTypeParameterList (IParameterHintingData data)
+ {
+ var ms = data.Symbol as IMethodSymbol;
+ if (ms != null)
+ return ms.TypeParameters;
+
+ var ps = data.Symbol as INamedTypeSymbol;
+ if (ps != null)
+ return ps.TypeParameters;
+
+ return ImmutableArray<ITypeParameterSymbol>.Empty;
+ }
+
+
+ public string GetParameterName (int currentParameter)
+ {
+ var list = GetTypeParameterList (this);
+ if (currentParameter < 0 || currentParameter >= list.Length)
+ throw new ArgumentOutOfRangeException ("currentParameter");
+ return list [currentParameter].Name;
+ }
+
+ public int ParameterCount {
+ get {
+ return GetTypeParameterList(this).Length;
+ }
+ }
+
+ public bool IsParameterListAllowed {
+ get {
+ return false;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ParameterHinting/IParameterHintingDataFactory.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ParameterHinting/IParameterHintingDataFactory.cs
new file mode 100644
index 0000000000..4995e19c3c
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ParameterHinting/IParameterHintingDataFactory.cs
@@ -0,0 +1,47 @@
+//
+// IParameterCopmletionFactory.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2011 Xamarin Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System.Collections.Generic;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ public interface IParameterHintingDataFactory
+ {
+ IParameterHintingData CreateConstructorProvider (IMethodSymbol constructor);
+
+ IParameterHintingData CreateMethodDataProvider (IMethodSymbol method);
+
+ IParameterHintingData CreateDelegateDataProvider (ITypeSymbol delegateType);
+
+ IParameterHintingData CreateIndexerParameterDataProvider (IPropertySymbol indexer, SyntaxNode resolvedNode);
+
+ IParameterHintingData CreateArrayDataProvider (IArrayTypeSymbol arrayType);
+
+ IParameterHintingData CreateTypeParameterDataProvider (INamedTypeSymbol type);
+
+ IParameterHintingData CreateTypeParameterDataProvider (IMethodSymbol method);
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ParameterHinting/ParameterHintingEngine.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ParameterHinting/ParameterHintingEngine.cs
new file mode 100644
index 0000000000..42fc3fe4cd
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ParameterHinting/ParameterHintingEngine.cs
@@ -0,0 +1,255 @@
+//
+// CSharpParameterCompletionEngine.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2011 Xamarin Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using System.Threading;
+using System.CodeDom;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp;
+using System.Threading.Tasks;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ public class ParameterHintingEngine
+ {
+ readonly IParameterHintingDataFactory factory;
+ readonly Workspace workspace;
+
+ public ParameterHintingEngine(Workspace workspace, IParameterHintingDataFactory factory)
+ {
+ if (workspace == null)
+ throw new ArgumentNullException("workspace");
+ if (factory == null)
+ throw new ArgumentNullException("factory");
+ this.workspace = workspace;
+ this.factory = factory;
+ }
+
+ public async Task<ParameterHintingResult> GetParameterDataProviderAsync(Document document, SemanticModel semanticModel, int position, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
+ if (tree.IsInNonUserCode(position, cancellationToken))
+ return ParameterHintingResult.Empty;
+ var tokenLeftOfPosition = tree.FindTokenOnLeftOfPosition (position, cancellationToken);
+
+ if (tokenLeftOfPosition.IsKind (SyntaxKind.LessThanToken)) {
+ var startToken = tokenLeftOfPosition.GetPreviousToken();
+ return HandleTypeParameterCase(semanticModel, startToken.Parent, cancellationToken);
+ }
+
+ var context = SyntaxContext.Create(workspace, document, semanticModel, position, cancellationToken);
+ var targetParent = context.TargetToken.Parent;
+ var node = targetParent.Parent;
+ // case: identifier<arg1,|
+ if (node == null) {
+ if (context.LeftToken.Kind() == SyntaxKind.CommaToken) {
+ targetParent = context.LeftToken.GetPreviousToken().Parent;
+ node = targetParent.Parent;
+ if (node.Kind() == SyntaxKind.LessThanExpression) {
+ return HandleTypeParameterCase(semanticModel, ((BinaryExpressionSyntax)node).Left, cancellationToken);
+
+ }
+ }
+ return ParameterHintingResult.Empty;
+ }
+ if (node.IsKind (SyntaxKind.Argument))
+ node = node.Parent.Parent;
+ switch (node.Kind()) {
+ case SyntaxKind.Attribute:
+ return HandleAttribute(semanticModel, node, cancellationToken);
+ case SyntaxKind.ThisConstructorInitializer:
+ case SyntaxKind.BaseConstructorInitializer:
+ return HandleConstructorInitializer(semanticModel, node, cancellationToken);
+ case SyntaxKind.ObjectCreationExpression:
+ return HandleObjectCreationExpression(semanticModel, node, cancellationToken);
+ case SyntaxKind.InvocationExpression:
+ return HandleInvocationExpression(semanticModel, (InvocationExpressionSyntax)node, cancellationToken);
+ case SyntaxKind.ElementAccessExpression:
+ return HandleElementAccessExpression(semanticModel, (ElementAccessExpressionSyntax)node, cancellationToken);
+ }
+ return ParameterHintingResult.Empty;
+ }
+
+ ParameterHintingResult HandleInvocationExpression(SemanticModel semanticModel, InvocationExpressionSyntax node, CancellationToken cancellationToken)
+ {
+ var info = semanticModel.GetSymbolInfo(node, cancellationToken);
+ var result = new ParameterHintingResult(node.SpanStart);
+
+ var targetTypeInfo = semanticModel.GetTypeInfo (node.Expression);
+ if (targetTypeInfo.Type != null && targetTypeInfo.Type.TypeKind == TypeKind.Delegate) {
+ result.AddData (factory.CreateMethodDataProvider (targetTypeInfo.Type.GetDelegateInvokeMethod ()));
+ return result;
+ }
+
+ var within = semanticModel.GetEnclosingNamedTypeOrAssembly(node.SpanStart, cancellationToken);
+ ITypeSymbol type;
+ var ma = node.Expression as MemberAccessExpressionSyntax;
+ string name = null;
+ bool staticLookup = false;
+ if (ma != null) {
+ staticLookup = semanticModel.GetSymbolInfo (ma.Expression).Symbol is ITypeSymbol;
+ type = semanticModel.GetTypeInfo (ma.Expression).Type;
+ name = ma.Name.ToString ();
+ } else {
+ type = within as ITypeSymbol;
+ name = node.Expression.ToString ();
+ var sym = semanticModel.GetEnclosingSymbol (node.SpanStart, cancellationToken);
+ staticLookup = sym.IsStatic;
+ }
+ var addedMethods = new List<IMethodSymbol> ();
+ var filterMethod = new HashSet<IMethodSymbol> ();
+ for (;type != null; type = type.BaseType) {
+ foreach (var method in type.GetMembers ().OfType<IMethodSymbol> ().Where (m => m.Name == name)) {
+ if (staticLookup && !method.IsStatic)
+ continue;
+ if (method.OverriddenMethod != null)
+ filterMethod.Add (method.OverriddenMethod);
+ if (filterMethod.Contains (method))
+ continue;
+ if (addedMethods.Any (added => SignatureComparer.HaveSameSignature (method, added, true)))
+ continue;
+ if (method.IsAccessibleWithin (within)) {
+ addedMethods.Add (method);
+ result.AddData (factory.CreateMethodDataProvider (method));
+ }
+ }
+ }
+ if (info.Symbol != null && !addedMethods.Contains (info.Symbol)) {
+ if (!staticLookup || info.Symbol.IsStatic)
+ result.AddData (factory.CreateMethodDataProvider ((IMethodSymbol)info.Symbol));
+ }
+ foreach (var candidate in info.CandidateSymbols) {
+ if (staticLookup && !candidate.IsStatic)
+ continue;
+
+ if (!addedMethods.Contains (candidate) && candidate.IsAccessibleWithin (within))
+ result.AddData (factory.CreateMethodDataProvider ((IMethodSymbol)candidate));
+ }
+ return result;
+ }
+
+ ParameterHintingResult HandleTypeParameterCase(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
+ {
+ var result = new ParameterHintingResult(node.SpanStart);
+ string typeName;
+ var gns = node as GenericNameSyntax;
+ if (gns != null) {
+ typeName = gns.Identifier.ToString ();
+ } else {
+ typeName = node.ToString ();
+ }
+
+ foreach (var cand in semanticModel.LookupSymbols (node.SpanStart).OfType<INamedTypeSymbol> ()) {
+ if (cand.TypeParameters.Length == 0)
+ continue;
+ if (cand.Name == typeName || cand.GetFullName () == typeName)
+ result.AddData(factory.CreateTypeParameterDataProvider(cand));
+ }
+
+ if (result.Count == 0) {
+ foreach (var cand in semanticModel.LookupSymbols (node.SpanStart).OfType<IMethodSymbol> ()) {
+ if (cand.TypeParameters.Length == 0)
+ continue;
+ if (cand.Name == typeName)
+ result.AddData (factory.CreateTypeParameterDataProvider (cand));
+ }
+ }
+ return result;
+ }
+
+ ParameterHintingResult HandleAttribute(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
+ {
+ var info = semanticModel.GetSymbolInfo(node, cancellationToken);
+ var result = new ParameterHintingResult(node.SpanStart);
+ var resolvedMethod = info.Symbol as IMethodSymbol;
+ if (resolvedMethod != null)
+ result.AddData(factory.CreateConstructorProvider(resolvedMethod));
+ result.AddRange(info.CandidateSymbols.OfType<IMethodSymbol>().Select (m => factory.CreateConstructorProvider(m)));
+ return result;
+ }
+
+ ParameterHintingResult HandleConstructorInitializer(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
+ {
+ var info = semanticModel.GetSymbolInfo(node, cancellationToken);
+ var result = new ParameterHintingResult(node.SpanStart);
+
+ var resolvedMethod = info.Symbol as IMethodSymbol;
+ if (resolvedMethod != null)
+ result.AddData(factory.CreateConstructorProvider(resolvedMethod));
+ result.AddRange(info.CandidateSymbols.OfType<IMethodSymbol>().Select (m => factory.CreateConstructorProvider(m)));
+ return result;
+ }
+
+ ParameterHintingResult HandleElementAccessExpression(SemanticModel semanticModel, ElementAccessExpressionSyntax node, CancellationToken cancellationToken)
+ {
+ var within = semanticModel.GetEnclosingNamedTypeOrAssembly(node.SpanStart, cancellationToken);
+
+ var targetTypeInfo = semanticModel.GetTypeInfo (node.Expression);
+ ITypeSymbol type = targetTypeInfo.Type;
+ if (type == null)
+ return ParameterHintingResult.Empty;
+
+ var result = new ParameterHintingResult(node.SpanStart);
+ if (type.TypeKind == TypeKind.Array) {
+ result.AddData (factory.CreateArrayDataProvider ((IArrayTypeSymbol)type));
+ return result;
+ }
+
+ var addedProperties = new List<IPropertySymbol> ();
+ for (;type != null; type = type.BaseType) {
+ foreach (var indexer in type.GetMembers ().OfType<IPropertySymbol> ().Where (p => p.IsIndexer)) {
+ if (addedProperties.Any (added => SignatureComparer.HaveSameSignature (indexer, added, true)))
+ continue;
+
+ if (indexer.IsAccessibleWithin (within)) {
+ addedProperties.Add (indexer);
+ result.AddData (factory.CreateIndexerParameterDataProvider (indexer, node));
+ }
+ }
+ }
+ return result;
+ }
+
+ ParameterHintingResult HandleObjectCreationExpression (SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
+ {
+ // var info = semanticModel.GetSymbolInfo(node, cancellationToken);
+ var result = new ParameterHintingResult(node.SpanStart);
+ var within = semanticModel.GetEnclosingNamedTypeOrAssembly(node.SpanStart, cancellationToken);
+
+ var targetTypeInfo = semanticModel.GetTypeInfo (node);
+ if (targetTypeInfo.Type != null) {
+ foreach (IMethodSymbol c in targetTypeInfo.Type.GetMembers().OfType<IMethodSymbol>().Where(m => m.MethodKind == MethodKind.Constructor)) {
+ if (c.IsAccessibleWithin (within)) {
+ result.AddData(factory.CreateConstructorProvider(c));
+ }
+ }
+ }
+ return result;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ParameterHinting/ParameterHintingResult.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ParameterHinting/ParameterHintingResult.cs
new file mode 100644
index 0000000000..3b9283cecd
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ParameterHinting/ParameterHintingResult.cs
@@ -0,0 +1,87 @@
+//
+// ParameterHintingResult.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2014 Xamarin Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Collections.Generic;
+
+namespace ICSharpCode.NRefactory6.CSharp.Completion
+{
+ public class ParameterHintingResult : IReadOnlyList<IParameterHintingData>
+ {
+ public static readonly ParameterHintingResult Empty = new ParameterHintingResult (-1);
+
+ readonly List<IParameterHintingData> data = new List<IParameterHintingData> ();
+
+ /// <summary>
+ /// Gets the start offset of the parameter expression node.
+ /// </summary>
+ public int StartOffset {
+ get;
+ private set;
+ }
+
+ #region IReadOnlyList<IParameterHintingData> implementation
+
+ public IEnumerator<IParameterHintingData> GetEnumerator()
+ {
+ return data.GetEnumerator();
+ }
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+ {
+ return ((System.Collections.IEnumerable)data).GetEnumerator();
+ }
+
+ public IParameterHintingData this[int index] {
+ get {
+ return data [index];
+ }
+ }
+
+ public int Count {
+ get {
+ return data.Count;
+ }
+ }
+
+ #endregion
+
+ internal protected ParameterHintingResult(int startOffset)
+ {
+ this.StartOffset = startOffset;
+ }
+
+ internal protected void AddData (IParameterHintingData parameterHintingData)
+ {
+ data.Add(parameterHintingData);
+ }
+
+ internal protected void AddRange (IEnumerable<IParameterHintingData> parameterHintingDataCollection)
+ {
+ data.AddRange(parameterHintingDataCollection);
+ }
+ }
+}
+
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ParameterHinting/ParameterUtil.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ParameterHinting/ParameterUtil.cs
new file mode 100644
index 0000000000..dbb75a56d9
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/ParameterHinting/ParameterUtil.cs
@@ -0,0 +1,215 @@
+//
+// ParameterUtil.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2014 Xamarin Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Collections.Generic;
+using Microsoft.CodeAnalysis;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace ICSharpCode.NRefactory6.CSharp
+{
+ public class ParameterIndexResult
+ {
+ public readonly static ParameterIndexResult Invalid = new ParameterIndexResult (null, -1);
+ public readonly static ParameterIndexResult First = new ParameterIndexResult (null, 0);
+
+ public readonly string[] UsedNamespaceParameters;
+ public readonly int ParameterIndex;
+
+
+ internal ParameterIndexResult(string[] usedNamespaceParameters, int parameterIndex)
+ {
+ UsedNamespaceParameters = usedNamespaceParameters;
+ ParameterIndex = parameterIndex;
+ }
+ }
+
+ public static class ParameterUtil
+ {
+ public static async Task<ParameterIndexResult> GetCurrentParameterIndex (Document document, int startOffset, int caretOffset, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ var usedNamedParameters =new List<string> ();
+ var parameter = new Stack<int> ();
+ var bracketStack = new Stack<Stack<int>> ();
+ bool inSingleComment = false, inString = false, inVerbatimString = false, inChar = false, inMultiLineComment = false;
+ var word = new StringBuilder ();
+ bool foundCharAfterOpenBracket = false;
+
+ var text = await document.GetTextAsync(cancellationToken);
+ for (int i = startOffset; i < caretOffset; i++) {
+ char ch = text[i];
+ char nextCh = i + 1 < text.Length ? text[i + 1] : '\0';
+ if (ch == ':') {
+ usedNamedParameters.Add (word.ToString ());
+ word.Length = 0;
+ } else if (char.IsLetterOrDigit (ch) || ch =='_') {
+ word.Append (ch);
+ } else if (char.IsWhiteSpace (ch)) {
+
+ } else {
+ word.Length = 0;
+ }
+ if (!char.IsWhiteSpace(ch) && parameter.Count > 0)
+ foundCharAfterOpenBracket = true;
+
+ switch (ch) {
+ case '{':
+ if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) {
+ break;
+ }
+ bracketStack.Push (parameter);
+ parameter = new Stack<int> ();
+ break;
+ case '[':
+ case '(':
+ if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) {
+ break;
+ }
+ parameter.Push (0);
+ break;
+ case '}':
+ if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) {
+ break;
+ }
+ if (bracketStack.Count > 0) {
+ parameter = bracketStack.Pop ();
+ } else {
+ return ParameterIndexResult.Invalid;
+ }
+ break;
+ case ']':
+ case ')':
+ if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) {
+ break;
+ }
+ if (parameter.Count > 0) {
+ parameter.Pop ();
+ } else {
+ return ParameterIndexResult.Invalid;
+ }
+ break;
+ case '<':
+ if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) {
+ break;
+ }
+ parameter.Push (0);
+ break;
+ case '=':
+ if (nextCh == '>') {
+ i++;
+ continue;
+ }
+ break;
+ case '>':
+ if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) {
+ break;
+ }
+ if (parameter.Count > 0) {
+ parameter.Pop ();
+ }
+ break;
+ case ',':
+ if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) {
+ break;
+ }
+ if (parameter.Count > 0) {
+ parameter.Push (parameter.Pop () + 1);
+ }
+ break;
+ case '/':
+ if (inString || inChar || inVerbatimString) {
+ break;
+ }
+ if (nextCh == '/') {
+ i++;
+ inSingleComment = true;
+ }
+ if (nextCh == '*') {
+ inMultiLineComment = true;
+ }
+ break;
+ case '*':
+ if (inString || inChar || inVerbatimString || inSingleComment) {
+ break;
+ }
+ if (nextCh == '/') {
+ i++;
+ inMultiLineComment = false;
+ }
+ break;
+ case '@':
+ if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) {
+ break;
+ }
+ if (nextCh == '"') {
+ i++;
+ inVerbatimString = true;
+ }
+ break;
+ case '\\':
+ if (inString || inChar) {
+ i++;
+ }
+ break;
+ case '"':
+ if (inSingleComment || inMultiLineComment || inChar) {
+ break;
+ }
+ if (inVerbatimString) {
+ if (nextCh == '"') {
+ i++;
+ break;
+ }
+ inVerbatimString = false;
+ break;
+ }
+ inString = !inString;
+ break;
+ case '\'':
+ if (inSingleComment || inMultiLineComment || inString || inVerbatimString) {
+ break;
+ }
+ inChar = !inChar;
+ break;
+ default:
+ if (NewLine.IsNewLine(ch)) {
+ inSingleComment = false;
+ inString = false;
+ inChar = false;
+ }
+ break;
+ }
+ }
+ if (parameter.Count != 1 || bracketStack.Count > 0) {
+ return ParameterIndexResult.Invalid;
+ }
+ if (!foundCharAfterOpenBracket)
+ return ParameterIndexResult.First;
+ return new ParameterIndexResult (usedNamedParameters.ToArray(), parameter.Pop() + 1);
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/RemoveUnnecessaryImports/CSharpRemoveUnnecessaryImportsService.Rewriter.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/RemoveUnnecessaryImports/CSharpRemoveUnnecessaryImportsService.Rewriter.cs
new file mode 100644
index 0000000000..04e9f3797b
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/RemoveUnnecessaryImports/CSharpRemoveUnnecessaryImportsService.Rewriter.cs
@@ -0,0 +1,168 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Symbols;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.RemoveUnnecessaryImports
+{
+ public partial class CSharpRemoveUnnecessaryImportsService
+ {
+ private class Rewriter : CSharpSyntaxRewriter
+ {
+ private readonly ISet<UsingDirectiveSyntax> _unnecessaryUsingsDoNotAccessDirectly;
+ private readonly CancellationToken _cancellationToken;
+
+ public Rewriter(ISet<UsingDirectiveSyntax> unnecessaryUsings, CancellationToken cancellationToken)
+ : base(visitIntoStructuredTrivia: true)
+ {
+ _unnecessaryUsingsDoNotAccessDirectly = unnecessaryUsings;
+ _cancellationToken = cancellationToken;
+ }
+
+ public override SyntaxNode DefaultVisit(SyntaxNode node)
+ {
+ _cancellationToken.ThrowIfCancellationRequested();
+ return base.DefaultVisit(node);
+ }
+
+ private void ProcessUsings(
+ SyntaxList<UsingDirectiveSyntax> usings,
+ ISet<UsingDirectiveSyntax> usingsToRemove,
+ out SyntaxList<UsingDirectiveSyntax> finalUsings,
+ out SyntaxTriviaList finalTrivia)
+ {
+ var currentUsings = new List<UsingDirectiveSyntax>(usings);
+
+ finalTrivia = default(SyntaxTriviaList);
+ for (int i = 0; i < usings.Count; i++)
+ {
+ if (usingsToRemove.Contains(usings[i]))
+ {
+ var currentUsing = currentUsings[i];
+ currentUsings[i] = null;
+
+ var leadingTrivia = currentUsing.GetLeadingTrivia();
+ if (leadingTrivia.Any(t => t.Kind() != SyntaxKind.EndOfLineTrivia && t.Kind() != SyntaxKind.WhitespaceTrivia))
+ {
+ // This using had trivia we want to preserve. If we're the last
+ // directive, then copy this trivia out so that our caller can place
+ // it on the next token. If there is any directive following us,
+ // then place it on that.
+ if (i < usings.Count - 1)
+ {
+ currentUsings[i + 1] = currentUsings[i + 1].WithPrependedLeadingTrivia(leadingTrivia);
+ }
+ else
+ {
+ finalTrivia = leadingTrivia;
+ }
+ }
+ }
+ }
+
+ finalUsings = currentUsings.WhereNotNull().ToSyntaxList();
+ }
+
+ private ISet<UsingDirectiveSyntax> GetUsingsToRemove(
+ SyntaxList<UsingDirectiveSyntax> oldUsings,
+ SyntaxList<UsingDirectiveSyntax> newUsings)
+ {
+ var result = new HashSet<UsingDirectiveSyntax>();
+ for (int i = 0; i < oldUsings.Count; i++)
+ {
+ if (_unnecessaryUsingsDoNotAccessDirectly.Contains(oldUsings[i]))
+ {
+ result.Add(newUsings[i]);
+ }
+ }
+
+ return result;
+ }
+
+ public override SyntaxNode VisitCompilationUnit(CompilationUnitSyntax node)
+ {
+ var compilationUnit = (CompilationUnitSyntax)base.VisitCompilationUnit(node);
+
+ var usingsToRemove = GetUsingsToRemove(node.Usings, compilationUnit.Usings);
+ if (usingsToRemove.Count == 0)
+ {
+ return compilationUnit;
+ }
+
+ SyntaxList<UsingDirectiveSyntax> finalUsings;
+ SyntaxTriviaList finalTrivia;
+ ProcessUsings(compilationUnit.Usings, usingsToRemove, out finalUsings, out finalTrivia);
+
+ // If there was any left over trivia, then attach it to the next token that
+ // follows the usings.
+ if (finalTrivia.Count > 0)
+ {
+ var nextToken = compilationUnit.Usings.Last().GetLastToken().GetNextToken();
+ compilationUnit = compilationUnit.ReplaceToken(nextToken, nextToken.WithPrependedLeadingTrivia(finalTrivia));
+ }
+
+ var resultCompilationUnit = compilationUnit.WithUsings(finalUsings);
+ if (finalUsings.Count == 0 &&
+ resultCompilationUnit.Externs.Count == 0 &&
+ resultCompilationUnit.Members.Count >= 1)
+ {
+ // We've removed all the usings and now the first thing in the namespace is a
+ // type. In this case, remove any newlines preceding the type.
+ var firstToken = resultCompilationUnit.GetFirstToken();
+ var newFirstToken = StripNewLines(firstToken);
+ resultCompilationUnit = resultCompilationUnit.ReplaceToken(firstToken, newFirstToken);
+ }
+
+ return resultCompilationUnit;
+ }
+
+ public override SyntaxNode VisitNamespaceDeclaration(NamespaceDeclarationSyntax node)
+ {
+ var namespaceDeclaration = (NamespaceDeclarationSyntax)base.VisitNamespaceDeclaration(node);
+ var usingsToRemove = GetUsingsToRemove(node.Usings, namespaceDeclaration.Usings);
+ if (usingsToRemove.Count == 0)
+ {
+ return namespaceDeclaration;
+ }
+
+ SyntaxList<UsingDirectiveSyntax> finalUsings;
+ SyntaxTriviaList finalTrivia;
+ ProcessUsings(namespaceDeclaration.Usings, usingsToRemove, out finalUsings, out finalTrivia);
+
+ // If there was any left over trivia, then attach it to the next token that
+ // follows the usings.
+ if (finalTrivia.Count > 0)
+ {
+ var nextToken = namespaceDeclaration.Usings.Last().GetLastToken().GetNextToken();
+ namespaceDeclaration = namespaceDeclaration.ReplaceToken(nextToken, nextToken.WithPrependedLeadingTrivia(finalTrivia));
+ }
+
+ var resultNamespace = namespaceDeclaration.WithUsings(finalUsings);
+ if (finalUsings.Count == 0 &&
+ resultNamespace.Externs.Count == 0 &&
+ resultNamespace.Members.Count >= 1)
+ {
+ // We've removed all the usings and now the first thing in the namespace is a
+ // type. In this case, remove any newlines preceding the type.
+ var firstToken = resultNamespace.Members.First().GetFirstToken();
+ var newFirstToken = StripNewLines(firstToken);
+ resultNamespace = resultNamespace.ReplaceToken(firstToken, newFirstToken);
+ }
+
+ return resultNamespace;
+ }
+
+ private static SyntaxToken StripNewLines(SyntaxToken firstToken)
+ {
+ return firstToken.WithLeadingTrivia(firstToken.LeadingTrivia.SkipWhile(t => t.Kind() == SyntaxKind.EndOfLineTrivia));
+ }
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/RemoveUnnecessaryImports/CSharpRemoveUnnecessaryImportsService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/RemoveUnnecessaryImports/CSharpRemoveUnnecessaryImportsService.cs
new file mode 100644
index 0000000000..5d207a7780
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/RemoveUnnecessaryImports/CSharpRemoveUnnecessaryImportsService.cs
@@ -0,0 +1,115 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Composition;
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Formatting;
+using Microsoft.CodeAnalysis.Host;
+using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.CodeAnalysis.Internal.Log;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Text;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+
+namespace ICSharpCode.NRefactory6.CSharp.Features.RemoveUnnecessaryImports
+{
+ public partial class CSharpRemoveUnnecessaryImportsService
+ {
+ public static IEnumerable<SyntaxNode> GetUnnecessaryImports(SemanticModel semanticModel, SyntaxNode root, CancellationToken cancellationToken)
+ {
+ var diagnostics = semanticModel.GetDiagnostics(cancellationToken: cancellationToken);
+ if (!diagnostics.Any())
+ {
+ return null;
+ }
+
+ var unnecessaryImports = new HashSet<UsingDirectiveSyntax>();
+
+ foreach (var diagnostic in diagnostics)
+ {
+ if (diagnostic.Id == "CS8019")
+ {
+ var node = root.FindNode(diagnostic.Location.SourceSpan) as UsingDirectiveSyntax;
+
+ if (node != null)
+ {
+ unnecessaryImports.Add(node);
+ }
+ }
+ }
+
+ if (cancellationToken.IsCancellationRequested || !unnecessaryImports.Any())
+ {
+ return null;
+ }
+
+ return unnecessaryImports;
+ }
+
+ public Document RemoveUnnecessaryImports(Document document, SemanticModel model, SyntaxNode root, CancellationToken cancellationToken)
+ {
+ var unnecessaryImports = GetUnnecessaryImports(model, root, cancellationToken) as ISet<UsingDirectiveSyntax>;
+ if (unnecessaryImports == null)
+ {
+ return document;
+ }
+
+ var oldRoot = (CompilationUnitSyntax)root;
+ if (unnecessaryImports.Any(import => oldRoot.OverlapsHiddenPosition(cancellationToken)))
+ {
+ return document;
+ }
+
+ var newRoot = (CompilationUnitSyntax)new Rewriter(unnecessaryImports, cancellationToken).Visit(oldRoot);
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return null;
+ }
+
+ return document.WithSyntaxRoot(FormatResult(document, newRoot, cancellationToken));
+ }
+
+ private SyntaxNode FormatResult(Document document, CompilationUnitSyntax newRoot, CancellationToken cancellationToken)
+ {
+ var spans = new List<TextSpan>();
+ AddFormattingSpans(newRoot, spans, cancellationToken);
+ return Formatter.Format(newRoot, spans, document.Project.Solution.Workspace, document.Project.Solution.Workspace.Options, cancellationToken: cancellationToken);
+ }
+
+ private void AddFormattingSpans(
+ CompilationUnitSyntax compilationUnit,
+ List<TextSpan> spans,
+ CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ spans.Add(TextSpan.FromBounds(0, GetEndPosition(compilationUnit, compilationUnit.Members)));
+
+ foreach (var @namespace in compilationUnit.Members.OfType<NamespaceDeclarationSyntax>())
+ {
+ AddFormattingSpans(@namespace, spans, cancellationToken);
+ }
+ }
+
+ private void AddFormattingSpans(
+ NamespaceDeclarationSyntax namespaceMember,
+ List<TextSpan> spans,
+ CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ spans.Add(TextSpan.FromBounds(namespaceMember.SpanStart, GetEndPosition(namespaceMember, namespaceMember.Members)));
+
+ foreach (var @namespace in namespaceMember.Members.OfType<NamespaceDeclarationSyntax>())
+ {
+ AddFormattingSpans(@namespace, spans, cancellationToken);
+ }
+ }
+
+ private int GetEndPosition(SyntaxNode container, SyntaxList<MemberDeclarationSyntax> list)
+ {
+ return list.Count > 0 ? list[0].SpanStart : container.Span.End;
+ }
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/SemanticHighlighting/SemanticHighlightingVisitor.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/SemanticHighlighting/SemanticHighlightingVisitor.cs
new file mode 100644
index 0000000000..08a35d5f77
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/SemanticHighlighting/SemanticHighlightingVisitor.cs
@@ -0,0 +1,539 @@
+// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace ICSharpCode.NRefactory6.CSharp.Analysis
+{
+ /// <summary>
+ /// C# Semantic highlighter.
+ /// </summary>
+ public abstract class SemanticHighlightingVisitor<TColor> : CSharpSyntaxWalker
+ {
+ protected CancellationToken cancellationToken = default(CancellationToken);
+
+ protected TColor defaultTextColor;
+ protected TColor referenceTypeColor;
+ protected TColor valueTypeColor;
+ protected TColor interfaceTypeColor;
+ protected TColor enumerationTypeColor;
+ protected TColor typeParameterTypeColor;
+ protected TColor delegateTypeColor;
+
+ protected TColor methodCallColor;
+ protected TColor methodDeclarationColor;
+
+ protected TColor eventDeclarationColor;
+ protected TColor eventAccessColor;
+
+ protected TColor propertyDeclarationColor;
+ protected TColor propertyAccessColor;
+
+ protected TColor fieldDeclarationColor;
+ protected TColor fieldAccessColor;
+
+ protected TColor variableDeclarationColor;
+ protected TColor variableAccessColor;
+
+ protected TColor parameterDeclarationColor;
+ protected TColor parameterAccessColor;
+
+ protected TColor valueKeywordColor;
+ protected TColor externAliasKeywordColor;
+ protected TColor varKeywordTypeColor;
+ protected TColor nameofKeywordColor;
+ protected TColor whenKeywordColor;
+
+ /// <summary>
+ /// Used for 'in' modifiers on type parameters.
+ /// </summary>
+ /// <remarks>
+ /// 'in' may have a different color when used with 'foreach'.
+ /// 'out' is not colored by semantic highlighting, as syntax highlighting can already detect it as a parameter modifier.
+ /// </remarks>
+ protected TColor parameterModifierColor;
+
+ /// <summary>
+ /// Used for inactive code (excluded by preprocessor or ConditionalAttribute)
+ /// </summary>
+ protected TColor inactiveCodeColor;
+
+ protected TColor stringFormatItemColor;
+
+ protected TextSpan region;
+
+ protected SemanticModel semanticModel;
+ // bool isInAccessorContainingValueParameter;
+
+ protected abstract void Colorize (TextSpan span, TColor color);
+
+ protected SemanticHighlightingVisitor (SemanticModel semanticModel)
+ {
+ this.semanticModel = semanticModel;
+ }
+
+ #region Colorize helper methods
+ protected void Colorize (SyntaxNode node, TColor color)
+ {
+ if (node == null)
+ return;
+ Colorize (node.Span, color);
+ }
+
+ protected void Colorize (SyntaxToken node, TColor color)
+ {
+ Colorize (node.Span, color);
+ }
+
+ #endregion
+ public override void VisitRefTypeExpression (Microsoft.CodeAnalysis.CSharp.Syntax.RefTypeExpressionSyntax node)
+ {
+ base.VisitRefTypeExpression (node);
+ Colorize (node.Expression, referenceTypeColor);
+ }
+
+ public override void VisitCompilationUnit (CompilationUnitSyntax node)
+ {
+ var startNode = node.DescendantNodesAndSelf (n => region.Start <= n.SpanStart).FirstOrDefault ();
+ if (startNode == node || startNode == null) {
+ base.VisitCompilationUnit (node);
+ } else {
+ this.Visit (startNode);
+ }
+ }
+
+ public override void Visit (SyntaxNode node)
+ {
+ if (node.Span.End < region.Start)
+ return;
+ if (node.Span.Start > region.End)
+ return;
+ base.Visit(node);
+ }
+
+ void HighlightStringFormatItems(LiteralExpressionSyntax expr)
+ {
+ if (!expr.Token.IsKind(SyntaxKind.StringLiteralToken))
+ return;
+ var text = expr.Token.Text;
+ int start = -1;
+ for (int i = 0; i < text.Length; i++) {
+ char ch = text [i];
+
+ if (NewLine.GetDelimiterType(ch, i + 1 < text.Length ? text [i + 1] : '\0') != UnicodeNewline.Unknown) {
+ continue;
+ }
+
+ if (ch == '{' && start < 0) {
+ char next = i + 1 < text.Length ? text [i + 1] : '\0';
+ if (next == '{') {
+ i++;
+ continue;
+ }
+ start = i;
+ }
+
+ if (ch == '}' && start >= 0) {
+ Colorize(new TextSpan (expr.SpanStart + start, i - start + 1), stringFormatItemColor);
+ start = -1;
+ }
+ }
+ }
+
+ public override void VisitInvocationExpression(InvocationExpressionSyntax node)
+ {
+ var symbolInfo = semanticModel.GetSymbolInfo(node.Expression, cancellationToken);
+
+ if (IsInactiveConditional (symbolInfo.Symbol) || IsEmptyPartialMethod(symbolInfo.Symbol, cancellationToken)) {
+ // mark the whole invocation statement as inactive code
+ Colorize(node.Span, inactiveCodeColor);
+ return;
+ }
+ if (node.Expression.IsKind (SyntaxKind.IdentifierName) && symbolInfo.Symbol == null) {
+ var id = (IdentifierNameSyntax)node.Expression;
+ if (id.Identifier.ValueText == "nameof") {
+ Colorize(id.Span, nameofKeywordColor);
+ }
+ }
+
+ ExpressionSyntax fmtArgumets;
+ IList<ExpressionSyntax> args;
+ if (node.ArgumentList.Arguments.Count > 1 && FormatStringHelper.TryGetFormattingParameters(semanticModel, node, out fmtArgumets, out args, null, cancellationToken)) {
+ var expr = node.ArgumentList.Arguments.First();
+ if (expr != null) {
+ var literalExpressionSyntax = expr.Expression as LiteralExpressionSyntax;
+ if (literalExpressionSyntax != null)
+ HighlightStringFormatItems(literalExpressionSyntax);
+ }
+ }
+
+ base.VisitInvocationExpression(node);
+ }
+
+ bool IsInactiveConditional(ISymbol member)
+ {
+ if (member == null || member.Kind != SymbolKind.Method)
+ return false;
+ var method = member as IMethodSymbol;
+ if (method.ReturnType.SpecialType != SpecialType.System_Void)
+ return false;
+
+ var om = method.OverriddenMethod;
+ while (om != null) {
+ if (IsInactiveConditional (om.GetAttributes()))
+ return true;
+ om = om.OverriddenMethod;
+ }
+
+ return IsInactiveConditional(member.GetAttributes());
+ }
+
+ bool IsInactiveConditional(System.Collections.Immutable.ImmutableArray<AttributeData> attributes)
+ {
+ foreach (var attr in attributes) {
+ if (attr.AttributeClass.Name == "ConditionalAttribute" && attr.AttributeClass.ContainingNamespace.ToString() == "System.Diagnostics" && attr.ConstructorArguments.Length == 1) {
+ string symbol = attr.ConstructorArguments[0].Value as string;
+ if (symbol != null) {
+ var options = (CSharpParseOptions)semanticModel.SyntaxTree.Options;
+ if (!options.PreprocessorSymbolNames.Contains(symbol))
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ static bool IsEmptyPartialMethod(ISymbol member, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ var method = member as IMethodSymbol;
+ if (method == null || method.IsDefinedInMetadata ())
+ return false;
+ foreach (var r in method.DeclaringSyntaxReferences) {
+ var node = r.GetSyntax (cancellationToken) as MethodDeclarationSyntax;
+ if (node == null)
+ continue;
+ if (node.Body != null || !node.Modifiers.Any(m => m.IsKind (SyntaxKind.PartialKeyword)))
+ return false;
+ }
+
+ return true;
+ }
+
+ public override void VisitExternAliasDirective(ExternAliasDirectiveSyntax node)
+ {
+ base.VisitExternAliasDirective(node);
+ Colorize (node.AliasKeyword.Span, externAliasKeywordColor);
+ }
+
+ public override void VisitGenericName(GenericNameSyntax node)
+ {
+ base.VisitGenericName(node);
+ var info = semanticModel.GetSymbolInfo(node, cancellationToken);
+ TColor color;
+ if (TryGetSymbolColor(info, out color)) {
+ Colorize(node.Identifier.Span, color);
+ }
+ }
+
+ public override void VisitStructDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.StructDeclarationSyntax node)
+ {
+ base.VisitStructDeclaration(node);
+ Colorize(node.Identifier, valueTypeColor);
+ }
+
+ public override void VisitInterfaceDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.InterfaceDeclarationSyntax node)
+ {
+ base.VisitInterfaceDeclaration(node);
+ Colorize(node.Identifier, interfaceTypeColor);
+ }
+
+ public override void VisitClassDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.ClassDeclarationSyntax node)
+ {
+ base.VisitClassDeclaration(node);
+ Colorize(node.Identifier, referenceTypeColor);
+ }
+
+ public override void VisitEnumDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.EnumDeclarationSyntax node)
+ {
+ base.VisitEnumDeclaration(node);
+ Colorize(node.Identifier, enumerationTypeColor);
+ }
+
+ public override void VisitDelegateDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.DelegateDeclarationSyntax node)
+ {
+ base.VisitDelegateDeclaration(node);
+ Colorize(node.Identifier, delegateTypeColor);
+ }
+
+ public override void VisitTypeParameter(Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterSyntax node)
+ {
+ base.VisitTypeParameter(node);
+ Colorize(node.Identifier, typeParameterTypeColor);
+ }
+
+ public override void VisitEventDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.EventDeclarationSyntax node)
+ {
+ base.VisitEventDeclaration(node);
+ Colorize(node.Identifier, eventDeclarationColor);
+ }
+
+ public override void VisitMethodDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.MethodDeclarationSyntax node)
+ {
+ base.VisitMethodDeclaration(node);
+ Colorize(node.Identifier, methodDeclarationColor);
+ }
+
+ public override void VisitPropertyDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.PropertyDeclarationSyntax node)
+ {
+ base.VisitPropertyDeclaration(node);
+ Colorize(node.Identifier, propertyDeclarationColor);
+ }
+
+ public override void VisitParameter(Microsoft.CodeAnalysis.CSharp.Syntax.ParameterSyntax node)
+ {
+ base.VisitParameter(node);
+ Colorize(node.Identifier, parameterDeclarationColor);
+ }
+
+ public override void VisitIdentifierName(Microsoft.CodeAnalysis.CSharp.Syntax.IdentifierNameSyntax node)
+ {
+ base.VisitIdentifierName(node);
+ if (node.IsVar) {
+ var symbolInfo = semanticModel.GetSymbolInfo(node, cancellationToken);
+ if (node.Parent is ForEachStatementSyntax) {
+ var sym = semanticModel.GetDeclaredSymbol(node.Parent, cancellationToken);
+ if (sym != null) {
+ Colorize(node.Span, varKeywordTypeColor);
+ return;
+ }
+ }
+ var vds = node.Parent as VariableDeclarationSyntax;
+ if (vds != null && vds.Variables.Count == 1) {
+ // var sym = vds.Variables[0].Initializer != null ? vds.Variables[0].Initializer.Value as LiteralExpressionSyntax : null;
+ if (symbolInfo.Symbol == null || symbolInfo.Symbol.Name != "var") {
+ Colorize(node.Span, varKeywordTypeColor);
+ return;
+ }
+ }
+ }
+
+ switch (node.Identifier.Text) {
+ case "add":
+ case "async":
+ case "await":
+ case "get":
+ case "partial":
+ case "remove":
+ case "set":
+ case "where":
+ case "yield":
+ case "from":
+ case "select":
+ case "group":
+ case "into":
+ case "orderby":
+ case "join":
+ case "let":
+ case "on":
+ case "equals":
+ case "by":
+ case "ascending":
+ case "descending":
+ case "dynamic":
+ // Reset color of contextual keyword to default if it's used as an identifier.
+ // Note that this method does not get called when 'var' or 'dynamic' is used as a type,
+ // because types get highlighted with valueTypeColor/referenceTypeColor instead.
+ Colorize(node.Span, defaultTextColor);
+ break;
+ case "global":
+// // Reset color of 'global' keyword to default unless its used as part of 'global::'.
+// MemberType parentMemberType = identifier.Parent as MemberType;
+// if (parentMemberType == null || !parentMemberType.IsDoubleColon)
+ Colorize(node.Span, defaultTextColor);
+ break;
+ }
+ // "value" is handled in VisitIdentifierExpression()
+ // "alias" is handled in VisitExternAliasDeclaration()
+
+ TColor color;
+ if (TryGetSymbolColor (semanticModel.GetSymbolInfo (node, cancellationToken), out color)) {
+ if (node.Parent is AttributeSyntax || node.Parent is QualifiedNameSyntax && node.Parent.Parent is AttributeSyntax)
+ color = referenceTypeColor;
+ Colorize (node.Span, color);
+ }
+ }
+
+ bool TryGetSymbolColor(SymbolInfo info, out TColor color)
+ {
+ var symbol = info.Symbol;
+
+ if (symbol == null) {
+ color = default(TColor);
+ return false;
+ }
+
+ switch (symbol.Kind) {
+ case SymbolKind.Field:
+ color = fieldAccessColor;
+ return true;
+ case SymbolKind.Event:
+ color = eventAccessColor;
+ return true;
+ case SymbolKind.Parameter:
+ var param = (IParameterSymbol)symbol;
+ var method = param.ContainingSymbol as IMethodSymbol;
+ if (param.Name == "value" && method != null && (
+ method.MethodKind == MethodKind.EventAdd ||
+ method.MethodKind == MethodKind.EventRaise ||
+ method.MethodKind == MethodKind.EventRemove ||
+ method.MethodKind == MethodKind.PropertySet)) {
+ color = valueKeywordColor;
+ } else {
+ color = parameterAccessColor;
+ }
+ return true;
+ case SymbolKind.RangeVariable:
+ color = variableAccessColor;
+ return true;
+ case SymbolKind.Method:
+ color = methodCallColor;
+ return true;
+ case SymbolKind.Property:
+ color = propertyAccessColor;
+ return true;
+ case SymbolKind.TypeParameter:
+ color = typeParameterTypeColor;
+ return true;
+ case SymbolKind.Local:
+ color = variableAccessColor;
+ return true;
+ case SymbolKind.NamedType:
+ var type = (INamedTypeSymbol)symbol;
+ switch (type.TypeKind) {
+ case TypeKind.Class:
+ color = referenceTypeColor;
+ break;
+ case TypeKind.Delegate:
+ color = delegateTypeColor;
+ break;
+ case TypeKind.Enum:
+ color = enumerationTypeColor;
+ break;
+ case TypeKind.Error:
+ color = default(TColor);
+ return false;
+ case TypeKind.Interface:
+ color = interfaceTypeColor;
+ break;
+ case TypeKind.Struct:
+ color = valueTypeColor;
+ break;
+ case TypeKind.TypeParameter:
+ color = typeParameterTypeColor;
+ break;
+ default:
+ color = referenceTypeColor;
+ break;
+ }
+ return true;
+ }
+ color = default(TColor);
+ return false;
+ }
+
+ public override void VisitVariableDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclarationSyntax node)
+ {
+ base.VisitVariableDeclaration(node);
+ TColor color;
+ if (node.Parent.IsKind(SyntaxKind.EventFieldDeclaration))
+ color = eventDeclarationColor;
+ else if (node.Parent.IsKind(SyntaxKind.FieldDeclaration))
+ color = fieldDeclarationColor;
+ else
+ color = variableDeclarationColor;
+
+ foreach (var declarations in node.Variables) {
+ // var info = semanticModel.GetTypeInfo(declarations, cancellationToken);
+ Colorize(declarations.Identifier, color);
+ }
+ }
+
+// public override void VisitComment(Comment comment)
+// {
+// if (comment.CommentType == CommentType.InactiveCode) {
+// Colorize(comment, inactiveCodeColor);
+// }
+// }
+//
+// public override void VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective)
+// {
+// }
+
+// public override void VisitAttribute(Microsoft.CodeAnalysis.CSharp.Syntax.AttributeSyntax node)
+// {
+// var symbol = semanticModel.GetDeclaredSymbol(node) as INamedTypeSymbol;
+// if (symbol != null && IsInactiveConditional(symbol)) {
+// Colorize(attribute, inactiveCodeColor);
+// } else {
+// base.VisitAttribute(node);
+// }
+// }
+
+ public override void VisitInitializerExpression(Microsoft.CodeAnalysis.CSharp.Syntax.InitializerExpressionSyntax node)
+ {
+ base.VisitInitializerExpression(node);
+
+ foreach (var a in node.Expressions) {
+ // TODO
+// var namedElement = a as NamedExpression;
+// if (namedElement != null) {
+// var result = resolver.Resolve (namedElement, cancellationToken);
+// if (result.IsError)
+// Colorize (namedElement.NameToken, syntaxErrorColor);
+// namedElement.Expression.AcceptVisitor (this);
+// }
+ }
+ }
+
+ int blockDepth;
+
+ public override void VisitBlock(Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax node)
+ {
+ blockDepth++;
+ cancellationToken.ThrowIfCancellationRequested ();
+ base.VisitBlock(node);
+ blockDepth--;
+ }
+
+ public override void VisitCatchFilterClause (CatchFilterClauseSyntax node)
+ {
+ if (!node.WhenKeyword.IsMissing) {
+ Colorize(node.WhenKeyword, whenKeywordColor);
+ }
+ base.VisitCatchFilterClause (node);
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValueTreeView.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValueTreeView.cs
index ceefda25db..e752e330da 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValueTreeView.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValueTreeView.cs
@@ -2468,7 +2468,7 @@ namespace MonoDevelop.Debugger
public event EventHandler CompletionListClosed;
}
- class DebugCompletionData : MonoDevelop.Ide.CodeCompletion.CompletionData, ICSharpCode.NRefactory6.CSharp.Completion.ICompletionData
+ class DebugCompletionData : MonoDevelop.Ide.CodeCompletion.CompletionData
{
readonly CompletionItem item;
@@ -2494,42 +2494,5 @@ namespace MonoDevelop.Debugger
return item.Name;
}
}
-
- ICSharpCode.NRefactory6.CSharp.Completion.ICompletionKeyHandler keyHandler;
-
- ICSharpCode.NRefactory6.CSharp.Completion.ICompletionKeyHandler ICSharpCode.NRefactory6.CSharp.Completion.ICompletionData.KeyHandler {
- get {
- return keyHandler;
- }
- }
-
- void ICSharpCode.NRefactory6.CSharp.Completion.ICompletionData.AddOverload (ICSharpCode.NRefactory6.CSharp.Completion.ICompletionData data)
- {
- base.AddOverload ((MonoDevelop.Ide.CodeCompletion.CompletionData)data);
- }
-
- IEnumerable<ICSharpCode.NRefactory6.CSharp.Completion.ICompletionData> ICSharpCode.NRefactory6.CSharp.Completion.ICompletionData.OverloadedData {
- get {
- return OverloadedData.OfType<ICSharpCode.NRefactory6.CSharp.Completion.ICompletionData> ();
- }
- }
-
- ICSharpCode.NRefactory6.CSharp.Completion.ICompletionCategory ICSharpCode.NRefactory6.CSharp.Completion.ICompletionData.CompletionCategory {
- get {
- return (ICSharpCode.NRefactory6.CSharp.Completion.ICompletionCategory)base.CompletionCategory;
- }
- set {
- base.CompletionCategory = (CompletionCategory)value;
- }
- }
-
- ICSharpCode.NRefactory6.CSharp.Completion.DisplayFlags ICSharpCode.NRefactory6.CSharp.Completion.ICompletionData.DisplayFlags {
- get {
- return (ICSharpCode.NRefactory6.CSharp.Completion.DisplayFlags)base.DisplayFlags;
- }
- set {
- base.DisplayFlags = (DisplayFlags)value;
- }
- }
}
}
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionEditorExtension.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionEditorExtension.cs
index 7461d80052..2af9d4e374 100644
--- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionEditorExtension.cs
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionEditorExtension.cs
@@ -38,7 +38,6 @@ using Microsoft.CodeAnalysis.Text;
using MonoDevelop.Ide.TypeSystem;
using MonoDevelop.Ide;
using Microsoft.CodeAnalysis.CodeActions;
-using ICSharpCode.NRefactory6.CSharp.Refactoring;
using MonoDevelop.AnalysisCore;
using MonoDevelop.Ide.Editor;
using MonoDevelop.Components;
@@ -47,6 +46,7 @@ using System.Collections.Immutable;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis;
using System.Reflection;
+using RefactoringEssentials;
namespace MonoDevelop.CodeActions
{
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeRefactoringService.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeRefactoringService.cs
index 5692ba40e1..a58a70929e 100644
--- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeRefactoringService.cs
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeRefactoringService.cs
@@ -36,7 +36,6 @@ using MonoDevelop.Core;
using MonoDevelop.Ide.Editor;
using MonoDevelop.CodeIssues;
using Mono.Addins;
-using ICSharpCode.NRefactory6.CSharp.Refactoring;
using MonoDevelop.Core.Text;
using System.Linq;
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/AnalyzersFromAssembly.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/AnalyzersFromAssembly.cs
index 0d38d291ba..16da9df145 100644
--- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/AnalyzersFromAssembly.cs
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/AnalyzersFromAssembly.cs
@@ -27,21 +27,17 @@
using System;
using System.Linq;
using System.Collections.Generic;
-using System.Threading;
-using MonoDevelop.CodeIssues;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.CodeFixes;
-using ICSharpCode.NRefactory6.CSharp.Refactoring;
using MonoDevelop.Core;
-using System.Threading.Tasks;
-using MonoDevelop.Ide.Editor;
using MonoDevelop.CodeActions;
using Microsoft.CodeAnalysis.CodeRefactorings;
using ICSharpCode.NRefactory6.CSharp;
+using RefactoringEssentials;
namespace MonoDevelop.CodeIssues
{
-
+
class AnalyzersFromAssembly
{
public List<CodeDiagnosticDescriptor> Analyzers;
@@ -70,7 +66,7 @@ namespace MonoDevelop.CodeIssues
var assemblyName = asm.GetName ().Name;
if (assemblyName == "MonoDevelop.AspNet" ||
assemblyName == "Microsoft.CodeAnalysis.CSharp" ||
- assemblyName != "NR6Pack" &&
+ assemblyName != "RefactoringEssentials" &&
!(asm.GetReferencedAssemblies ().Any (a => a.Name == diagnosticAnalyzerAssembly) && asm.GetReferencedAssemblies ().Any (a => a.Name == "MonoDevelop.Ide")))
return;
}
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/DiagnosticResult.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/DiagnosticResult.cs
index f14a822101..830d8483bb 100644
--- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/DiagnosticResult.cs
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/DiagnosticResult.cs
@@ -26,9 +26,9 @@
//#define PROFILE
using System;
using MonoDevelop.AnalysisCore;
-using ICSharpCode.NRefactory6.CSharp;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis;
+using RefactoringEssentials;
namespace MonoDevelop.CodeIssues
{
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.csproj b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.csproj
index 77a3178061..8d1e9bd31b 100644
--- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.csproj
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.csproj
@@ -274,9 +274,9 @@
<Name>MonoDevelop.SourceEditor</Name>
<Private>False</Private>
</ProjectReference>
- <ProjectReference Include="..\..\..\external\NRefactory6\NR6Pack\NR6Pack.csproj">
+ <ProjectReference Include="..\..\..\external\RefactoringEssentials\RefactoringEssentials\RefactoringEssentials.csproj">
<Project>{C465A5DC-AD28-49A2-89C0-F81838814A7E}</Project>
- <Name>NR6Pack</Name>
+ <Name>RefactoringEssentials</Name>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionData.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionData.cs
index 77490702aa..8bd55966cf 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionData.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionData.cs
@@ -168,4 +168,11 @@ namespace MonoDevelop.Ide.CodeCompletion
return ApplyDiplayFlagsFormatting (GLib.Markup.EscapeText (DisplayText));
}
}
+
+ public interface ISymbolCompletionData
+ {
+ Microsoft.CodeAnalysis.ISymbol Symbol {
+ get;
+ }
+ }
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ListWidget.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ListWidget.cs
index c60add505c..0eb0a0612d 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ListWidget.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ListWidget.cs
@@ -30,7 +30,6 @@ using System.Linq;
using Gdk;
using Gtk;
using Pango;
-using ICSharpCode.NRefactory6.CSharp.Completion;
using MonoDevelop.Components;
using MonoDevelop.Ide.Fonts;
using MonoDevelop.Ide.Gui.Content;
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ListWindow.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ListWindow.cs
index dd329a7475..653615f2bf 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ListWindow.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ListWindow.cs
@@ -28,7 +28,6 @@
using System;
using System.Collections.Generic;
using MonoDevelop.Core.Text;
-using ICSharpCode.NRefactory6.CSharp.Completion;
using MonoDevelop.Ide.Gui.Content;
using System.Linq;
using Gdk;
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ParameterHintingData.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ParameterHintingData.cs
index 616c158ad8..638f156bd1 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ParameterHintingData.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/ParameterHintingData.cs
@@ -25,7 +25,6 @@
// THE SOFTWARE.
using System;
using ICSharpCode.NRefactory.Completion;
-using ICSharpCode.NRefactory6.CSharp.Completion;
using Microsoft.CodeAnalysis;
using System.Collections.Immutable;
using System.Linq;
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/ExpansionObject.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/ExpansionObject.cs
index 946cca22c3..cba9e81770 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/ExpansionObject.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/ExpansionObject.cs
@@ -36,7 +36,6 @@ using Microsoft.CodeAnalysis;
using System.Threading.Tasks;
using System.Linq;
using ICSharpCode.NRefactory6.CSharp;
-using ICSharpCode.NRefactory6.CSharp.Completion;
using MonoDevelop.Ide.Editor;
using MonoDevelop.Ide.Editor.Extension;
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj
index 450cef6250..31ea34124d 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj
@@ -202,14 +202,14 @@
<Project>{7E891659-45F3-42B5-B940-A728780CCAE9}</Project>
<Name>ICSharpCode.NRefactory6.CSharp</Name>
</ProjectReference>
- <ProjectReference Include="..\..\..\external\NRefactory6\NR6Pack\NR6Pack.csproj">
- <Project>{C465A5DC-AD28-49A2-89C0-F81838814A7E}</Project>
- <Name>NR6Pack</Name>
- </ProjectReference>
<ProjectReference Include="..\..\..\external\nrefactory\ICSharpCode.NRefactory.Cecil\ICSharpCode.NRefactory.Cecil.csproj">
<Project>{2B8F4F83-C2B3-4E84-A27B-8DEE1BE0E006}</Project>
<Name>ICSharpCode.NRefactory.Cecil</Name>
</ProjectReference>
+ <ProjectReference Include="..\..\..\external\RefactoringEssentials\RefactoringEssentials\RefactoringEssentials.csproj">
+ <Project>{C465A5DC-AD28-49A2-89C0-F81838814A7E}</Project>
+ <Name>RefactoringEssentials</Name>
+ </ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="templates\AppConfigFile.xft.xml">