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

github.com/microsoft/vs-editor-api.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorOleg Tkachenko <olegtk@microsoft.com>2018-01-17 09:46:41 +0300
committerOleg Tkachenko <olegtk@microsoft.com>2018-01-17 09:46:41 +0300
commit82d657798f3873d1fbb9090a062327059544a8b0 (patch)
tree63cbd81b0081c897d11d39614d7dbb2369ccbd40 /src
parentc60cb8991cf77e0de575f4cf15b1bb74a8091762 (diff)
Sync with 15.6.253, add commanding
Diffstat (limited to 'src')
-rw-r--r--src/Core/Def/BaseUtility/ExportImplementationAttribute.cs64
-rw-r--r--src/Core/Def/BaseUtility/INamed.cs17
-rw-r--r--src/Core/Def/BaseUtility/ImportImplementationsAttribute.cs62
-rw-r--r--src/Core/Impl/ContentType/Strings.Designer.cs2
-rw-r--r--src/Microsoft.VisualStudio.Text.Implementation.csproj15
-rw-r--r--src/Microsoft.VisualStudio.Text.Implementation.nuspec2
-rw-r--r--src/Text/Def/TextLogic/PatternMatching/IPatternMatcher.cs43
-rw-r--r--src/Text/Def/TextLogic/PatternMatching/IPatternMatcherFactory.cs45
-rw-r--r--src/Text/Def/TextLogic/PatternMatching/PatternMatch.cs123
-rw-r--r--src/Text/Def/TextLogic/PatternMatching/PatternMatchKind.cs91
-rw-r--r--src/Text/Def/TextLogic/PatternMatching/PatternMatchKindExtensions.cs16
-rw-r--r--src/Text/Def/TextLogic/PatternMatching/PatternMatcherCreationFlags.cs33
-rw-r--r--src/Text/Def/TextLogic/PatternMatching/PatternMatcherCreationOptions.cs49
-rw-r--r--src/Text/Def/TextLogic/TextMate/CommonEditorConstants.cs23
-rw-r--r--src/Text/Def/TextLogic/TextMate/ICommonEditorAssetMetadata.cs19
-rw-r--r--src/Text/Def/TextLogic/TextMate/ICommonEditorAssetService.cs27
-rw-r--r--src/Text/Def/TextLogic/TextMate/ICommonEditorAssetServiceFactory.cs40
-rw-r--r--src/Text/Def/TextUI/Commanding/CommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/CommandExecutionContext.cs27
-rw-r--r--src/Text/Def/TextUI/Commanding/CommandHandlerExtensions.cs123
-rw-r--r--src/Text/Def/TextUI/Commanding/CommandState.cs55
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/AutomaticLineEnderCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/BackTabKeyCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/BackspaceKeyCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/CommentSelectionCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/CommitUniqueCompletionListItemCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/ContractSelectionCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/CopyCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/CopyToInteractiveCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/CutCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/DeleteKeyCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/DocumentEndCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/DocumentStartCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/DownKeyCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/DuplicateSelectionCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/EncapsulateFieldCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/EscapeKeyCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/ExecuteInInteractiveCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/ExpandSelectionCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/ExtractInterfaceCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/ExtractMethodCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/FindReferencesCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/FormatDocumentCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/FormatSelectionCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/GoToDefinitionCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/GoToNextMemberCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/GoToPreviousMemberCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/InsertCommentCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/InsertSnippetCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/InvokeCompletionListCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/InvokeQuickInfoCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/InvokeSignatureHelpCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/LeftKeyCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/LineEndCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/LineEndExtendCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/LineStartCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/LineStartExtendCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/MoveSelectedLinesDownCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/MoveSelectedLinesUpCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/NavigateToNextHighlightedReferenceCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/NavigateToPreviousHighlightedReferenceCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/OpenLineAboveCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/OpenLineBelowCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/PageDownKeyCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/PageUpKeyCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/PasteCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/RedoCommandArgs.cs12
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/RemoveParametersCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/RenameCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/ReorderParametersCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/ReturnKeyCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/RightKeyCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/SaveCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/SelectAllCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/StartAutomaticOutliningCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/SurroundWithCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/SyncClassViewCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/TabKeyCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/ToggleCompletionModeCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/TypeCharCommandArgs.cs11
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/UncommentSelectionCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/UndoCommandArgs.cs12
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/UpKeyCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/ViewCallHierarchyCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/WordDeleteToEndCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/Commands/WordDeleteToStartCommandArgs.cs9
-rw-r--r--src/Text/Def/TextUI/Commanding/EditorCommandArgs.cs33
-rw-r--r--src/Text/Def/TextUI/Commanding/IChainedCommandHandler.cs39
-rw-r--r--src/Text/Def/TextUI/Commanding/ICommandHandler.cs56
-rw-r--r--src/Text/Def/TextUI/Commanding/ICommandingTextBufferResolver.cs20
-rw-r--r--src/Text/Def/TextUI/Commanding/ICommandingTextBufferResolverProvider.cs23
-rw-r--r--src/Text/Def/TextUI/Commanding/IEditorCommandHandlerService.cs32
-rw-r--r--src/Text/Def/TextUI/Commanding/IEditorCommandHandlerServiceFactory.cs27
-rw-r--r--src/Text/Def/TextUI/Utilities/AbstractUIThreadOperationContext.cs219
-rw-r--r--src/Text/Def/TextUI/Utilities/IUIThreadOperationContext.cs58
-rw-r--r--src/Text/Def/TextUI/Utilities/IUIThreadOperationExecutor.cs89
-rw-r--r--src/Text/Def/TextUI/Utilities/IUIThreadOperationScope.cs58
-rw-r--r--src/Text/Def/TextUI/Utilities/UIThreadOperationStatus.cs18
-rw-r--r--src/Text/Impl/Commanding/CommandHandlerBucket.cs42
-rw-r--r--src/Text/Impl/Commanding/CommandingStrings.Designer.cs81
-rw-r--r--src/Text/Impl/Commanding/CommandingStrings.resx107
-rw-r--r--src/Text/Impl/Commanding/DefaultBufferResolver.cs36
-rw-r--r--src/Text/Impl/Commanding/EditorCommandHandlerService.cs373
-rw-r--r--src/Text/Impl/Commanding/EditorCommandHandlerServiceFactory.cs81
-rw-r--r--src/Text/Impl/Commanding/ICommandHandlerMetadata.cs13
-rw-r--r--src/Text/Impl/Commanding/SingleBufferResolver.cs28
-rw-r--r--src/Text/Util/TextDataUtil/StableContentTypeComparer.cs61
-rw-r--r--src/Text/Util/TextDataUtil/StableOrderer.cs34
-rw-r--r--src/Text/Util/TextDataUtil/StableTopologicalSort.cs198
-rw-r--r--src/Text/Util/TextUIUtil/DefaultUIThreadOperationExecutor.cs30
-rw-r--r--src/Text/Util/TextUIUtil/UIThreadOperationExecutor.cs45
111 files changed, 3275 insertions, 4 deletions
diff --git a/src/Core/Def/BaseUtility/ExportImplementationAttribute.cs b/src/Core/Def/BaseUtility/ExportImplementationAttribute.cs
new file mode 100644
index 0000000..0f98f43
--- /dev/null
+++ b/src/Core/Def/BaseUtility/ExportImplementationAttribute.cs
@@ -0,0 +1,64 @@
+using System;
+using System.ComponentModel.Composition;
+
+namespace Microsoft.VisualStudio.Utilities
+{
+ /// <summary>
+ /// Along with <see cref="ImportImplementationsAttribute"/> enables MEF proxy pattern where a single component export serves
+ /// as a proxy for the best implementation selected at run time. This pattern allows component consumers to just [Import] it,
+ /// hiding the complexity of selecting one of implementations.
+ /// </summary>
+ /// <example>
+ /// A typical sample:
+ ///
+ /// A component contract definition:
+ ///
+ /// interface IService {
+ /// void Foo();
+ /// }
+ ///
+ /// Default implementation:
+ ///
+ /// [ExportImplementation(typeof(IService))]
+ /// [Name("default")]
+ /// class DefaultService : IService {...}
+ ///
+ /// Another implementation:
+ ///
+ /// [ExportImplementation(typeof(IService))]
+ /// [Name("A better implementation")]
+ /// [Order(Before = "default")]
+ /// class AdvancedService : IService {...}
+ ///
+ /// A proxy:
+ ///
+ /// [Export(typeof(IService))]
+ /// class ProxyService : IService {
+ /// [ImportImplementations(typeof(IService))]
+ /// IEnumerable&lt;Lazy&lt;IService, IOrderable>> _unorderedImplementations;
+ ///
+ /// public void Foo() {
+ /// Orderer.Order(_unorderedImplementations).FirstOrDefault()?.Value.Foo();
+ /// }
+ /// }
+ ///
+ /// Consuming IService:
+ ///
+ /// [Import]
+ /// IService service = null;
+ /// </example>
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
+ public sealed class ExportImplementationAttribute : ExportAttribute
+ {
+ internal const string ImplementationContractName = "Microsoft.VisualStudio.Utilities.Export.Implementation";
+
+ /// <summary>
+ /// Creates new <see cref="ExportImplementationAttribute"/> instance.
+ /// </summary>
+ /// <param name="contractType">A contract type.</param>
+ public ExportImplementationAttribute(Type contractType)
+ : base(ImplementationContractName, contractType)
+ {
+ }
+ }
+}
diff --git a/src/Core/Def/BaseUtility/INamed.cs b/src/Core/Def/BaseUtility/INamed.cs
new file mode 100644
index 0000000..122b247
--- /dev/null
+++ b/src/Core/Def/BaseUtility/INamed.cs
@@ -0,0 +1,17 @@
+namespace Microsoft.VisualStudio.Utilities
+{
+ /// <summary>
+ /// Represents an object that provides a localized display name
+ /// to be used when it's being represented to the user, for
+ /// example when blaming for delays.
+ /// </summary>
+ public interface INamed
+ {
+ /// <summary>
+ /// Gets display name of an instance used to represent it to the user, for
+ /// example when blaming it for delays.
+ /// </summary>
+ string DisplayName { get; }
+ }
+}
+
diff --git a/src/Core/Def/BaseUtility/ImportImplementationsAttribute.cs b/src/Core/Def/BaseUtility/ImportImplementationsAttribute.cs
new file mode 100644
index 0000000..4849cfc
--- /dev/null
+++ b/src/Core/Def/BaseUtility/ImportImplementationsAttribute.cs
@@ -0,0 +1,62 @@
+using System;
+using System.ComponentModel.Composition;
+
+namespace Microsoft.VisualStudio.Utilities
+{
+ /// <summary>
+ /// Along with <see cref="ExportImplementationAttribute"/> enables MEF proxy pattern where a single component export serves
+ /// as a proxy for the best implementation selected at run time. This pattern allows component consumers to just [Import] it,
+ /// hiding the complexity of selecting one of implementations.
+ /// </summary>
+ /// <example>
+ /// A typical sample:
+ ///
+ /// A component contract definition:
+ ///
+ /// interface IService {
+ /// void Foo();
+ /// }
+ ///
+ /// Default implementation:
+ ///
+ /// [ExportImplementation(typeof(IService))]
+ /// [Name("default")]
+ /// class DefaultService : IService {...}
+ ///
+ /// Another implementation:
+ ///
+ /// [ExportImplementation(typeof(IService))]
+ /// [Name("A better implementation")]
+ /// [Order(Before = "default")]
+ /// class AdvancedService : IService {...}
+ ///
+ /// A proxy:
+ ///
+ /// [Export(typeof(IService))]
+ /// class ProxyService : IService {
+ /// [ImportImplementations(typeof(IService))]
+ /// IEnumerable&lt;Lazy&lt;IService, IOrderable>> _unorderedImplementations;
+ ///
+ /// public void Foo() {
+ /// Orderer.Order(_unorderedImplementations).FirstOrDefault()?.Value.Foo();
+ /// }
+ /// }
+ ///
+ /// Consuming IService:
+ ///
+ /// [Import]
+ /// IService service = null;
+ /// </example>
+
+ public class ImportImplementationsAttribute : ImportManyAttribute
+ {
+ /// <summary>
+ /// Creates new <see cref="ImportImplementationsAttribute"/> instance.
+ /// </summary>
+ /// <param name="contractType">A contract type.</param>
+ public ImportImplementationsAttribute(Type contractType)
+ : base(ExportImplementationAttribute.ImplementationContractName, contractType)
+ {
+ }
+ }
+}
diff --git a/src/Core/Impl/ContentType/Strings.Designer.cs b/src/Core/Impl/ContentType/Strings.Designer.cs
index 9572258..48b56f5 100644
--- a/src/Core/Impl/ContentType/Strings.Designer.cs
+++ b/src/Core/Impl/ContentType/Strings.Designer.cs
@@ -115,7 +115,7 @@ namespace Microsoft.VisualStudio.Utilities.Implementation {
}
/// <summary>
- /// Looks up a localized string similar to The content type {0} leads to a cycle in its base types..
+ /// Looks up a localized string similar to The content type {0} cannot be used as base for content type {1} because it would create a derivation cycle..
/// </summary>
internal static string ContentTypeRegistry_CausesCycles {
get {
diff --git a/src/Microsoft.VisualStudio.Text.Implementation.csproj b/src/Microsoft.VisualStudio.Text.Implementation.csproj
index 728f071..4c4478e 100644
--- a/src/Microsoft.VisualStudio.Text.Implementation.csproj
+++ b/src/Microsoft.VisualStudio.Text.Implementation.csproj
@@ -5,9 +5,9 @@
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
<DelaySign>false</DelaySign>
- <Version>15.0.7-pre</Version>
+ <Version>15.0.8-pre</Version>
<AssemblyVersion>15.0.0.0</AssemblyVersion>
- <NuGetVersionEditor>15.6.241-preview</NuGetVersionEditor>
+ <NuGetVersionEditor>15.6.253-preview</NuGetVersionEditor>
</PropertyGroup>
<PropertyGroup>
@@ -56,6 +56,11 @@
<DesignTime>True</DesignTime>
<DependentUpon>Strings.resx</DependentUpon>
</Compile>
+ <Compile Update="Text\Impl\Commanding\CommandingStrings.Designer.cs">
+ <AutoGen>True</AutoGen>
+ <DesignTime>True</DesignTime>
+ <DependentUpon>CommandingStrings.resx</DependentUpon>
+ </Compile>
</ItemGroup>
<ItemGroup>
@@ -89,6 +94,11 @@
<LastGenOutput>Strings.Designer.cs</LastGenOutput>
<CustomToolNamespace>Microsoft.VisualStudio.Text.Operations.Implementation</CustomToolNamespace>
</EmbeddedResource>
+ <EmbeddedResource Update="Text\Impl\Commanding\CommandingStrings.resx">
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>CommandingStrings.Designer.cs</LastGenOutput>
+ <CustomToolNamespace>Microsoft.VisualStudio.Text.Commanding.Implementation</CustomToolNamespace>
+ </EmbeddedResource>
</ItemGroup>
<ItemGroup>
@@ -102,6 +112,7 @@
<ItemGroup>
<PackageReference Include="System.Collections.Immutable" Version="1.3.1" />
+ <PackageReference Include="System.ValueTuple" Version="4.3.0" />
<PackageReference Include="Microsoft.VisualStudio.CoreUtility" Version="$(NuGetVersionEditor)" />
<PackageReference Include="Microsoft.VisualStudio.Language.StandardClassification" Version="$(NuGetVersionEditor)" />
<PackageReference Include="Microsoft.VisualStudio.Text.Data" Version="$(NuGetVersionEditor)" />
diff --git a/src/Microsoft.VisualStudio.Text.Implementation.nuspec b/src/Microsoft.VisualStudio.Text.Implementation.nuspec
index 365a828..9301476 100644
--- a/src/Microsoft.VisualStudio.Text.Implementation.nuspec
+++ b/src/Microsoft.VisualStudio.Text.Implementation.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>Microsoft.VisualStudio.Text.Implementation</id>
- <version>15.0.7-pre</version>
+ <version>15.0.8-pre</version>
<authors>Microsoft</authors>
<owners>Microsoft</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
diff --git a/src/Text/Def/TextLogic/PatternMatching/IPatternMatcher.cs b/src/Text/Def/TextLogic/PatternMatching/IPatternMatcher.cs
new file mode 100644
index 0000000..27e6e2a
--- /dev/null
+++ b/src/Text/Def/TextLogic/PatternMatching/IPatternMatcher.cs
@@ -0,0 +1,43 @@
+using Microsoft.VisualStudio.Utilities;
+
+namespace Microsoft.VisualStudio.Text.PatternMatching
+{
+ /// <summary>
+ /// Defines a pattern matcher that can compare a candidate string against a search pattern to identify relevance. <see cref="IPatternMatcherFactory"/> defines
+ /// the way to obtain an <see cref="IPatternMatcher"/> given a search pattern and options.
+ /// </summary>
+ public interface IPatternMatcher
+ {
+ /// <summary>
+ /// Determines if, and how well a candidate string matches a search pattern and a set of <see cref="PatternMatcherCreationOptions"/>.
+ /// </summary>
+ /// <param name="candidate">The string to evaluate for relevancy.</param>
+ /// <returns>A <see cref="PatternMatch"/> object describing how well the candidate matched the pattern. If no match is found, this returns <see langword="null"/> instead.</returns>
+ /// <remarks>
+ /// This pattern matcher uses the concepts of a 'Pattern' and a 'Candidate' to to differentiate between what the user types to search
+ /// and what the system compares against. The pattern and some <see cref="PatternMatcherCreationOptions"/> are specified in <see cref="IPatternMatcherFactory"/> in order to obtain an <see cref="IPatternMatcher"/>.
+ ///
+ /// The user can then call this method repeatedly with multiple candidates to filter out non-matches, and obtain sortable <see cref="PatternMatch"/> objects to help decide
+ /// what the user actually wanted.
+ ///
+ /// A few examples are useful here. Suppose the user obtains an IPatternMatcher using the following:
+ /// Pattern = "PatMat"
+ ///
+ /// The following calls to TryMatch could expect these results:
+ /// Candidate = "PatternMatcher"
+ /// Returns a match containing <see cref="PatternMatchKind.CamelCaseExact"/>.
+ ///
+ /// Candidate = "IPatternMatcher"
+ /// Returns a match containing <see cref="PatternMatchKind.CamelCaseSubstring"/>
+ ///
+ /// Candidate = "patmat"
+ /// Returns a match containing <see cref="PatternMatchKind.Exact"/>, but <see cref="PatternMatch.IsCaseSensitive"/> will be false.
+ ///
+ /// Candidate = "Not A Match"
+ /// Returns <see langword="null"/>.
+ ///
+ /// To determine sort order, call <see cref="PatternMatch.CompareTo(PatternMatch)"/>.
+ /// </remarks>
+ PatternMatch? TryMatch(string candidate);
+ }
+}
diff --git a/src/Text/Def/TextLogic/PatternMatching/IPatternMatcherFactory.cs b/src/Text/Def/TextLogic/PatternMatching/IPatternMatcherFactory.cs
new file mode 100644
index 0000000..86857e9
--- /dev/null
+++ b/src/Text/Def/TextLogic/PatternMatching/IPatternMatcherFactory.cs
@@ -0,0 +1,45 @@
+namespace Microsoft.VisualStudio.Text.PatternMatching
+{
+ /// <summary>
+ /// Provides instances of a <see cref="IPatternMatcher"/> for a given
+ /// search string and creation options.
+ /// </summary>
+ /// <remarks>This is a MEF component part, and should be imported as follows:
+ /// [Import]
+ /// IPatternMatcherFactory factory = null;
+ /// </remarks>
+ public interface IPatternMatcherFactory
+ {
+ /// <summary>
+ /// Gets an <see cref="IPatternMatcher"/> given a search pattern and search options.
+ /// </summary>
+ /// <param name="pattern">Describes the search pattern that candidate strings will be compared against for relevancy.</param>
+ /// <param name="creationOptions">Defines parameters for what should be considered relevant in a match.</param>
+ /// <remarks>
+ /// This pattern matcher uses the concepts of a 'Pattern' and a 'Candidate' to to differentiate between what the user types to search
+ /// and what the system compares against. The pattern and some <see cref="PatternMatcherCreationOptions"/> are specified in here in order to obtain an <see cref="IPatternMatcher"/>.
+ ///
+ /// The user can then call <see cref="IPatternMatcher.TryMatch(string)"/> repeatedly with multiple candidates to filter out non-matches, and obtain sortable <see cref="PatternMatch"/> objects to help decide
+ /// what the user actually wanted.
+ ///
+ /// A few examples are useful here. Suppose the user obtains an IPatternMatcher using the following:
+ /// Pattern = "PatMat"
+ ///
+ /// The following calls to TryMatch could expect these results:
+ /// Candidate = "PatternMatcher"
+ /// Returns a match containing <see cref="PatternMatchKind.CamelCaseExact"/>.
+ ///
+ /// Candidate = "IPatternMatcher"
+ /// Returns a match containing <see cref="PatternMatchKind.CamelCaseSubstring"/>
+ ///
+ /// Candidate = "patmat"
+ /// Returns a match containing <see cref="PatternMatchKind.Exact"/>, but <see cref="PatternMatch.IsCaseSensitive"/> will be false.
+ ///
+ /// Candidate = "Not A Match"
+ /// Returns <see langword="null"/>.
+ ///
+ /// To determine sort order, call <see cref="PatternMatch.CompareTo(PatternMatch)"/>.
+ /// </remarks>
+ IPatternMatcher CreatePatternMatcher(string pattern, PatternMatcherCreationOptions creationOptions);
+ }
+}
diff --git a/src/Text/Def/TextLogic/PatternMatching/PatternMatch.cs b/src/Text/Def/TextLogic/PatternMatching/PatternMatch.cs
new file mode 100644
index 0000000..dd907d0
--- /dev/null
+++ b/src/Text/Def/TextLogic/PatternMatching/PatternMatch.cs
@@ -0,0 +1,123 @@
+using System;
+using System.Collections.Immutable;
+using System.Linq;
+
+namespace Microsoft.VisualStudio.Text.PatternMatching
+{
+ public struct PatternMatch : IComparable<PatternMatch>
+ {
+ /// <summary>
+ /// True if this was a case sensitive match.
+ /// </summary>
+ public bool IsCaseSensitive { get; }
+
+ /// <summary>
+ /// The type of match that occurred.
+ /// </summary>
+ public PatternMatchKind Kind { get; }
+
+ /// <summary>
+ /// The spans in the original text that were matched. Only returned if the
+ /// pattern matcher is asked to collect these spans.
+ /// </summary>
+ public ImmutableArray<Span> MatchedSpans { get; }
+
+ /// <summary>
+ /// True if punctuation was removed for this match.
+ /// </summary>
+ public bool IsPunctuationStripped { get; }
+
+ /// <summary>
+ /// Creates a PatternMatch object with an optional single span.
+ /// </summary>
+ /// <param name="resultType">How is this match categorized?</param>
+ /// <param name="punctuationStripped">Was punctuation removed?</param>
+ /// <param name="isCaseSensitive">Was this a case sensitive match?</param>
+ public PatternMatch(
+ PatternMatchKind resultType,
+ bool punctuationStripped,
+ bool isCaseSensitive)
+ : this(resultType, punctuationStripped, isCaseSensitive, ImmutableArray<Span>.Empty)
+ {
+ }
+
+ /// <summary>
+ /// Creates a PatternMatch object with a set of spans
+ /// </summary>
+ /// <param name="resultType">How is this match categorized?</param>
+ /// <param name="punctuationStripped">Was punctuation removed?</param>
+ /// <param name="isCaseSensitive">Was this a case sensitive match?</param>
+ /// <param name="matchedSpans">What spans of the candidate were matched? An empty array signifies no span information is given.</param>
+ public PatternMatch(
+ PatternMatchKind resultType,
+ bool punctuationStripped,
+ bool isCaseSensitive,
+ ImmutableArray<Span> matchedSpans)
+ : this()
+ {
+ this.Kind = resultType;
+ this.IsCaseSensitive = isCaseSensitive;
+ this.MatchedSpans = matchedSpans;
+ this.IsPunctuationStripped = punctuationStripped;
+ }
+
+ /// <summary>
+ /// Get a PatternMatch object with additional spans added to it. This is an optimization to avoid having to call the whole constructor.
+ /// </summary>
+ /// <param name="matchedSpans">Spans to associate with this PatternMatch.</param>
+ /// <returns>A new instance of a PatternMatch with the specified spans.</returns>
+ public PatternMatch WithMatchedSpans(ImmutableArray<Span> matchedSpans)
+ => new PatternMatch(Kind, IsPunctuationStripped, IsCaseSensitive, matchedSpans);
+
+ /// <summary>
+ /// Compares two PatternMatch objects.
+ /// </summary>
+ public int CompareTo(PatternMatch other)
+ => CompareTo(other, ignoreCase: false);
+
+ /// <summary>
+ /// Compares two PatternMatch objects with the specified behavior for ignoring capitalization.
+ /// </summary>
+ /// <param name="ignoreCase">Should case be ignored?</param>
+ public int CompareTo(PatternMatch other, bool ignoreCase)
+ {
+ int diff;
+ if ((diff = CompareType(this, other)) != 0 ||
+ (diff = CompareCase(this, other, ignoreCase)) != 0 ||
+ (diff = ComparePunctuation(this, other)) != 0)
+ {
+ return diff;
+ }
+
+ return 0;
+ }
+
+ private static int ComparePunctuation(PatternMatch result1, PatternMatch result2)
+ {
+ // Consider a match to be better if it was successful without stripping punctuation
+ // versus a match that had to strip punctuation to succeed.
+ if (result1.IsPunctuationStripped != result2.IsPunctuationStripped)
+ {
+ return result1.IsPunctuationStripped ? 1 : -1;
+ }
+
+ return 0;
+ }
+
+ private static int CompareCase(PatternMatch result1, PatternMatch result2, bool ignoreCase)
+ {
+ if (!ignoreCase)
+ {
+ if (result1.IsCaseSensitive != result2.IsCaseSensitive)
+ {
+ return result1.IsCaseSensitive ? -1 : 1;
+ }
+ }
+
+ return 0;
+ }
+
+ private static int CompareType(PatternMatch result1, PatternMatch result2)
+ => result1.Kind.CompareTo(result2.Kind);
+ }
+}
diff --git a/src/Text/Def/TextLogic/PatternMatching/PatternMatchKind.cs b/src/Text/Def/TextLogic/PatternMatching/PatternMatchKind.cs
new file mode 100644
index 0000000..88be244
--- /dev/null
+++ b/src/Text/Def/TextLogic/PatternMatching/PatternMatchKind.cs
@@ -0,0 +1,91 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Microsoft.VisualStudio.Text.PatternMatching
+{
+ /// <summary>
+ /// This enum is NOT ordered. Sorting based on the PatternMatchKind is performed in <see cref="PatternMatch"/> and is subject to change.
+ /// Additional entries can be added at the bottom of this enumeration.
+ /// </summary>
+ public enum PatternMatchKind
+ {
+ /// <summary>
+ /// The candidate string matched the pattern exactly.
+ /// </summary>
+ Exact,
+
+ /// <summary>
+ /// The pattern was a prefix of the candidate string.
+ /// </summary>
+ Prefix,
+
+ /// <summary>
+ /// The pattern was a substring of the candidate string, but in a way that wasn't a CamelCase match.
+ /// </summary>
+ Substring,
+
+ // Note: CamelCased matches are ordered from best to worst.
+
+ /// <summary>
+ /// All camel-humps in the pattern matched a camel-hump in the candidate. All camel-humps
+ /// in the candidate were matched by a camel-hump in the pattern.
+ ///
+ /// Example: "CFPS" matching "CodeFixProviderService"
+ /// Example: "cfps" matching "CodeFixProviderService"
+ /// Example: "CoFiPrSe" matching "CodeFixProviderService"
+ /// </summary>
+ CamelCaseExact,
+
+ /// <summary>
+ /// All camel-humps in the pattern matched a camel-hump in the candidate. The first camel-hump
+ /// in the pattern matched the first camel-hump in the candidate. There was no gap in the camel-
+ /// humps in the candidate that were matched.
+ ///
+ /// Example: "CFP" matching "CodeFixProviderService"
+ /// Example: "cfp" matching "CodeFixProviderService"
+ /// Example: "CoFiPRo" matching "CodeFixProviderService"
+ /// </summary>
+ CamelCasePrefix,
+
+ /// <summary>
+ /// All camel-humps in the pattern matched a camel-hump in the candidate. The first camel-hump
+ /// in the pattern matched the first camel-hump in the candidate. There was at least one gap in
+ /// the camel-humps in the candidate that were matched.
+ ///
+ /// Example: "CP" matching "CodeFixProviderService"
+ /// Example: "cp" matching "CodeFixProviderService"
+ /// Example: "CoProv" matching "CodeFixProviderService"
+ /// </summary>
+ CamelCaseNonContiguousPrefix,
+
+ /// <summary>
+ /// All camel-humps in the pattern matched a camel-hump in the candidate. The first camel-hump
+ /// in the pattern did not match the first camel-hump in the pattern. There was no gap in the camel-
+ /// humps in the candidate that were matched.
+ ///
+ /// Example: "FP" matching "CodeFixProviderService"
+ /// Example: "fp" matching "CodeFixProviderService"
+ /// Example: "FixPro" matching "CodeFixProviderService"
+ /// </summary>
+ CamelCaseSubstring,
+
+ /// <summary>
+ /// All camel-humps in the pattern matched a camel-hump in the candidate. The first camel-hump
+ /// in the pattern did not match the first camel-hump in the pattern. There was at least one gap in
+ /// the camel-humps in the candidate that were matched.
+ ///
+ /// Example: "FS" matching "CodeFixProviderService"
+ /// Example: "fs" matching "CodeFixProviderService"
+ /// Example: "FixSer" matching "CodeFixProviderService"
+ /// </summary>
+ CamelCaseNonContiguousSubstring,
+
+ /// <summary>
+ /// The pattern matches the candidate in a fuzzy manner. Fuzzy matching allows for
+ /// a certain amount of misspellings, missing words, etc.
+ /// </summary>
+ Fuzzy
+ }
+}
diff --git a/src/Text/Def/TextLogic/PatternMatching/PatternMatchKindExtensions.cs b/src/Text/Def/TextLogic/PatternMatching/PatternMatchKindExtensions.cs
new file mode 100644
index 0000000..fda778f
--- /dev/null
+++ b/src/Text/Def/TextLogic/PatternMatching/PatternMatchKindExtensions.cs
@@ -0,0 +1,16 @@
+namespace Microsoft.VisualStudio.Text.PatternMatching
+{
+ public static class PatternMatchKindExtensions
+ {
+ /// <summary>
+ /// Compares two <see cref="PatternMatchKind"/> values, suggesting which one is more likely to be what the user was searching for.
+ /// </summary>
+ /// <param name="kind1">Item to be compared.</param>
+ /// <param name="kind2">Item to be compared.</param>
+ /// <returns>A negative value means kind1 is preferable, positive means kind2 is preferable. Zero means they are equivalent.</returns>
+ public static int CompareTo(this PatternMatchKind kind1, PatternMatchKind kind2)
+ {
+ return kind1 - kind2;
+ }
+ }
+}
diff --git a/src/Text/Def/TextLogic/PatternMatching/PatternMatcherCreationFlags.cs b/src/Text/Def/TextLogic/PatternMatching/PatternMatcherCreationFlags.cs
new file mode 100644
index 0000000..ad21476
--- /dev/null
+++ b/src/Text/Def/TextLogic/PatternMatching/PatternMatcherCreationFlags.cs
@@ -0,0 +1,33 @@
+using System;
+
+namespace Microsoft.VisualStudio.Text.PatternMatching
+{
+ /// <summary>
+ /// Specifies flags that control optional behavior of the pattern matching.
+ /// </summary>
+ [Flags]
+ public enum PatternMatcherCreationFlags
+ {
+ /// <summary>
+ /// No options selected.
+ /// </summary>
+ None = 0,
+
+ /// <summary>
+ /// Signifies that strings differing from the initial pattern by minor spelling changes should be considered a match.
+ /// </summary>
+ AllowFuzzyMatching = 1,
+
+ /// <summary>
+ /// Signifies that spans indicating matched segments in candidate strings should be returned.
+ /// </summary>
+ IncludeMatchedSpans = 2,
+
+ /// <summary>
+ /// Signifies that a case insensitive substring match, but not a prefix should be considered a match.
+ /// This covers the case of non camel case naming conventions, for example matching
+ /// 'afxsettingsstore.h' when user types 'store.h'
+ /// </summary>
+ AllowSimpleSubstringMatching = 4
+ }
+}
diff --git a/src/Text/Def/TextLogic/PatternMatching/PatternMatcherCreationOptions.cs b/src/Text/Def/TextLogic/PatternMatching/PatternMatcherCreationOptions.cs
new file mode 100644
index 0000000..5693a3f
--- /dev/null
+++ b/src/Text/Def/TextLogic/PatternMatching/PatternMatcherCreationOptions.cs
@@ -0,0 +1,49 @@
+using System.Collections.Generic;
+using System.Globalization;
+
+namespace Microsoft.VisualStudio.Text.PatternMatching
+{
+ /// <summary>
+ /// Defines context for what should be considered relevant in a pattern match.
+ /// </summary>
+ public sealed class PatternMatcherCreationOptions
+ {
+ /// <summary>
+ /// Used to tailor character comparisons to the correct culture.
+ /// </summary>
+ public readonly CultureInfo CultureInfo;
+
+ /// <summary>
+ /// A set of biniary options, used to control options like case-sensitivity.
+ /// </summary>
+ public readonly PatternMatcherCreationFlags Flags;
+
+ /// <summary>
+ /// Characters that should be considered as describing a container/contained boundary. When matching types, this can be the '.' character
+ /// e.g. Namespace.Class.Property, so that the search can tailor behavior to better match Property first, then Class, then Namespace.
+ /// This also can work with directory separators in filenames and any other logical container/contained pattern in candidate strings.
+ ///
+ /// <see langword="null"/> signifies no characters are container boundaries.
+ /// </summary>
+ public readonly IReadOnlyCollection<char> ContainerSplitCharacters;
+
+ /// <summary>
+ /// Creates an instance of <see cref="PatternMatcherCreationOptions"/>.
+ /// </summary>
+ /// <param name="cultureInfo">Used to tailor character comparisons to the correct culture.</param>
+ /// <param name="flags">A set of biniary options, used to control options like case-sensitivity.</param>
+ /// <param name="containerSplitCharacters">
+ /// Characters that should be considered as describing a container/contained boundary. When matching types, this can be the '.' character
+ /// e.g. Namespace.Class.Property, so that the search can tailor behavior to better match Property first, then Class, then Namespace.
+ /// This also can work with directory separators in filenames and any other logical container/contained pattern in candidate strings.
+ ///
+ /// <see langword="null"/> signifies no characters are container boundaries.
+ /// </param>
+ public PatternMatcherCreationOptions(CultureInfo cultureInfo, PatternMatcherCreationFlags flags, IReadOnlyCollection<char> containerSplitCharacters = null)
+ {
+ CultureInfo = cultureInfo;
+ Flags = flags;
+ ContainerSplitCharacters = containerSplitCharacters;
+ }
+ }
+}
diff --git a/src/Text/Def/TextLogic/TextMate/CommonEditorConstants.cs b/src/Text/Def/TextLogic/TextMate/CommonEditorConstants.cs
new file mode 100644
index 0000000..d2eaf68
--- /dev/null
+++ b/src/Text/Def/TextLogic/TextMate/CommonEditorConstants.cs
@@ -0,0 +1,23 @@
+namespace Microsoft.VisualStudio.Editor
+{
+ /// <summary>
+ /// Constants for interacting with <see cref="ICommonEditorAssetService"/> and Common Editor languages.
+ /// </summary>
+ public static class CommonEditorConstants
+ {
+ /// <summary>
+ /// Name used to identify all Common Editor assets.
+ /// </summary>
+ public const string AssetName = "TextMate";
+
+ /// <summary>
+ /// Name of the content type under from which all TextMate based languages are derived.
+ /// </summary>
+ public const string ContentTypeName = "code++";
+
+ /// <summary>
+ /// Name of the registry key under which new repositories for TextMate grammars can be defined.
+ /// </summary>
+ public const string TextMateRepositoryKey = @"TextMate\Repositories";
+ }
+}
diff --git a/src/Text/Def/TextLogic/TextMate/ICommonEditorAssetMetadata.cs b/src/Text/Def/TextLogic/TextMate/ICommonEditorAssetMetadata.cs
new file mode 100644
index 0000000..e2da623
--- /dev/null
+++ b/src/Text/Def/TextLogic/TextMate/ICommonEditorAssetMetadata.cs
@@ -0,0 +1,19 @@
+namespace Microsoft.VisualStudio.Editor
+{
+ using System;
+ using System.Collections.Generic;
+ using System.ComponentModel;
+ using Microsoft.VisualStudio.Utilities;
+
+ /// <summary>
+ /// Common Editor asset metadata.
+ /// </summary>
+ public interface ICommonEditorAssetMetadata : IOrderable
+ {
+ /// <summary>
+ /// The type of tags supported by the Common Editor asset.
+ /// </summary>
+ [DefaultValue(null)]
+ IEnumerable<Type> TagTypes { get; }
+ }
+}
diff --git a/src/Text/Def/TextLogic/TextMate/ICommonEditorAssetService.cs b/src/Text/Def/TextLogic/TextMate/ICommonEditorAssetService.cs
new file mode 100644
index 0000000..11d0d96
--- /dev/null
+++ b/src/Text/Def/TextLogic/TextMate/ICommonEditorAssetService.cs
@@ -0,0 +1,27 @@
+namespace Microsoft.VisualStudio.Editor
+{
+ using System;
+
+ /// <summary>
+ /// Service produced by <see cref="ICommonEditorAssetServiceFactory"/> that provides common language service assets.
+ /// </summary>
+ /// <remarks>This class supports the Visual Studio
+ /// infrastructure and in general is not intended to be used directly from your code.</remarks>
+ public interface ICommonEditorAssetService
+ {
+ /// <summary>
+ /// Produces common language service asset.
+ /// </summary>
+ /// <typeparam name="T">
+ /// The type of language service asset to produce. Can be ITaggerProvider, IViewTaggerProvider,
+ /// or ICompletionSource. Use <paramref name="isMatch"/> to find a tagger of the desired type.
+ /// </typeparam>
+ /// <param name="isMatch">Returns true if the <see cref="ICommonEditorAssetMetadata"/> matches the desired feature.</param>
+ /// <remarks>
+ /// This method supports the Visual Studio infrastructure and in
+ /// general is not intended to be used directly from your code.
+ /// </remarks>
+ /// <returns>A feature of <typeparamref name="T"/> or null if unknown.</returns>
+ T FindAsset<T>(Predicate<ICommonEditorAssetMetadata> isMatch = null) where T : class;
+ }
+}
diff --git a/src/Text/Def/TextLogic/TextMate/ICommonEditorAssetServiceFactory.cs b/src/Text/Def/TextLogic/TextMate/ICommonEditorAssetServiceFactory.cs
new file mode 100644
index 0000000..36b618b
--- /dev/null
+++ b/src/Text/Def/TextLogic/TextMate/ICommonEditorAssetServiceFactory.cs
@@ -0,0 +1,40 @@
+namespace Microsoft.VisualStudio.Editor
+{
+ using Microsoft.VisualStudio.Text;
+
+ /// <summary>
+ /// Service for getting a service that provides common language service elements.
+ /// </summary>
+ /// <remarks>This class supports the Visual Studio
+ /// infrastructure and in general is not intended to be used directly from your code.</remarks>
+ /// <example>
+ /// This is a MEF component part. Use the code below in your MEF exported class to import an
+ /// instance of the service factory.
+ /// <code>
+ /// [Import]
+ /// private ICommonEditorAssetServiceFactory assetServiceFactory = null;
+ /// </code>
+ /// Then, you can use the code below to get the ITaggerProvider for the Common Editor's
+ /// IClassificationTagger. Modify as needed to get the desired asset.
+ /// <code>
+ /// var factory = this.assetServiceFactory.GetOrCreate(buffer);
+ /// var tagger = factory.FindAsset<ITaggerProvider>(
+ /// (metadata) => metadata.TagTypes.Any(tagType => typeof(IClassificationTagger).IsAssignableFrom(tagType)))
+ /// ?.CreateTagger<T>(buffer);
+ /// </code>
+ /// </example>
+ public interface ICommonEditorAssetServiceFactory
+ {
+ /// <summary>
+ /// Gets a service that provides common language service elements.
+ /// </summary>
+ /// </summary>
+ /// <param name="textBuffer">The <see cref="ITextBuffer"/> for which to initialize TextMate.</param>
+ /// <remarks>
+ /// This method supports the Visual Studio infrastructure and in
+ /// general is not intended to be used directly from your code.
+ /// </remarks>
+ /// <returns>An instance of <see cref="ITextMateAssetService"/>.</returns>
+ ICommonEditorAssetService GetOrCreate(ITextBuffer textBuffer);
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/CommandArgs.cs b/src/Text/Def/TextUI/Commanding/CommandArgs.cs
new file mode 100644
index 0000000..ea4b4e2
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/CommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Commanding
+{
+ /// <summary>
+ /// A base class for all command arguments.
+ /// </summary>
+ public abstract class CommandArgs
+ {
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/CommandExecutionContext.cs b/src/Text/Def/TextUI/Commanding/CommandExecutionContext.cs
new file mode 100644
index 0000000..f39cd55
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/CommandExecutionContext.cs
@@ -0,0 +1,27 @@
+using System;
+using Microsoft.VisualStudio.Utilities;
+
+namespace Microsoft.VisualStudio.Commanding
+{
+ /// <summary>
+ /// Represents a command execution context, which is set up by a command handler service
+ /// and provided to each command handler.
+ /// </summary>
+ public sealed class CommandExecutionContext
+ {
+ /// <summary>
+ /// Creates new instance of the <see cref="CommandExecutionContext"/>.
+ /// </summary>
+ public CommandExecutionContext(IUIThreadOperationContext waitContext)
+ {
+ this.WaitContext = waitContext ?? throw new ArgumentNullException(nameof(waitContext));
+ }
+
+ /// <summary>
+ /// Provides a context of executing a command handler on the UI thread, which
+ /// enables two way shared cancellability and wait indication.
+ /// </summary>
+ public IUIThreadOperationContext WaitContext { get; }
+ }
+}
+
diff --git a/src/Text/Def/TextUI/Commanding/CommandHandlerExtensions.cs b/src/Text/Def/TextUI/Commanding/CommandHandlerExtensions.cs
new file mode 100644
index 0000000..a4afd58
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/CommandHandlerExtensions.cs
@@ -0,0 +1,123 @@
+using System;
+
+namespace Microsoft.VisualStudio.Commanding
+{
+ /// <summary>
+ /// Contains command handler utility extension methods.
+ /// </summary>
+ public static class CommandHandlerExtensions
+ {
+ /// <summary>
+ /// Called to determine the state of the command.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// A command handler can implement <see cref="ICommandHandler{T}"/> or
+ /// <see cref="IChainedCommandHandler{T}"/>, but either way this method returns
+ /// the final state of the command as returned by either this or next
+ /// command handler.
+ /// </para>
+ /// <para>If <paramref name="commandHandler"/> implements <see cref="ICommandHandler{T}"/>,
+ /// its <see cref="ICommandHandler{T}.GetCommandState(T)"/> method is called. If it returns
+ /// <see cref="CommandState.Unspecified"/>, <paramref name="nextCommandHandler"/> is invoked.
+ ///</para>
+ ///<para>
+ /// If <paramref name="commandHandler"/> implements <see cref="IChainedCommandHandler{T}"/>,
+ /// its <see cref="IChainedCommandHandler{T}.GetCommandState(T, Func{CommandState})"/> method is invoked with
+ /// <paramref name="nextCommandHandler"/> passed as an argument.
+ ///</para>
+ /// </remarks>
+ /// <param name="args">The <see cref="CommandArgs"/> arguments for the command.</param>
+ /// <param name="nextCommandHandler">The next command handler in the command execution chain.</param>
+ /// <param name="commandHandler">A command handler to query the state of the command.</param>
+ /// <returns>A <see cref="CommandState"/> instance that contains information on the availability of the command.</returns>
+ public static CommandState GetCommandState<T>(this ICommandHandler commandHandler, T args, Func<CommandState> nextCommandHandler) where T : CommandArgs
+ {
+ if (commandHandler == null)
+ {
+ throw new ArgumentNullException(nameof(commandHandler));
+ }
+
+ if (nextCommandHandler == null)
+ {
+ throw new ArgumentNullException(nameof(nextCommandHandler));
+ }
+
+ if (commandHandler is ICommandHandler<T> simpleCommandHandler)
+ {
+ var commandState = simpleCommandHandler.GetCommandState(args);
+ if (commandState.IsUnspecified)
+ {
+ return nextCommandHandler();
+ }
+
+ return commandState;
+ }
+
+ if (commandHandler is IChainedCommandHandler<T> chainedCommandHandler)
+ {
+ return chainedCommandHandler.GetCommandState(args, nextCommandHandler);
+ }
+
+ throw new ArgumentException($"Unsupported CommandHandler type: {commandHandler.GetType()}");
+ }
+
+ /// <summary>
+ /// Called to execute the command.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// A command handler can implement <see cref="ICommandHandler{T}"/> or
+ /// <see cref="IChainedCommandHandler{T}"/>, but either way this method executes
+ /// the command by either this or next command handler.
+ /// </para>
+ /// <para>If <paramref name="commandHandler"/> implements <see cref="ICommandHandler{T}"/>,
+ /// its <see cref="ICommandHandler{T}.ExecuteCommand(T, CommandExecutionContext)"/> method is called. If it returns
+ /// <c>false</c>, <paramref name="nextCommandHandler"/> is invoked.
+ ///</para>
+ ///<para>
+ /// If <paramref name="commandHandler"/> implements <see cref="IChainedCommandHandler{T}"/>,
+ /// its <see cref="IChainedCommandHandler{T}.ExecuteCommand(T, Action, CommandExecutionContext)"/> method is invoked with
+ /// <paramref name="nextCommandHandler"/> passed as an argument.
+ ///</para>
+ /// </remarks>
+ /// <param name="args">The <see cref="CommandArgs"/> arguments for the command.</param>
+ /// <param name="nextCommandHandler">The next command handler in the command execution chain.</param>
+ /// <param name="commandHandler">A command handler to execute the command.</param>
+ /// <param name="executionContext">Current command execution context.</param>
+ public static void ExecuteCommand<T>(this ICommandHandler commandHandler, T args, Action nextCommandHandler, CommandExecutionContext executionContext) where T : CommandArgs
+ {
+ if (commandHandler == null)
+ {
+ throw new ArgumentNullException(nameof(commandHandler));
+ }
+
+ if (nextCommandHandler == null)
+ {
+ throw new ArgumentNullException(nameof(nextCommandHandler));
+ }
+
+ if (commandHandler is ICommandHandler<T> simpleCommandHandler)
+ {
+ if (simpleCommandHandler.ExecuteCommand(args, executionContext))
+ {
+ return;
+ }
+ else
+ {
+ nextCommandHandler();
+ return;
+ }
+ }
+
+ if (commandHandler is IChainedCommandHandler<T> chainedCommandHandler)
+ {
+ chainedCommandHandler.ExecuteCommand(args, nextCommandHandler, executionContext);
+ return;
+ }
+
+ throw new ArgumentException($"Unsupported CommandHandler type: {commandHandler.GetType()}");
+ }
+ }
+}
+
diff --git a/src/Text/Def/TextUI/Commanding/CommandState.cs b/src/Text/Def/TextUI/Commanding/CommandState.cs
new file mode 100644
index 0000000..93680c1
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/CommandState.cs
@@ -0,0 +1,55 @@
+using System;
+
+namespace Microsoft.VisualStudio.Commanding
+{
+ public struct CommandState
+ {
+ /// <summary>
+ /// If true, the command state is unspecified and should not be taken into account.
+ /// </summary>
+ public bool IsUnspecified { get; }
+
+ /// <summary>
+ /// If true, the command should be visible and enabled in the UI.
+ /// </summary>
+ public bool IsAvailable { get; }
+
+ /// <summary>
+ /// If true, the command should appear as checked (i.e. toggled) in the UI.
+ /// </summary>
+ public bool IsChecked { get; }
+
+ /// <summary>
+ /// If specified, returns the custom text that should be displayed in the UI.
+ /// </summary>
+ public string DisplayText { get; }
+
+ public CommandState(bool isAvailable = false, bool isChecked = false, string displayText = null, bool isUnspecified = false)
+ {
+ if (isUnspecified && (isAvailable || isChecked || displayText != null))
+ {
+ throw new ArgumentException("Unspecified command state cannot be combined with other states or command text.");
+ }
+
+ this.IsAvailable = isAvailable;
+ this.IsChecked = isChecked;
+ this.IsUnspecified = isUnspecified;
+ this.DisplayText = displayText;
+ }
+
+ /// <summary>
+ /// A helper singleton representing an available command state.
+ /// </summary>
+ public static CommandState Available { get; } = new CommandState(isAvailable: true);
+
+ /// <summary>
+ /// A helper singleton representing an unavailable command state.
+ /// </summary>
+ public static CommandState Unavailable { get; } = new CommandState(isAvailable: false);
+
+ /// <summary>
+ /// A helper singleton representing an unspecified command state.
+ /// </summary>
+ public static CommandState Unspecified { get; } = new CommandState(isUnspecified: true);
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/AutomaticLineEnderCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/AutomaticLineEnderCommandArgs.cs
new file mode 100644
index 0000000..dca0d0c
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/AutomaticLineEnderCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class AutomaticLineEnderCommandArgs : EditorCommandArgs
+ {
+ public AutomaticLineEnderCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/BackTabKeyCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/BackTabKeyCommandArgs.cs
new file mode 100644
index 0000000..eeea83d
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/BackTabKeyCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class BackTabKeyCommandArgs : EditorCommandArgs
+ {
+ public BackTabKeyCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/BackspaceKeyCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/BackspaceKeyCommandArgs.cs
new file mode 100644
index 0000000..5808047
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/BackspaceKeyCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class BackspaceKeyCommandArgs : EditorCommandArgs
+ {
+ public BackspaceKeyCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/CommentSelectionCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/CommentSelectionCommandArgs.cs
new file mode 100644
index 0000000..5072200
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/CommentSelectionCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class CommentSelectionCommandArgs : EditorCommandArgs
+ {
+ public CommentSelectionCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/CommitUniqueCompletionListItemCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/CommitUniqueCompletionListItemCommandArgs.cs
new file mode 100644
index 0000000..48c34f5
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/CommitUniqueCompletionListItemCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class CommitUniqueCompletionListItemCommandArgs : EditorCommandArgs
+ {
+ public CommitUniqueCompletionListItemCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/ContractSelectionCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/ContractSelectionCommandArgs.cs
new file mode 100644
index 0000000..038e9e3
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/ContractSelectionCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class ContractSelectionCommandArgs: EditorCommandArgs
+ {
+ public ContractSelectionCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/CopyCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/CopyCommandArgs.cs
new file mode 100644
index 0000000..47037f1
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/CopyCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class CopyCommandArgs : EditorCommandArgs
+ {
+ public CopyCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/CopyToInteractiveCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/CopyToInteractiveCommandArgs.cs
new file mode 100644
index 0000000..4351246
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/CopyToInteractiveCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class CopyToInteractiveCommandArgs : EditorCommandArgs
+ {
+ public CopyToInteractiveCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/CutCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/CutCommandArgs.cs
new file mode 100644
index 0000000..fb60598
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/CutCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class CutCommandArgs : EditorCommandArgs
+ {
+ public CutCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/DeleteKeyCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/DeleteKeyCommandArgs.cs
new file mode 100644
index 0000000..434ab6f
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/DeleteKeyCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class DeleteKeyCommandArgs : EditorCommandArgs
+ {
+ public DeleteKeyCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/DocumentEndCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/DocumentEndCommandArgs.cs
new file mode 100644
index 0000000..657e6ae
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/DocumentEndCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class DocumentEndCommandArgs : EditorCommandArgs
+ {
+ public DocumentEndCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/DocumentStartCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/DocumentStartCommandArgs.cs
new file mode 100644
index 0000000..b8bc5d4
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/DocumentStartCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class DocumentStartCommandArgs : EditorCommandArgs
+ {
+ public DocumentStartCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/DownKeyCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/DownKeyCommandArgs.cs
new file mode 100644
index 0000000..7f2ed81
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/DownKeyCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class DownKeyCommandArgs : EditorCommandArgs
+ {
+ public DownKeyCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/DuplicateSelectionCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/DuplicateSelectionCommandArgs.cs
new file mode 100644
index 0000000..12b6628
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/DuplicateSelectionCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class DuplicateSelectionCommandArgs: EditorCommandArgs
+ {
+ public DuplicateSelectionCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/EncapsulateFieldCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/EncapsulateFieldCommandArgs.cs
new file mode 100644
index 0000000..13786b1
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/EncapsulateFieldCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class EncapsulateFieldCommandArgs : EditorCommandArgs
+ {
+ public EncapsulateFieldCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/EscapeKeyCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/EscapeKeyCommandArgs.cs
new file mode 100644
index 0000000..7f7e30c
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/EscapeKeyCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class EscapeKeyCommandArgs : EditorCommandArgs
+ {
+ public EscapeKeyCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/ExecuteInInteractiveCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/ExecuteInInteractiveCommandArgs.cs
new file mode 100644
index 0000000..8ea34f7
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/ExecuteInInteractiveCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class ExecuteInInteractiveCommandArgs : EditorCommandArgs
+ {
+ public ExecuteInInteractiveCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/ExpandSelectionCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/ExpandSelectionCommandArgs.cs
new file mode 100644
index 0000000..7526f19
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/ExpandSelectionCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class ExpandSelectionCommandArgs: EditorCommandArgs
+ {
+ public ExpandSelectionCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/ExtractInterfaceCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/ExtractInterfaceCommandArgs.cs
new file mode 100644
index 0000000..0c48f8f
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/ExtractInterfaceCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class ExtractInterfaceCommandArgs : EditorCommandArgs
+ {
+ public ExtractInterfaceCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/ExtractMethodCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/ExtractMethodCommandArgs.cs
new file mode 100644
index 0000000..3e8694f
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/ExtractMethodCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class ExtractMethodCommandArgs : EditorCommandArgs
+ {
+ public ExtractMethodCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/FindReferencesCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/FindReferencesCommandArgs.cs
new file mode 100644
index 0000000..32aadd1
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/FindReferencesCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class FindReferencesCommandArgs : EditorCommandArgs
+ {
+ public FindReferencesCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/FormatDocumentCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/FormatDocumentCommandArgs.cs
new file mode 100644
index 0000000..7ba8eeb
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/FormatDocumentCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class FormatDocumentCommandArgs : EditorCommandArgs
+ {
+ public FormatDocumentCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/FormatSelectionCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/FormatSelectionCommandArgs.cs
new file mode 100644
index 0000000..57f821e
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/FormatSelectionCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class FormatSelectionCommandArgs : EditorCommandArgs
+ {
+ public FormatSelectionCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/GoToDefinitionCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/GoToDefinitionCommandArgs.cs
new file mode 100644
index 0000000..e5df4e9
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/GoToDefinitionCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class GoToDefinitionCommandArgs : EditorCommandArgs
+ {
+ public GoToDefinitionCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/GoToNextMemberCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/GoToNextMemberCommandArgs.cs
new file mode 100644
index 0000000..8f74c77
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/GoToNextMemberCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class GoToNextMemberCommandArgs : EditorCommandArgs
+ {
+ public GoToNextMemberCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/GoToPreviousMemberCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/GoToPreviousMemberCommandArgs.cs
new file mode 100644
index 0000000..af8d3c8
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/GoToPreviousMemberCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class GoToPreviousMemberCommandArgs : EditorCommandArgs
+ {
+ public GoToPreviousMemberCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/InsertCommentCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/InsertCommentCommandArgs.cs
new file mode 100644
index 0000000..e7e3bd3
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/InsertCommentCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class InsertCommentCommandArgs : EditorCommandArgs
+ {
+ public InsertCommentCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/InsertSnippetCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/InsertSnippetCommandArgs.cs
new file mode 100644
index 0000000..cba1f12
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/InsertSnippetCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class InsertSnippetCommandArgs : EditorCommandArgs
+ {
+ public InsertSnippetCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/InvokeCompletionListCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/InvokeCompletionListCommandArgs.cs
new file mode 100644
index 0000000..8cd8bc3
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/InvokeCompletionListCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class InvokeCompletionListCommandArgs : EditorCommandArgs
+ {
+ public InvokeCompletionListCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/InvokeQuickInfoCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/InvokeQuickInfoCommandArgs.cs
new file mode 100644
index 0000000..4e317b5
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/InvokeQuickInfoCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class InvokeQuickInfoCommandArgs : EditorCommandArgs
+ {
+ public InvokeQuickInfoCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/InvokeSignatureHelpCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/InvokeSignatureHelpCommandArgs.cs
new file mode 100644
index 0000000..09a2b52
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/InvokeSignatureHelpCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class InvokeSignatureHelpCommandArgs : EditorCommandArgs
+ {
+ public InvokeSignatureHelpCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/LeftKeyCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/LeftKeyCommandArgs.cs
new file mode 100644
index 0000000..5bdc8b7
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/LeftKeyCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class LeftKeyCommandArgs : EditorCommandArgs
+ {
+ public LeftKeyCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/LineEndCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/LineEndCommandArgs.cs
new file mode 100644
index 0000000..320c096
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/LineEndCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class LineEndCommandArgs : EditorCommandArgs
+ {
+ public LineEndCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/LineEndExtendCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/LineEndExtendCommandArgs.cs
new file mode 100644
index 0000000..ab66007
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/LineEndExtendCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class LineEndExtendCommandArgs : EditorCommandArgs
+ {
+ public LineEndExtendCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/LineStartCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/LineStartCommandArgs.cs
new file mode 100644
index 0000000..decb21d
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/LineStartCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class LineStartCommandArgs : EditorCommandArgs
+ {
+ public LineStartCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/LineStartExtendCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/LineStartExtendCommandArgs.cs
new file mode 100644
index 0000000..1282c85
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/LineStartExtendCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class LineStartExtendCommandArgs : EditorCommandArgs
+ {
+ public LineStartExtendCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/MoveSelectedLinesDownCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/MoveSelectedLinesDownCommandArgs.cs
new file mode 100644
index 0000000..2c3d701
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/MoveSelectedLinesDownCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class MoveSelectedLinesDownCommandArgs : EditorCommandArgs
+ {
+ public MoveSelectedLinesDownCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/MoveSelectedLinesUpCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/MoveSelectedLinesUpCommandArgs.cs
new file mode 100644
index 0000000..c92378a
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/MoveSelectedLinesUpCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class MoveSelectedLinesUpCommandArgs : EditorCommandArgs
+ {
+ public MoveSelectedLinesUpCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/NavigateToNextHighlightedReferenceCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/NavigateToNextHighlightedReferenceCommandArgs.cs
new file mode 100644
index 0000000..d8353b9
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/NavigateToNextHighlightedReferenceCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class NavigateToNextHighlightedReferenceCommandArgs : EditorCommandArgs
+ {
+ public NavigateToNextHighlightedReferenceCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/NavigateToPreviousHighlightedReferenceCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/NavigateToPreviousHighlightedReferenceCommandArgs.cs
new file mode 100644
index 0000000..86cbf14
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/NavigateToPreviousHighlightedReferenceCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class NavigateToPreviousHighlightedReferenceCommandArgs : EditorCommandArgs
+ {
+ public NavigateToPreviousHighlightedReferenceCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/OpenLineAboveCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/OpenLineAboveCommandArgs.cs
new file mode 100644
index 0000000..5a2f736
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/OpenLineAboveCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class OpenLineAboveCommandArgs : EditorCommandArgs
+ {
+ public OpenLineAboveCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/OpenLineBelowCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/OpenLineBelowCommandArgs.cs
new file mode 100644
index 0000000..808bbac
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/OpenLineBelowCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class OpenLineBelowCommandArgs : EditorCommandArgs
+ {
+ public OpenLineBelowCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/PageDownKeyCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/PageDownKeyCommandArgs.cs
new file mode 100644
index 0000000..f54a5df
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/PageDownKeyCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class PageDownKeyCommandArgs : EditorCommandArgs
+ {
+ public PageDownKeyCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/PageUpKeyCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/PageUpKeyCommandArgs.cs
new file mode 100644
index 0000000..0887797
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/PageUpKeyCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class PageUpKeyCommandArgs : EditorCommandArgs
+ {
+ public PageUpKeyCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/PasteCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/PasteCommandArgs.cs
new file mode 100644
index 0000000..d307787
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/PasteCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class PasteCommandArgs : EditorCommandArgs
+ {
+ public PasteCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/RedoCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/RedoCommandArgs.cs
new file mode 100644
index 0000000..5e606b8
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/RedoCommandArgs.cs
@@ -0,0 +1,12 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class RedoCommandArgs : EditorCommandArgs
+ {
+ public readonly int Count;
+
+ public RedoCommandArgs(ITextView textView, ITextBuffer subjectBuffer, int count = 1) : base(textView, subjectBuffer)
+ {
+ this.Count = count;
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/RemoveParametersCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/RemoveParametersCommandArgs.cs
new file mode 100644
index 0000000..d34e1ae
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/RemoveParametersCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class RemoveParametersCommandArgs : EditorCommandArgs
+ {
+ public RemoveParametersCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/RenameCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/RenameCommandArgs.cs
new file mode 100644
index 0000000..6439a59
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/RenameCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class RenameCommandArgs : EditorCommandArgs
+ {
+ public RenameCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/ReorderParametersCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/ReorderParametersCommandArgs.cs
new file mode 100644
index 0000000..b582981
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/ReorderParametersCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class ReorderParametersCommandArgs : EditorCommandArgs
+ {
+ public ReorderParametersCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/ReturnKeyCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/ReturnKeyCommandArgs.cs
new file mode 100644
index 0000000..d2744ce
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/ReturnKeyCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class ReturnKeyCommandArgs : EditorCommandArgs
+ {
+ public ReturnKeyCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/RightKeyCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/RightKeyCommandArgs.cs
new file mode 100644
index 0000000..45b2b8c
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/RightKeyCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class RightKeyCommandArgs : EditorCommandArgs
+ {
+ public RightKeyCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/SaveCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/SaveCommandArgs.cs
new file mode 100644
index 0000000..1a45c29
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/SaveCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class SaveCommandArgs : EditorCommandArgs
+ {
+ public SaveCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/SelectAllCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/SelectAllCommandArgs.cs
new file mode 100644
index 0000000..829b978
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/SelectAllCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class SelectAllCommandArgs : EditorCommandArgs
+ {
+ public SelectAllCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/StartAutomaticOutliningCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/StartAutomaticOutliningCommandArgs.cs
new file mode 100644
index 0000000..ad097e5
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/StartAutomaticOutliningCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class StartAutomaticOutliningCommandArgs : EditorCommandArgs
+ {
+ public StartAutomaticOutliningCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/SurroundWithCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/SurroundWithCommandArgs.cs
new file mode 100644
index 0000000..844451e
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/SurroundWithCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class SurroundWithCommandArgs : EditorCommandArgs
+ {
+ public SurroundWithCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/SyncClassViewCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/SyncClassViewCommandArgs.cs
new file mode 100644
index 0000000..3ecd832
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/SyncClassViewCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class SyncClassViewCommandArgs : EditorCommandArgs
+ {
+ public SyncClassViewCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/TabKeyCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/TabKeyCommandArgs.cs
new file mode 100644
index 0000000..24a9c25
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/TabKeyCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class TabKeyCommandArgs : EditorCommandArgs
+ {
+ public TabKeyCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/ToggleCompletionModeCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/ToggleCompletionModeCommandArgs.cs
new file mode 100644
index 0000000..ddbfa6b
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/ToggleCompletionModeCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class ToggleCompletionModeCommandArgs : EditorCommandArgs
+ {
+ public ToggleCompletionModeCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/TypeCharCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/TypeCharCommandArgs.cs
new file mode 100644
index 0000000..9ff72dc
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/TypeCharCommandArgs.cs
@@ -0,0 +1,11 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class TypeCharCommandArgs : EditorCommandArgs
+ {
+ public char TypedChar { get; }
+ public TypeCharCommandArgs(ITextView textView, ITextBuffer subjectBuffer, char typedChar) : base(textView, subjectBuffer)
+ {
+ TypedChar = typedChar;
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/UncommentSelectionCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/UncommentSelectionCommandArgs.cs
new file mode 100644
index 0000000..9c46e39
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/UncommentSelectionCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class UncommentSelectionCommandArgs : EditorCommandArgs
+ {
+ public UncommentSelectionCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/UndoCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/UndoCommandArgs.cs
new file mode 100644
index 0000000..ce0df8b
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/UndoCommandArgs.cs
@@ -0,0 +1,12 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class UndoCommandArgs : EditorCommandArgs
+ {
+ public readonly int Count;
+
+ public UndoCommandArgs(ITextView textView, ITextBuffer subjectBuffer, int count = 1) : base(textView, subjectBuffer)
+ {
+ this.Count = count;
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/UpKeyCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/UpKeyCommandArgs.cs
new file mode 100644
index 0000000..29b6bb2
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/UpKeyCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class UpKeyCommandArgs : EditorCommandArgs
+ {
+ public UpKeyCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/ViewCallHierarchyCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/ViewCallHierarchyCommandArgs.cs
new file mode 100644
index 0000000..3eea996
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/ViewCallHierarchyCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class ViewCallHierarchyCommandArgs : EditorCommandArgs
+ {
+ public ViewCallHierarchyCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/WordDeleteToEndCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/WordDeleteToEndCommandArgs.cs
new file mode 100644
index 0000000..eeeddc5
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/WordDeleteToEndCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class WordDeleteToEndCommandArgs : EditorCommandArgs
+ {
+ public WordDeleteToEndCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/Commands/WordDeleteToStartCommandArgs.cs b/src/Text/Def/TextUI/Commanding/Commands/WordDeleteToStartCommandArgs.cs
new file mode 100644
index 0000000..5d8c532
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/Commands/WordDeleteToStartCommandArgs.cs
@@ -0,0 +1,9 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding.Commands
+{
+ public sealed class WordDeleteToStartCommandArgs : EditorCommandArgs
+ {
+ public WordDeleteToStartCommandArgs(ITextView textView, ITextBuffer subjectBuffer) : base(textView, subjectBuffer)
+ {
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/EditorCommandArgs.cs b/src/Text/Def/TextUI/Commanding/EditorCommandArgs.cs
new file mode 100644
index 0000000..deaf9cb
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/EditorCommandArgs.cs
@@ -0,0 +1,33 @@
+using System;
+using Microsoft.VisualStudio.Commanding;
+
+namespace Microsoft.VisualStudio.Text.Editor.Commanding
+{
+ /// <summary>
+ /// A base class for all editor command arguments.
+ /// </summary>
+ public abstract class EditorCommandArgs : CommandArgs
+ {
+ /// <summary>
+ /// A subject buffer to execute a command on.
+ /// </summary>
+ public ITextBuffer SubjectBuffer { get; }
+
+ /// <summary>
+ /// An <see cref="ITextView"/> to execute a command on.
+ /// </summary>
+ public ITextView TextView { get; }
+
+ /// <summary>
+ /// Creates new instance of the <see cref="EditorCommandArgs"/> with given
+ /// <see cref="ITextView"/> and <see cref="ITextBuffer"/>.
+ /// </summary>
+ /// <param name="textView">A <see cref="ITextView"/> to execute a command on.</param>
+ /// <param name="subjectBuffer">A <see cref="ITextBuffer"/> to execute command on.</param>
+ public EditorCommandArgs(ITextView textView, ITextBuffer subjectBuffer)
+ {
+ this.TextView = textView ?? throw new ArgumentNullException(nameof(textView));
+ this.SubjectBuffer = subjectBuffer ?? throw new ArgumentNullException(nameof(subjectBuffer));
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/IChainedCommandHandler.cs b/src/Text/Def/TextUI/Commanding/IChainedCommandHandler.cs
new file mode 100644
index 0000000..7dab7e3
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/IChainedCommandHandler.cs
@@ -0,0 +1,39 @@
+using System;
+using Microsoft.VisualStudio.Utilities;
+
+namespace Microsoft.VisualStudio.Commanding
+{
+ /// <summary>
+ /// Represents a command handler that depends on behavior of following command handlers in the command execution chain
+ /// formed from same strongly-typed <see cref="ICommandHandler"/>s ordered according to their [Order] attributes.
+ /// </summary>
+ /// <remarks>
+ /// This is a MEF component part and should be exported as the non-generic <see cref="ICommandHandler"/> with required
+ /// [Name], [ContentType] attributes and optional [Order] and [TextViewRole] attributes.
+ /// </remarks>
+ /// <example>
+ /// [Export(typeof(ICommandHandler))]
+ /// [Name(nameof(MyCommandHandler))]
+ /// [ContentType("text")]
+ /// [Order(Before ="OtherCommandHandler")]
+ /// [TextViewRole(PredefinedTextViewRoles.Editable)]
+ /// internal class MyCommandHandler : IChainedCommandHandler<MyCommandArgs>
+ /// </example>
+ public interface IChainedCommandHandler<T> : ICommandHandler, INamed where T : CommandArgs
+ {
+ /// <summary>
+ /// Called to determine the state of the command.
+ /// </summary>
+ /// <param name="args">The <see cref="CommandArgs"/> arguments for the command.</param>
+ /// <param name="nextCommandHandler">The next command handler in the command execution chain.</param>
+ /// <returns>A <see cref="CommandState"/> instance that contains information on the availability of the command.</returns>
+ CommandState GetCommandState(T args, Func<CommandState> nextCommandHandler);
+
+ /// <summary>
+ /// Called to execute the command.
+ /// </summary>
+ /// <param name="args">The <see cref="CommandArgs"/> arguments for the command.</param>
+ /// <param name="nextCommandHandler">The next command handler in the command execution chain.</param>
+ void ExecuteCommand(T args, Action nextCommandHandler, CommandExecutionContext executionContext);
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/ICommandHandler.cs b/src/Text/Def/TextUI/Commanding/ICommandHandler.cs
new file mode 100644
index 0000000..c999be2
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/ICommandHandler.cs
@@ -0,0 +1,56 @@
+using Microsoft.VisualStudio.Utilities;
+
+namespace Microsoft.VisualStudio.Commanding
+{
+ /// <summary>
+ /// This interface marks a class that implements at least one strongly-typed
+ /// <see cref="ICommandHandler{T}"/> or <see cref="IChainedCommandHandler{T}"/>.
+ /// </summary>
+ /// <remarks>
+ /// This is a MEF component part and should be exported as the non-generic <see cref="ICommandHandler"/> with required
+ /// [Name], [ContentType] attributes and optional [Order] and [TextViewRole] attributes.
+ /// </remarks>
+ /// <example>
+ /// [Export(typeof(ICommandHandler))]
+ /// [Name(nameof(MyCommandHandler))]
+ /// [ContentType("text")]
+ /// [Order(Before ="OtherCommandHandler")]
+ /// [TextViewRole(PredefinedTextViewRoles.Editable)]
+ /// internal class MyCommandHandler : ICommandHandler<MyCommandArgs>
+ /// </example>
+ public interface ICommandHandler
+ {
+ }
+
+ /// <summary>
+ /// Represents a handler for a command associated with specific <see cref="CommandArgs"/>.
+ /// </summary>
+ /// <remarks>
+ /// This is a MEF component part and should be exported as the non-generic <see cref="ICommandHandler"/> with required
+ /// [Name], [ContentType] attributes and optional [Order] and [TextViewRole] attributes.
+ /// </remarks>
+ /// <example>
+ /// [Export(typeof(ICommandHandler))]
+ /// [Name(nameof(MyCommandHandler))]
+ /// [ContentType("text")]
+ /// [Order(Before ="OtherCommandHandler")]
+ /// [TextViewRole(PredefinedTextViewRoles.Editable)]
+ /// internal class MyCommandHandler : ICommandHandler<MyCommandArgs>
+ /// </example>
+ public interface ICommandHandler<T> : ICommandHandler, INamed where T : CommandArgs
+ {
+ /// <summary>
+ /// Called to determine the state of the command.
+ /// </summary>
+ /// <param name="args">The <see cref="CommandArgs"/> arguments for the command.</param>
+ /// <returns>A <see cref="CommandState"/> instance that contains information on the availability of the command.</returns>
+ CommandState GetCommandState(T args);
+
+ /// <summary>
+ /// Called to execute the command.
+ /// </summary>
+ /// <param name="args">The <see cref="CommandArgs"/> arguments for the command.</param>
+ /// <returns>Returns <c>true</c> if the command was handled, <c>false</c> otherwise.</returns>
+ bool ExecuteCommand(T args, CommandExecutionContext executionContext);
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/ICommandingTextBufferResolver.cs b/src/Text/Def/TextUI/Commanding/ICommandingTextBufferResolver.cs
new file mode 100644
index 0000000..4cf5a12
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/ICommandingTextBufferResolver.cs
@@ -0,0 +1,20 @@
+using System.Collections.Generic;
+
+namespace Microsoft.VisualStudio.Text.Editor.Commanding
+{
+ /// <summary>
+ /// Given a text view and a command type, resolves a list of text buffers on which a command should be executed.
+ /// Default implementation of this service returns a list of buffers in the text view which can be mapped
+ /// to the caret position. Other implementations might take into acount text selection in addition to the caret position,
+ /// for example when executing Format Document command in a projection scenario.
+ /// </summary>
+ public interface ICommandingTextBufferResolver
+ {
+ /// <summary>
+ /// Given a command type, resolves a list of text buffers on which a command should be executed.
+ /// </summary>
+ /// <typeparam name="TArgs">Command type.</typeparam>
+ /// <returns>A list of text buffers on which a command should be executed.</returns>
+ IEnumerable<ITextBuffer> ResolveBuffersForCommand<TArgs>() where TArgs : EditorCommandArgs;
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/ICommandingTextBufferResolverProvider.cs b/src/Text/Def/TextUI/Commanding/ICommandingTextBufferResolverProvider.cs
new file mode 100644
index 0000000..eef5914
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/ICommandingTextBufferResolverProvider.cs
@@ -0,0 +1,23 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding
+{
+ /// <summary>
+ /// Provides a <see cref="ICommandingTextBufferResolver"/> for a given
+ /// <see cref="ITextView"/> and content type.
+ /// </summary>
+ /// <remarks>This is a MEF component and should be exported as
+ ///
+ /// Export(typeof(ICommandingTextBufferResolverProvider))]
+ /// [ContentType("MyContentType")]
+ /// internal class MyBufferResolverProvider : ICommandingTextBufferResolverProvider
+ /// </remarks>
+ public interface ICommandingTextBufferResolverProvider
+ {
+ /// <summary>
+ /// Creates a <see cref="ICommandingTextBufferResolver"/> for a given
+ /// <see cref="ITextView"/>.
+ /// </summary>
+ /// <param name="textView">A <see cref="ITextView"/> to create a text buffer resolver for.</param>
+ /// <returns>A new instance of <see cref="ICommandingTextBufferResolver"/>.</returns>
+ ICommandingTextBufferResolver CreateResolver(ITextView textView);
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/IEditorCommandHandlerService.cs b/src/Text/Def/TextUI/Commanding/IEditorCommandHandlerService.cs
new file mode 100644
index 0000000..2516bd7
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/IEditorCommandHandlerService.cs
@@ -0,0 +1,32 @@
+using Microsoft.VisualStudio.Commanding;
+using System;
+
+namespace Microsoft.VisualStudio.Text.Editor.Commanding
+{
+ /// <summary>
+ /// A service to execute commands on a text view.
+ /// </summary>
+ /// <remarks>
+ /// Instance of this service are created by <see cref="IEditorCommandHandlerServiceFactory"/>.
+ /// </remarks>
+ public interface IEditorCommandHandlerService
+ {
+ /// <summary>
+ /// Get the <see cref="CommandState"/> for command handlers of a given command.
+ /// </summary>
+ /// <param name="argsFactory">A factory of <see cref="EditorCommandArgs"/> that specifies what kind of command is being queried.</param>
+ /// <param name="nextCommandHandler">A next command handler to be called if no command handlers were
+ /// able to determine a command state.</param>
+ /// <typeparam name="T">Tehe </typeparam>
+ /// <returns>The command state of a given command.</returns>
+ CommandState GetCommandState<T>(Func<ITextView, ITextBuffer, T> argsFactory, Func<CommandState> nextCommandHandler) where T : EditorCommandArgs;
+
+ /// <summary>
+ /// Execute a given command on the <see cref="ITextView"/> associated with this <see cref="IEditorCommandHandlerService"/> instance.
+ /// </summary>
+ /// <param name="argsFactory">A factory of <see cref="EditorCommandArgs"/> that specifies what kind of command is being executed.
+ /// <paramref name="nextCommandHandler">>A next command handler to be called if no command handlers were
+ /// able to handle a command.</paramref>
+ void Execute<T>(Func<ITextView, ITextBuffer, T> argsFactory, Action nextCommandHandler) where T : EditorCommandArgs;
+ }
+}
diff --git a/src/Text/Def/TextUI/Commanding/IEditorCommandHandlerServiceFactory.cs b/src/Text/Def/TextUI/Commanding/IEditorCommandHandlerServiceFactory.cs
new file mode 100644
index 0000000..226deb2
--- /dev/null
+++ b/src/Text/Def/TextUI/Commanding/IEditorCommandHandlerServiceFactory.cs
@@ -0,0 +1,27 @@
+namespace Microsoft.VisualStudio.Text.Editor.Commanding
+{
+ /// <summary>
+ /// A factory producing <see cref="IEditorCommandHandlerService"/> used to execute commands in a given text view.
+ /// </summary>
+ /// <remarks>
+ /// This is a MEF component and should be imported as
+ ///
+ /// [Import]
+ /// private IEditorCommandHandlerServiceFactory factory;
+ /// </remarks>
+ public interface IEditorCommandHandlerServiceFactory
+ {
+ /// <summary>
+ /// Gets or creates a <see cref="IEditorCommandHandlerService"/> for a given <see cref="ITextView"/>.
+ /// </summary>
+ /// <param name="textView">A text view to get or create <see cref="IEditorCommandHandlerService"/> for.</param>
+ IEditorCommandHandlerService GetService(ITextView textView);
+
+ /// <summary>
+ /// Gets or creates a <see cref="IEditorCommandHandlerService"/> for a given <see cref="ITextView"/> and <see cref="ITextBuffer"/>.
+ /// </summary>
+ /// <param name="textView">A text view to get or create <see cref="IEditorCommandHandlerService"/> for.</param>
+ /// <param name="subjectBuffer">A text buffer to get or create <see cref="IEditorCommandHandlerService"/> for.</param>
+ IEditorCommandHandlerService GetService(ITextView textView, ITextBuffer subjectBuffer);
+ }
+}
diff --git a/src/Text/Def/TextUI/Utilities/AbstractUIThreadOperationContext.cs b/src/Text/Def/TextUI/Utilities/AbstractUIThreadOperationContext.cs
new file mode 100644
index 0000000..c7d8279
--- /dev/null
+++ b/src/Text/Def/TextUI/Utilities/AbstractUIThreadOperationContext.cs
@@ -0,0 +1,219 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+
+namespace Microsoft.VisualStudio.Utilities
+{
+ /// <summary>
+ /// Abstract base implementation of the <see cref="IUIThreadOperationContext"/> interface.
+ /// </summary>
+ public abstract class AbstractUIThreadOperationContext : IUIThreadOperationContext
+ {
+ private List<IUIThreadOperationScope> _scopes;
+ private bool _allowCancellation;
+ private PropertyCollection _properties;
+ private readonly string _contextDescription;
+ private int _completedItems;
+ private int _totalItems;
+
+ /// <summary>
+ /// Creates a new instance of the <see cref="AbstractUIThreadOperationContext"/>.
+ /// </summary>
+ /// <param name="allowCancellation">Initial value of the <see cref="IUIThreadOperationContext.AllowCancellation"/>
+ /// property, which can change as new scopes are added to the context.</param>
+ /// <param name="description">Initial value of the <see cref="IUIThreadOperationContext.Description"/>
+ /// property, which can change as new scopes are added to the context.</param>
+ public AbstractUIThreadOperationContext(bool allowCancellation, string description)
+ {
+ _contextDescription = description ?? throw new ArgumentNullException(nameof(description));
+ _allowCancellation = allowCancellation;
+ }
+
+ /// <summary>
+ /// Cancellation token for cancelling the operation.
+ /// </summary>
+ public virtual CancellationToken UserCancellationToken => CancellationToken.None;
+
+ /// <summary>
+ /// Gets whether the operation can be cancelled.
+ /// </summary>
+ /// <remarks>This value is composed of initial AllowCancellation value and
+ /// <see cref="IUIThreadOperationScope.AllowCancellation"/> values of all currently added scopes.
+ /// The value composition logic takes into acount disposed scopes too - if any of added scopes
+ /// were disposed while its <see cref="IUIThreadOperationScope.AllowCancellation"/> was false,
+ /// this property will stay false regardless of all other scopes' <see cref="IUIThreadOperationScope.AllowCancellation"/>
+ /// values.
+ /// </remarks>
+ public virtual bool AllowCancellation
+ {
+ get
+ {
+ if (!_allowCancellation)
+ {
+ return false;
+ }
+
+ if (_scopes == null || _scopes.Count == 0)
+ {
+ return _allowCancellation;
+ }
+
+ return _scopes.All((s) => s.AllowCancellation);
+ }
+ }
+
+ /// <summary>
+ /// Gets user readable operation description, composed of initial context description and
+ /// descriptions of all currently added scopes.
+ /// </summary>
+ public virtual string Description
+ {
+ get
+ {
+ if (_scopes == null || _scopes.Count == 0)
+ {
+ return _contextDescription;
+ }
+
+ // Combine context description with descriptions of all current scopes
+ return _contextDescription + Environment.NewLine + string.Join(Environment.NewLine, _scopes.Select((s) => s.Description));
+ }
+ }
+
+ protected int CompletedItems => _completedItems;
+ protected int TotalItems => _totalItems;
+
+ private IList<IUIThreadOperationScope> LazyScopes => _scopes ?? (_scopes = new List<IUIThreadOperationScope>());
+
+ /// <summary>
+ /// Gets current list of <see cref="IUIThreadOperationScope"/>s in this context.
+ /// </summary>
+ public virtual IEnumerable<IUIThreadOperationScope> Scopes => this.LazyScopes;
+
+ /// <summary>
+ /// A collection of properties.
+ /// </summary>
+ public virtual PropertyCollection Properties => _properties ?? (_properties = new PropertyCollection());
+
+ /// <summary>
+ /// Adds an UI thread operation scope with its own cancellability, description and progress tracker.
+ /// The scope is removed from the context on dispose.
+ /// </summary>
+ public virtual IUIThreadOperationScope AddScope(bool allowCancellation, string description)
+ {
+ var scope = new UIThreadOperationScope(allowCancellation, description, this);
+ this.LazyScopes.Add(scope);
+ this.OnScopesChanged();
+ return scope;
+ }
+
+ protected virtual void OnScopeProgressChanged(IUIThreadOperationScope changedScope)
+ {
+ int completed = 0;
+ int total = 0;
+ foreach (UIThreadOperationScope scope in this.LazyScopes)
+ {
+ completed += scope.CompletedItems;
+ total += scope.TotalItems;
+ }
+
+ Interlocked.Exchange(ref _completedItems, completed);
+ Interlocked.Exchange(ref _totalItems, total);
+ }
+
+ /// <summary>
+ /// Invoked when new <see cref="IUIThreadOperationScope"/>s are added or disposed.
+ /// </summary>
+ protected virtual void OnScopesChanged() { }
+
+ protected virtual void OnScopeChanged(IUIThreadOperationScope uiThreadOperationScope)
+ {
+ }
+
+ /// <summary>
+ /// Disposes this instance.
+ /// </summary>
+ public virtual void Dispose()
+ {
+ }
+
+ /// <summary>
+ /// Allows a component to take full ownership over this UI thread operation, for example
+ /// when it shows its own modal UI dialog and handles cancellability through that dialog instead.
+ /// </summary>
+ public virtual void TakeOwnership()
+ {
+ }
+
+ protected virtual void OnScopeDisposed(IUIThreadOperationScope scope)
+ {
+ _allowCancellation &= scope.AllowCancellation;
+ _scopes.Remove(scope);
+ OnScopesChanged();
+ }
+
+ private class UIThreadOperationScope : IUIThreadOperationScope
+ {
+ private bool _allowCancellation;
+ private string _description;
+ private IProgress<ProgressInfo> _progress;
+ private readonly AbstractUIThreadOperationContext _context;
+ private int _completedItems;
+ private int _totalItems;
+
+ public UIThreadOperationScope(bool allowCancellation, string description, AbstractUIThreadOperationContext context)
+ {
+ _context = context ?? throw new ArgumentNullException(nameof(context));
+ this.AllowCancellation = allowCancellation;
+ this.Description = description ?? "";
+ }
+
+ public bool AllowCancellation
+ {
+ get { return _allowCancellation; }
+ set
+ {
+ if (_allowCancellation != value)
+ {
+ _allowCancellation = value;
+ _context.OnScopeChanged(this);
+ }
+ }
+ }
+
+ public string Description
+ {
+ get { return _description; }
+ set
+ {
+ if (_description != value)
+ {
+ _description = value;
+ _context.OnScopeChanged(this);
+ }
+ }
+ }
+
+ public IUIThreadOperationContext Context => _context;
+
+ public IProgress<ProgressInfo> Progress => _progress ?? (_progress = new Progress<ProgressInfo>((progressInfo) => OnProgressChanged(progressInfo)));
+
+ public int CompletedItems => _completedItems;
+
+ public int TotalItems => _totalItems;
+
+ private void OnProgressChanged(ProgressInfo progressInfo)
+ {
+ Interlocked.Exchange(ref _completedItems, progressInfo.CompletedItems);
+ Interlocked.Exchange(ref _totalItems, progressInfo.TotalItems);
+ _context.OnScopeProgressChanged(this);
+ }
+
+ public void Dispose()
+ {
+ _context.OnScopeDisposed(this);
+ }
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Utilities/IUIThreadOperationContext.cs b/src/Text/Def/TextUI/Utilities/IUIThreadOperationContext.cs
new file mode 100644
index 0000000..fce50d4
--- /dev/null
+++ b/src/Text/Def/TextUI/Utilities/IUIThreadOperationContext.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace Microsoft.VisualStudio.Utilities
+{
+ /// <summary>
+ /// Represents a context of executing potentially long running operation on the UI thread, which
+ /// enables shared two way cancellability and wait indication.
+ /// </summary>
+ /// <remarks>
+ /// Instances implementing this interface are produced by <see cref="IUIThreadOperationExecutor"/>
+ /// MEF component.
+ /// </remarks>
+ public interface IUIThreadOperationContext : IPropertyOwner, IDisposable
+ {
+ /// <summary>
+ /// Cancellation token that allows user to cancel the operation unless the operation
+ /// is not cancellable.
+ /// </summary>
+ CancellationToken UserCancellationToken { get; }
+
+ /// <summary>
+ /// Gets whether the operation can be cancelled.
+ /// </summary>
+ /// <remarks>This value is composed of initial AllowCancellation value and
+ /// <see cref="IUIThreadOperationScope.AllowCancellation"/> values of all currently added scopes.
+ /// The value composition logic takes into acount disposed scopes too - if any of added scopes
+ /// were disposed while its <see cref="IUIThreadOperationScope.AllowCancellation"/> was false,
+ /// this property will stay false regardless of all other scopes' <see cref="IUIThreadOperationScope.AllowCancellation"/>
+ /// values.
+ /// </remarks>
+ bool AllowCancellation { get; }
+
+ /// <summary>
+ /// Gets user readable operation description, composed of initial context description and
+ /// descriptions of all currently added scopes.
+ /// </summary>
+ string Description { get; }
+
+ /// <summary>
+ /// Gets current list of <see cref="IUIThreadOperationScope"/>s in this context.
+ /// </summary>
+ IEnumerable<IUIThreadOperationScope> Scopes { get; }
+
+ /// <summary>
+ /// Adds a UI thread operation scope with its own two way cancellability, description and progress tracker.
+ /// The scope is removed from the context on dispose.
+ /// </summary>
+ IUIThreadOperationScope AddScope(bool allowCancellation, string description);
+
+ /// <summary>
+ /// Allows a component to take full ownership over this UI thread operation, for example
+ /// when it shows its own modal UI dialog and handles cancellability through that dialog instead.
+ /// </summary>
+ void TakeOwnership();
+ }
+}
diff --git a/src/Text/Def/TextUI/Utilities/IUIThreadOperationExecutor.cs b/src/Text/Def/TextUI/Utilities/IUIThreadOperationExecutor.cs
new file mode 100644
index 0000000..3daf364
--- /dev/null
+++ b/src/Text/Def/TextUI/Utilities/IUIThreadOperationExecutor.cs
@@ -0,0 +1,89 @@
+using System;
+
+namespace Microsoft.VisualStudio.Utilities
+{
+ /// <summary>
+ /// Executes potentially long running operation on the UI thread and provides shared two way cancellability
+ /// and wait indication.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// Visual Studio implementation of this service measures operation execution duration
+ /// and displays a modal wait dialog if it takes too long. The wait dialog describes operation to the user,
+ /// optionally provides progress information and allows user to cancel the operation if
+ /// it can be cancelled.
+ /// Components running in the operation can affect the wait dialog via <see cref="IUIThreadOperationContext"/>
+ /// provided by this service.
+ /// </para>
+ /// <para>
+ /// This is a MEF component and should be imported for consumptions as follows:
+ ///
+ /// [Import]
+ /// private IUIThreadOperationExecutor uiThreadOperationExecutor = null;
+ /// </para>
+ /// <para>
+ /// Host specific implementations of this service should be exported as follows:
+ ///
+ /// [ExportImplementation(typeof(IUIThreadOperationExecutor))]
+ /// [Name("Visual Studio UI thread operation executor")]
+ /// [Order(Before = "default")]
+ /// internal sealed class VSUIThreadOperationExecutor : IUIThreadOperationExecutor
+ /// </para>
+ /// <para>
+ /// All methods of this interface should only be called on the UI thread.
+ /// </para>
+ /// </remarks>
+ /// <example>
+ /// A typical usage of <see cref="IUIThreadOperationExecutor"/> to execute a potentially
+ /// long running operation on the UI thread is as follows:
+ ///
+ /// [Import]
+ /// private IUIThreadOperationExecutor uiThreadOperationExecutor = null;
+ /// ...
+ /// UIThreadOperationStatus status = _uiThreadOperationExecutor.Execute("Format document",
+ /// "Please wait for document formatting...", allowCancel: true, showProgress: false,
+ /// action: (context) => Format(context.UserCancellationToken));
+ /// if (status == UIThreadOperationStatus.Completed)...
+ ///
+ /// Or alternatively
+ ///
+ /// using (var context = _uiThreadOperationExecutor.BeginExecute("Format document",
+ /// "Please wait for document formatting...", allowCancel: true, showProgress: false))
+ /// {
+ /// Format(context);
+ /// }
+ ///
+ /// private void Format(IUIThreadOperationContext context)
+ /// {
+ /// using (context.AddScope(allowCancellation: true, description: "Acquiring user preferences..."))
+ /// {...}
+ /// }
+ /// </example>
+ public interface IUIThreadOperationExecutor
+ {
+ /// <summary>
+ /// Executes the action synchronously and waits for it to complete.
+ /// </summary>
+ /// <param name="title">Operation's title.</param>
+ /// <param name="description">Initial operation's description.</param>
+ /// <param name="allowCancel">Whether to allow cancellability.</param>
+ /// <param name="showProgress">Whether to show progress indication.</param>
+ /// <param name="action">An action to execute.</param>
+ /// <returns>A status of action execution.</returns>
+ UIThreadOperationStatus Execute(string title, string description, bool allowCancel, bool showProgress,
+ Action<IUIThreadOperationContext> action);
+
+ /// <summary>
+ /// Begins executing potentially long running operation on the caller thread and provides a context object that provides access to shared
+ /// cancellability and wait indication.
+ /// </summary>
+ /// <param name="title">Operation's title.</param>
+ /// <param name="description">Initial operation's description.</param>
+ /// <param name="allowCancel">Whether to allow cancellability.</param>
+ /// <param name="showProgress">Whether to show progress indication.</param>
+ /// <returns><see cref="IUIThreadOperationContext"/> instance that provides access to shared two way
+ /// cancellability and wait indication for the given operation. The operation is considered executed
+ /// when this <see cref="IUIThreadOperationContext"/> instance is disposed.</returns>
+ IUIThreadOperationContext BeginExecute(string title, string description, bool allowCancel, bool showProgress);
+ }
+}
diff --git a/src/Text/Def/TextUI/Utilities/IUIThreadOperationScope.cs b/src/Text/Def/TextUI/Utilities/IUIThreadOperationScope.cs
new file mode 100644
index 0000000..8f1d05f
--- /dev/null
+++ b/src/Text/Def/TextUI/Utilities/IUIThreadOperationScope.cs
@@ -0,0 +1,58 @@
+using System;
+
+namespace Microsoft.VisualStudio.Utilities
+{
+ /// <summary>
+ /// Represents a single scope of a context of executing potentially long running operation on the UI thread.
+ /// Scopes allow multiple components running within an operation to share the same context.
+ /// </summary>
+ public interface IUIThreadOperationScope : IDisposable
+ {
+ /// <summary>
+ /// Gets or sets whether the operation can be cancelled.
+ /// </summary>
+ bool AllowCancellation { get; set; }
+
+ /// <summary>
+ /// Gets user readable operation description.
+ /// </summary>
+ string Description { get; set; }
+
+ /// <summary>
+ /// The <see cref="IUIThreadOperationContext" /> owning this scope instance.
+ /// </summary>
+ IUIThreadOperationContext Context { get; }
+
+ /// <summary>
+ /// Progress tracker instance to report operation progress.
+ /// </summary>
+ IProgress<ProgressInfo> Progress { get; }
+ }
+
+ /// <summary>
+ /// Represents an update of a progress.
+ /// </summary>
+ public struct ProgressInfo
+ {
+ /// <summary>
+ /// A number of already completed items.
+ /// </summary>
+ public int CompletedItems { get; }
+
+ /// <summary>
+ /// A total number if items.
+ /// </summary>
+ public int TotalItems { get; }
+
+ /// <summary>
+ /// Creates a new instance of the <see cref="ProgressInfo"/> struct.
+ /// </summary>
+ /// <param name="completedItems">A number of already completed items.</param>
+ /// <param name="totalItems">A total number if items.</param>
+ public ProgressInfo(int completedItems, int totalItems)
+ {
+ this.CompletedItems = completedItems;
+ this.TotalItems = totalItems;
+ }
+ }
+}
diff --git a/src/Text/Def/TextUI/Utilities/UIThreadOperationStatus.cs b/src/Text/Def/TextUI/Utilities/UIThreadOperationStatus.cs
new file mode 100644
index 0000000..b7e47ae
--- /dev/null
+++ b/src/Text/Def/TextUI/Utilities/UIThreadOperationStatus.cs
@@ -0,0 +1,18 @@
+namespace Microsoft.VisualStudio.Utilities
+{
+ /// <summary>
+ /// Represents a status of executing a potentially long running operation on the UI thread.
+ /// </summary>
+ public enum UIThreadOperationStatus
+ {
+ /// <summary>
+ /// An operation was successfully completed.
+ /// </summary>
+ Completed,
+
+ /// <summary>
+ /// An operation was cancelled.
+ /// </summary>
+ Canceled,
+ }
+}
diff --git a/src/Text/Impl/Commanding/CommandHandlerBucket.cs b/src/Text/Impl/Commanding/CommandHandlerBucket.cs
new file mode 100644
index 0000000..2374c76
--- /dev/null
+++ b/src/Text/Impl/Commanding/CommandHandlerBucket.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.VisualStudio.Commanding;
+
+namespace Microsoft.VisualStudio.UI.Text.Commanding.Implementation
+{
+ /// <summary>
+ /// Lightweight stack-like view over a readonly ordered list of command handlers.
+ /// </summary>
+ internal class CommandHandlerBucket
+ {
+ private int _currentCommandHandlerIndex;
+ private readonly IReadOnlyList<Lazy<ICommandHandler, ICommandHandlerMetadata>> _commandHandlers;
+
+ public CommandHandlerBucket(IReadOnlyList<Lazy<ICommandHandler, ICommandHandlerMetadata>> commandHandlers)
+ {
+ _commandHandlers = commandHandlers ?? throw new ArgumentNullException(nameof(commandHandlers));
+ }
+
+ public bool IsEmpty => _currentCommandHandlerIndex >= _commandHandlers.Count;
+
+ public Lazy<ICommandHandler, ICommandHandlerMetadata> Peek()
+ {
+ if (!IsEmpty)
+ {
+ return _commandHandlers[_currentCommandHandlerIndex];
+ }
+
+ throw new InvalidOperationException($"{nameof(CommandHandlerBucket)} is empty.");
+ }
+
+ internal Lazy<ICommandHandler, ICommandHandlerMetadata> Pop()
+ {
+ if (!IsEmpty)
+ {
+ return _commandHandlers[_currentCommandHandlerIndex++];
+ }
+
+ throw new InvalidOperationException($"{nameof(CommandHandlerBucket)} is empty.");
+ }
+ }
+}
diff --git a/src/Text/Impl/Commanding/CommandingStrings.Designer.cs b/src/Text/Impl/Commanding/CommandingStrings.Designer.cs
new file mode 100644
index 0000000..cfaadfd
--- /dev/null
+++ b/src/Text/Impl/Commanding/CommandingStrings.Designer.cs
@@ -0,0 +1,81 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace Microsoft.VisualStudio.UI.Text.Commanding.Implementation {
+ using System;
+
+
+ /// <summary>
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ /// </summary>
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class CommandingStrings {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal CommandingStrings() {
+ }
+
+ /// <summary>
+ /// Returns the cached ResourceManager instance used by this class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.VisualStudio.Text.Implementation.Text.Impl.Commanding.CommandingStrings", typeof(CommandingStrings).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ /// <summary>
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Executing a command.
+ /// </summary>
+ internal static string ExecutingCommand {
+ get {
+ return ResourceManager.GetString("ExecutingCommand", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Please wait for command execution to finish....
+ /// </summary>
+ internal static string WaitForCommandExecution {
+ get {
+ return ResourceManager.GetString("WaitForCommandExecution", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/Text/Impl/Commanding/CommandingStrings.resx b/src/Text/Impl/Commanding/CommandingStrings.resx
new file mode 100644
index 0000000..72d604d
--- /dev/null
+++ b/src/Text/Impl/Commanding/CommandingStrings.resx
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 1.3
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">1.3</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1">this is my long string</data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ [base64 mime encoded serialized .NET Framework object]
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ [base64 mime encoded string representing a byte array form of the .NET Framework object]
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>1.3</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <data name="ExecutingCommand" xml:space="preserve">
+ <value>Executing a command</value>
+ </data>
+ <data name="WaitForCommandExecution" xml:space="preserve">
+ <value>Please wait for command execution to finish...</value>
+ </data>
+</root> \ No newline at end of file
diff --git a/src/Text/Impl/Commanding/DefaultBufferResolver.cs b/src/Text/Impl/Commanding/DefaultBufferResolver.cs
new file mode 100644
index 0000000..71c93d8
--- /dev/null
+++ b/src/Text/Impl/Commanding/DefaultBufferResolver.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Editor.Commanding;
+using Microsoft.VisualStudio.Utilities;
+
+namespace Microsoft.VisualStudio.UI.Text.Commanding.Implementation
+{
+ [Export(typeof(ICommandingTextBufferResolverProvider))]
+ [ContentType("any")]
+ internal class DefaultBufferResolverProvider : ICommandingTextBufferResolverProvider
+ {
+ public ICommandingTextBufferResolver CreateResolver(ITextView textView)
+ {
+ return new DefaultBufferResolver(textView);
+ }
+ }
+
+ internal class DefaultBufferResolver : ICommandingTextBufferResolver
+ {
+ private readonly ITextView _textView;
+
+ public DefaultBufferResolver(ITextView textView)
+ {
+ _textView = textView ?? throw new ArgumentNullException(nameof(textView));
+ }
+
+ public IEnumerable<ITextBuffer> ResolveBuffersForCommand<TArgs>() where TArgs : EditorCommandArgs
+ {
+ var mappingPoint = _textView.BufferGraph.CreateMappingPoint(_textView.Caret.Position.BufferPosition, PointTrackingMode.Negative);
+ return _textView.BufferGraph.GetTextBuffers((b) => mappingPoint.GetPoint(b, PositionAffinity.Successor) != null);
+ }
+ }
+}
diff --git a/src/Text/Impl/Commanding/EditorCommandHandlerService.cs b/src/Text/Impl/Commanding/EditorCommandHandlerService.cs
new file mode 100644
index 0000000..3bd08c2
--- /dev/null
+++ b/src/Text/Impl/Commanding/EditorCommandHandlerService.cs
@@ -0,0 +1,373 @@
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using Microsoft.VisualStudio.Utilities;
+using Microsoft.VisualStudio.Commanding;
+using Microsoft.VisualStudio.Text.Utilities;
+using Microsoft.VisualStudio.Text.Editor.Commanding;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Threading;
+using ICommandHandlerAndMetadata = System.Lazy<Microsoft.VisualStudio.Commanding.ICommandHandler, Microsoft.VisualStudio.UI.Text.Commanding.Implementation.ICommandHandlerMetadata>;
+
+namespace Microsoft.VisualStudio.UI.Text.Commanding.Implementation
+{
+ internal class EditorCommandHandlerService : IEditorCommandHandlerService
+ {
+ private readonly IEnumerable<ICommandHandlerAndMetadata> _commandHandlers;
+ private readonly IUIThreadOperationExecutor _uiThreadOperationExecutor;
+ private readonly JoinableTaskContext _joinableTaskContext;
+ private readonly ITextView _textView;
+ private readonly IComparer<IEnumerable<string>> _contentTypesComparer;
+ private readonly ICommandingTextBufferResolver _bufferResolver;
+ private readonly IGuardedOperations _guardedOperations;
+
+ private readonly static IReadOnlyList<ICommandHandlerAndMetadata> EmptyHandlerList = new List<ICommandHandlerAndMetadata>(0);
+ private readonly static Action EmptyAction = delegate { };
+ private readonly static Func<CommandState> UnavalableCommandFunc = new Func<CommandState>(() => CommandState.Unavailable);
+
+ /// This dictionary acts as a cache so we can avoid having to look through the full list of
+ /// handlers every time we need handlers of a specific type, for a given content type.
+ private readonly Dictionary<(Type commandArgType, IContentType contentType), IReadOnlyList<ICommandHandlerAndMetadata>> _commandHandlersByTypeAndContentType;
+
+ public EditorCommandHandlerService(ITextView textView,
+ IEnumerable<ICommandHandlerAndMetadata> commandHandlers,
+ IUIThreadOperationExecutor uiThreadOperationExecutor, JoinableTaskContext joinableTaskContext,
+ IComparer<IEnumerable<string>> contentTypesComparer,
+ ICommandingTextBufferResolver bufferResolver,
+ IGuardedOperations guardedOperations)
+ {
+ _commandHandlers = commandHandlers ?? throw new ArgumentNullException(nameof(commandHandlers));
+ _textView = textView ?? throw new ArgumentNullException(nameof(textView));
+ _uiThreadOperationExecutor = uiThreadOperationExecutor ?? throw new ArgumentNullException(nameof(uiThreadOperationExecutor));
+ _joinableTaskContext = joinableTaskContext ?? throw new ArgumentNullException(nameof(joinableTaskContext));
+ _contentTypesComparer = contentTypesComparer ?? throw new ArgumentNullException(nameof(contentTypesComparer));
+ _commandHandlersByTypeAndContentType = new Dictionary<(Type commandArgType, IContentType contentType), IReadOnlyList<ICommandHandlerAndMetadata>>();
+ _bufferResolver = bufferResolver ?? throw new ArgumentNullException(nameof(bufferResolver));
+ _guardedOperations = guardedOperations ?? throw new ArgumentNullException(nameof(guardedOperations));
+ }
+
+ public CommandState GetCommandState<T>(Func<ITextView, ITextBuffer, T> argsFactory, Func<CommandState> nextCommandHandler) where T : EditorCommandArgs
+ {
+ if (!_joinableTaskContext.IsOnMainThread)
+ {
+ throw new InvalidOperationException($"{nameof(IEditorCommandHandlerService.GetCommandState)} method shoudl only be called on the UI thread.");
+ }
+
+ // In Razor scenario it's possible that EditorCommandHandlerService is called re-entrantly,
+ // first by contained language command filter and then by editor command chain.
+ // To preserve Razor commanding semantics, only execute handlers once.
+ if (IsReentrantCall())
+ {
+ return nextCommandHandler?.Invoke() ?? CommandState.Unavailable;
+ }
+
+ using (var reentrancyGuard = new ReentrancyGuard(_textView))
+ {
+ // Build up chain of handlers per buffer
+ Func<CommandState> handlerChain = nextCommandHandler ?? UnavalableCommandFunc;
+ foreach (var bufferAndHandler in GetOrderedBuffersAndCommandHandlers<T>().Reverse())
+ {
+ T args = null;
+ // Declare locals to ensure that we don't end up capturing the wrong thing
+ var nextHandler = handlerChain;
+ var handler = bufferAndHandler.handler;
+ args = args ?? (args = argsFactory(_textView, bufferAndHandler.buffer));
+ if (args == null)
+ {
+ // Args factory failed, skip command handlers and just call next
+ return handlerChain();
+ }
+
+ handlerChain = () => handler.GetCommandState(args, nextHandler);
+ }
+
+ // Kick off the first command handler
+ return handlerChain();
+ }
+ }
+
+ public void Execute<T>(Func<ITextView, ITextBuffer, T> argsFactory, Action nextCommandHandler) where T : EditorCommandArgs
+ {
+ if (!_joinableTaskContext.IsOnMainThread)
+ {
+ throw new InvalidOperationException($"{nameof(IEditorCommandHandlerService.Execute)} method shoudl only be called on the UI thread.");
+ }
+
+ // In Razor scenario it's possible that EditorCommandHandlerService is called re-entrantly,
+ // first by contained language command filter and then by editor command chain.
+ // To preserve Razor commanding semantics, only execute handlers once.
+ if (IsReentrantCall())
+ {
+ nextCommandHandler?.Invoke();
+ return;
+ }
+
+ using (var reentrancyGuard = new ReentrancyGuard(_textView))
+ {
+ CommandExecutionContext commandExecutionContext = null;
+
+ // Build up chain of handlers per buffer
+ Action handlerChain = nextCommandHandler ?? EmptyAction;
+ // TODO: realize the chain dynamically and without Reverse()
+ foreach (var bufferAndHandler in GetOrderedBuffersAndCommandHandlers<T>().Reverse())
+ {
+ T args = null;
+ // Declare locals to ensure that we don't end up capturing the wrong thing
+ var nextHandler = handlerChain;
+ var handler = bufferAndHandler.handler;
+ args = args ?? (args = argsFactory(_textView, bufferAndHandler.buffer));
+ if (args == null)
+ {
+ // Args factory failed, skip command handlers and just call next
+ handlerChain();
+ }
+
+ if (commandExecutionContext == null)
+ {
+ commandExecutionContext = CreateCommandExecutionContext();
+ }
+
+ handlerChain = () => handler.ExecuteCommand(args, nextHandler, commandExecutionContext);
+ }
+
+ ExecuteCommandHandlerChain(commandExecutionContext, handlerChain, nextCommandHandler);
+ }
+ }
+
+ private void ExecuteCommandHandlerChain(CommandExecutionContext commandExecutionContext,
+ Action handlerChain, Action nextCommandHandler)
+ {
+ try
+ {
+ // Kick off the first command handler.
+ handlerChain();
+ }
+ catch (OperationCanceledException)
+ {
+ nextCommandHandler?.Invoke();
+ }
+ catch (AggregateException aggregate) when (aggregate.InnerExceptions.All(e => e is OperationCanceledException))
+ {
+ nextCommandHandler?.Invoke();
+ }
+ finally
+ {
+ commandExecutionContext?.WaitContext?.Dispose();
+ }
+ }
+
+ private class ReentrancyGuard : IDisposable
+ {
+ private readonly IPropertyOwner _owner;
+
+ public ReentrancyGuard(IPropertyOwner owner)
+ {
+ _owner = owner ?? throw new ArgumentNullException(nameof(owner));
+ _owner.Properties[typeof(ReentrancyGuard)] = this;
+ }
+
+ public void Dispose()
+ {
+ _owner.Properties.RemoveProperty(typeof(ReentrancyGuard));
+ }
+ }
+
+ private bool IsReentrantCall()
+ {
+ return _textView.Properties.ContainsProperty(typeof(ReentrancyGuard));
+ }
+
+ private CommandExecutionContext CreateCommandExecutionContext()
+ {
+ CommandExecutionContext commandExecutionContext;
+ var uiThreadOperationContext = _uiThreadOperationExecutor.BeginExecute(CommandingStrings.ExecutingCommand,
+ CommandingStrings.WaitForCommandExecution, allowCancel: true, showProgress: true);
+ commandExecutionContext = new CommandExecutionContext(uiThreadOperationContext);
+ return commandExecutionContext;
+ }
+
+ //internal for unit tests
+ internal IEnumerable<(ITextBuffer buffer, ICommandHandler handler)> GetOrderedBuffersAndCommandHandlers<T>() where T : EditorCommandArgs
+ {
+ // This method creates an ordered sequence of (buffer, handler) pairs that define proper order of
+ // command handling that takes into account the buffer graph and command handlers matching buffers in the graph by
+ // content types.
+
+ // Currently this method discovers affected buffers based on caret mapping only.
+ // TODO: this should be an extensibility point as in some scenarios we might want to consider selection too for example.
+
+ // A general idea is that command handlers matching more specifically content type of buffers higher in the buffer
+ // graph should be executed before those matching buffers lower in the graph or less specific content types.
+
+ // So for example in a projection scenario (projection buffer containing C# buffer), 3 command handlers
+ // matching "projection", "CSharp" and "any" content types will be ordered like this:
+ // 1. command handler matching "projection" content type is executed on the projection buffer
+ // 2. command handler matching "CSharp" content type is executed on the C# buffer
+ // 3. command handler matching "any" content type is executed on the projection buffer
+
+ // The ordering algorithm is as follows:
+ // 1. Create an ordered list of all affected buffers in the buffer graph
+ // by mapping caret position down and up the buffer graph. In a typical projection scenario
+ // (projection buffer containing C# buffer) that will produce (projection buffer, C# buffer) sequence.
+ // 2. For each affected buffer get or create a bucket of matching command handlers,
+ // ordered by [Order] and content type specificity.
+ // 3. Pick best command handler in all buckets in terms of content type specificity (e.g.
+ // if one command handler can handle "text" content type, but another can
+ // handle "CSharp" content type, we pick the latter one:
+ // 3. Start with top command handler in first non empty bucket.
+ // 4. Compare it with top command handlers in all other buckets in terms of content type specificity.
+ // 5. yield return current handler or better one if found, pop it from its bucket
+ // 6. Repeat starting with #3 utill all buckets are empty.
+ // In the projection scenario that will result in the following
+ // list of (buffer, handler) pairs: (projection buffer, projection handler), (C# buffer, C# handler),
+ // (projection buffer, any handler).
+
+ IReadOnlyList<ITextBuffer> buffers = _bufferResolver.ResolveBuffersForCommand<T>().ToArray();
+ if (buffers == null || buffers.Count == 0)
+ {
+ yield break;
+ }
+
+ // An array of per-buffer buckets, each containing cached list of matching command handlers,
+ // ordered by [Order] and content type specificity
+ var handlerBuckets = new CommandHandlerBucket[buffers.Count];
+ for (int i = 0; i < buffers.Count; i++)
+ {
+ handlerBuckets[i] = new CommandHandlerBucket(GetOrCreateOrderedHandlers<T>(buffers[i].ContentType, _textView.Roles));
+ }
+
+ while (true)
+ {
+ ICommandHandlerAndMetadata currentHandler = null;
+ int currentHandlerBufferIndex = 0;
+
+ for (int i = 0; i < handlerBuckets.Length; i++)
+ {
+ if (!handlerBuckets[i].IsEmpty)
+ {
+ currentHandler = handlerBuckets[i].Peek();
+ currentHandlerBufferIndex = i;
+ break;
+ }
+ }
+
+ if (currentHandler == null)
+ {
+ // All buckets are empty, all done
+ break;
+ }
+
+ // Check if any other bucket has a better handler (i.e. can handle more specific content type).
+ var foundBetterHandler = false;
+ for (int i = 0; i < buffers.Count; i++)
+ {
+ // Search in other buckets only
+ if (i != currentHandlerBufferIndex)
+ {
+ if (!handlerBuckets[i].IsEmpty)
+ {
+ var handler = handlerBuckets[i].Peek();
+ // Can this handler handle content type more specific than top handler in firstNonEmptyBucket?
+ if (_contentTypesComparer.Compare(handler.Metadata.ContentTypes, currentHandler.Metadata.ContentTypes) < 0)
+ {
+ foundBetterHandler = true;
+ handlerBuckets[i].Pop();
+ yield return (buffers[i], handler.Value);
+ break;
+ }
+ }
+ }
+ }
+
+ if (!foundBetterHandler)
+ {
+ yield return (buffers[currentHandlerBufferIndex], currentHandler.Value);
+ handlerBuckets[currentHandlerBufferIndex].Pop();
+ }
+ }
+ }
+
+ private IReadOnlyList<ICommandHandlerAndMetadata> GetOrCreateOrderedHandlers<T>(IContentType contentType, ITextViewRoleSet textViewRoles) where T : EditorCommandArgs
+ {
+ var cacheKey = (commandArgsType: typeof(T), contentType: contentType);
+ if (!_commandHandlersByTypeAndContentType.TryGetValue(cacheKey, out var commandHandlerList))
+ {
+ IList<ICommandHandlerAndMetadata> newCommandHandlerList = null;
+ foreach (var lazyCommandHandler in SelectMatchingCommandHandlers(_commandHandlers, contentType, textViewRoles))
+ {
+ var commandHandler = _guardedOperations.InstantiateExtension<ICommandHandler>(this, lazyCommandHandler);
+ if (commandHandler is ICommandHandler<T> || commandHandler is IChainedCommandHandler<T>)
+ {
+ if (newCommandHandlerList == null)
+ {
+ newCommandHandlerList = new FrugalList<ICommandHandlerAndMetadata>();
+ }
+
+ newCommandHandlerList.Add(lazyCommandHandler);
+ }
+ }
+
+ if (newCommandHandlerList?.Count > 1)
+ {
+ // Order handlers by [Order] across content types, but preserve sort order otherwise
+ newCommandHandlerList = StableOrderer.Order(newCommandHandlerList).ToArray();
+ }
+
+ commandHandlerList = newCommandHandlerList?.ToArray() ?? EmptyHandlerList;
+ _commandHandlersByTypeAndContentType[cacheKey] = commandHandlerList;
+ }
+
+ return commandHandlerList;
+ }
+
+ /// <summary>
+ /// Selects matching command handlers without allocating a new list.
+ /// </summary>
+ private static IEnumerable<ICommandHandlerAndMetadata> SelectMatchingCommandHandlers(
+ IEnumerable<ICommandHandlerAndMetadata> commandHandlers,
+ IContentType contentType, ITextViewRoleSet textViewRoles)
+ {
+ foreach (var handler in commandHandlers)
+ {
+ if (MatchesContentType(handler.Metadata, contentType) &&
+ MatchesTextViewRoles(handler.Metadata, textViewRoles))
+ {
+ yield return handler;
+ }
+ }
+ }
+
+ private static bool MatchesContentType(ICommandHandlerMetadata handlerMetadata, IContentType contentType)
+ {
+ foreach (var handlerContentType in handlerMetadata.ContentTypes)
+ {
+ if (contentType.IsOfType(handlerContentType))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static bool MatchesTextViewRoles(ICommandHandlerMetadata handlerMetadata, ITextViewRoleSet roles)
+ {
+ // Text view roles are optional
+ if (handlerMetadata.TextViewRoles == null)
+ {
+ return true;
+ }
+
+ foreach (var handlerRole in handlerMetadata.TextViewRoles)
+ {
+ if (roles.Contains(handlerRole))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/src/Text/Impl/Commanding/EditorCommandHandlerServiceFactory.cs b/src/Text/Impl/Commanding/EditorCommandHandlerServiceFactory.cs
new file mode 100644
index 0000000..241b3b6
--- /dev/null
+++ b/src/Text/Impl/Commanding/EditorCommandHandlerServiceFactory.cs
@@ -0,0 +1,81 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.VisualStudio.Text.Editor;
+using System.ComponentModel.Composition;
+using Microsoft.VisualStudio.Text.Editor.Commanding;
+using Microsoft.VisualStudio.Commanding;
+using Microsoft.VisualStudio.Utilities;
+using Microsoft.VisualStudio.Threading;
+using System.Linq;
+using Microsoft.VisualStudio.Text;
+
+namespace Microsoft.VisualStudio.UI.Text.Commanding.Implementation
+{
+ [Export(typeof(IEditorCommandHandlerServiceFactory))]
+ internal class EditorCommandHandlerServiceFactory : IEditorCommandHandlerServiceFactory
+ {
+ private readonly IEnumerable<Lazy<ICommandHandler, ICommandHandlerMetadata>> _commandHandlers;
+ private readonly IList<Lazy<ICommandingTextBufferResolverProvider, IContentTypeMetadata>> _bufferResolverProviders;
+ private readonly IUIThreadOperationExecutor _uiThreadOperationExecutor;
+ private readonly JoinableTaskContext _joinableTaskContext;
+ private readonly IContentTypeRegistryService _contentTypeRegistryService;
+ private readonly IGuardedOperations _guardedOperations;
+ private readonly StableContentTypeComparer _contentTypeComparer;
+
+ [ImportingConstructor]
+ public EditorCommandHandlerServiceFactory(
+ [ImportMany]IEnumerable<Lazy<ICommandHandler, ICommandHandlerMetadata>> commandHandlers,
+ [ImportMany]IEnumerable<Lazy<ICommandingTextBufferResolverProvider, IContentTypeMetadata>> bufferResolvers,
+ IUIThreadOperationExecutor uiThreadOperationExecutor,
+ JoinableTaskContext joinableTaskContext,
+ IContentTypeRegistryService contentTypeRegistryService,
+ IGuardedOperations guardedOperations)
+ {
+ _uiThreadOperationExecutor = uiThreadOperationExecutor;
+ _joinableTaskContext = joinableTaskContext;
+ _guardedOperations = guardedOperations;
+ _contentTypeRegistryService = contentTypeRegistryService;
+ _contentTypeComparer = new StableContentTypeComparer(_contentTypeRegistryService);
+ _commandHandlers = OrderCommandHandlers(commandHandlers);
+ if (!bufferResolvers.Any())
+ {
+ throw new ImportCardinalityMismatchException($"Expected to import at least one {typeof(ICommandingTextBufferResolver).Name}");
+ }
+
+ _bufferResolverProviders = bufferResolvers.ToList();
+ }
+
+ public IEditorCommandHandlerService GetService(ITextView textView)
+ {
+ return textView.Properties.GetOrCreateSingletonProperty(() =>
+ {
+ var bufferResolverProvider = _guardedOperations.InvokeBestMatchingFactory(_bufferResolverProviders, textView.TextBuffer.ContentType, _contentTypeRegistryService, errorSource: this);
+ ICommandingTextBufferResolver bufferResolver = null;
+ _guardedOperations.CallExtensionPoint(() => bufferResolver = bufferResolverProvider.CreateResolver(textView));
+ bufferResolver = bufferResolver ?? new DefaultBufferResolver(textView);
+ return new EditorCommandHandlerService(textView, _commandHandlers, _uiThreadOperationExecutor, _joinableTaskContext,
+ _contentTypeComparer, bufferResolver, _guardedOperations);
+ });
+ }
+
+ public IEditorCommandHandlerService GetService(ITextView textView, ITextBuffer subjectBuffer)
+ {
+ if (subjectBuffer == null)
+ {
+ return GetService(textView);
+ }
+
+ return subjectBuffer.Properties.GetOrCreateSingletonProperty(() =>
+ {
+ return new EditorCommandHandlerService(textView, _commandHandlers, _uiThreadOperationExecutor,
+ _joinableTaskContext, _contentTypeComparer,
+ new SingleBufferResolver(subjectBuffer), _guardedOperations);
+ });
+ }
+
+ private IEnumerable<Lazy<ICommandHandler, ICommandHandlerMetadata>> OrderCommandHandlers(IEnumerable<Lazy<ICommandHandler, ICommandHandlerMetadata>> commandHandlers)
+ {
+ return commandHandlers.OrderBy((handler) => handler.Metadata.ContentTypes, _contentTypeComparer);
+ }
+ }
+}
diff --git a/src/Text/Impl/Commanding/ICommandHandlerMetadata.cs b/src/Text/Impl/Commanding/ICommandHandlerMetadata.cs
new file mode 100644
index 0000000..49af645
--- /dev/null
+++ b/src/Text/Impl/Commanding/ICommandHandlerMetadata.cs
@@ -0,0 +1,13 @@
+using System.Collections.Generic;
+using System.ComponentModel;
+using Microsoft.VisualStudio.Utilities;
+
+namespace Microsoft.VisualStudio.UI.Text.Commanding.Implementation
+{
+ public interface ICommandHandlerMetadata : IOrderable, IContentTypeMetadata
+ {
+ [DefaultValue(null)] // [TextViewRole] is optional
+ IEnumerable<string> TextViewRoles { get; }
+ }
+
+}
diff --git a/src/Text/Impl/Commanding/SingleBufferResolver.cs b/src/Text/Impl/Commanding/SingleBufferResolver.cs
new file mode 100644
index 0000000..6e7a20d
--- /dev/null
+++ b/src/Text/Impl/Commanding/SingleBufferResolver.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Editor.Commanding;
+
+namespace Microsoft.VisualStudio.UI.Text.Commanding.Implementation
+{
+ internal class SingleBufferResolver : ICommandingTextBufferResolver
+ {
+ private readonly ITextBuffer[] _textBuffer;
+
+ public SingleBufferResolver(ITextBuffer textBuffer)
+ {
+ if (textBuffer == null)
+ {
+ throw new ArgumentNullException(nameof(textBuffer));
+ }
+
+ _textBuffer = new ITextBuffer[] { textBuffer };
+ }
+
+ public IEnumerable<ITextBuffer> ResolveBuffersForCommand<TArgs>() where TArgs : EditorCommandArgs
+ {
+ return _textBuffer;
+ }
+ }
+}
diff --git a/src/Text/Util/TextDataUtil/StableContentTypeComparer.cs b/src/Text/Util/TextDataUtil/StableContentTypeComparer.cs
new file mode 100644
index 0000000..19ed88d
--- /dev/null
+++ b/src/Text/Util/TextDataUtil/StableContentTypeComparer.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Microsoft.VisualStudio.Utilities
+{
+ /// <summary>
+ /// Custom comparer for sorting lists of content types that preserves original order of unrelated content types.
+ /// </summary>
+ internal class StableContentTypeComparer : IComparer<IEnumerable<string>>
+ {
+ private readonly IContentTypeRegistryService _contentTypeRegistryService;
+
+ public StableContentTypeComparer(IContentTypeRegistryService contentTypeRegistryService)
+ {
+ _contentTypeRegistryService = contentTypeRegistryService ?? throw new ArgumentNullException(nameof(contentTypeRegistryService));
+ }
+
+ public int Compare(IEnumerable<string> x, IEnumerable<string> y)
+ {
+ if (x.SequenceEqual(y))
+ {
+ return 0;
+ }
+
+ foreach (var contentTypeXStr in x)
+ {
+ var contentTypeX = _contentTypeRegistryService.GetContentType(contentTypeXStr);
+ if (contentTypeX != null)
+ {
+ foreach (var contentTypeYStr in y)
+ {
+ if (contentTypeX.IsOfType(contentTypeYStr))
+ {
+ return -1;
+ }
+ }
+ }
+ }
+
+ foreach (var contentTypeYStr in y)
+ {
+ var contentTypeY = _contentTypeRegistryService.GetContentType(contentTypeYStr);
+ if (contentTypeY != null)
+ {
+ foreach (var contentTypeXStr in x)
+ {
+ if (contentTypeY.IsOfType(contentTypeXStr))
+ {
+ return 1;
+ }
+ }
+ }
+ }
+
+ // Content types are unrelated, there is no way resolve the tie so
+ // let's consider them equal to preserve original order.
+ return 0;
+ }
+ }
+}
diff --git a/src/Text/Util/TextDataUtil/StableOrderer.cs b/src/Text/Util/TextDataUtil/StableOrderer.cs
new file mode 100644
index 0000000..ae9ee8f
--- /dev/null
+++ b/src/Text/Util/TextDataUtil/StableOrderer.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Microsoft.VisualStudio.Utilities
+{
+ internal static class StableOrderer
+ {
+ private static bool OrderDependencyFunction<TValue, TMetadata>(Lazy<TValue, TMetadata> x,
+ Lazy<TValue, TMetadata> y)
+ where TValue : class
+ where TMetadata : IOrderable
+ {
+ if (y.Metadata.Before?.Contains(x.Metadata.Name) == true)
+ {
+ return true;
+ }
+
+ if (x.Metadata.After?.Contains(y.Metadata.Name) == true)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ public static IEnumerable<Lazy<TValue, TMetadata>> Order<TValue, TMetadata>(IEnumerable<Lazy<TValue, TMetadata>> itemsToOrder)
+ where TValue : class
+ where TMetadata : IOrderable
+ {
+ return StableTopologicalSort.Order(itemsToOrder, OrderDependencyFunction);
+ }
+ }
+}
diff --git a/src/Text/Util/TextDataUtil/StableTopologicalSort.cs b/src/Text/Util/TextDataUtil/StableTopologicalSort.cs
new file mode 100644
index 0000000..04703d1
--- /dev/null
+++ b/src/Text/Util/TextDataUtil/StableTopologicalSort.cs
@@ -0,0 +1,198 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.VisualStudio.Text.Utilities;
+
+namespace Microsoft.VisualStudio.Utilities
+{
+ /// <summary>
+ /// Orders elements of a linearized directed graph in a dependency order, while preserving the original order
+ /// between elements with no dependencies.
+ /// </summary>
+ /// <remarks>This is an implementation of the Gapotchenko's stable topological sort algorithm,
+ /// https://blog.gapotchenko.com/stable-topological-sort.
+ /// </remarks>
+ internal static class StableTopologicalSort
+ {
+ /// <summary>
+ /// An element dependency function.
+ /// </summary>
+ /// <returns>
+ /// <c>true</c> if x depends on y, <c>false</c> otherwise.
+ /// </returns>
+ public delegate bool TopologicalDependencyFunction<in T>(T x, T y);
+
+ /// <summary>
+ /// Orders elements of a linearized directed graph in a dependency order, while preserving the original order
+ /// between elements with no dependencies.
+ /// </summary>
+ public static IEnumerable<T> Order<T>(
+ IEnumerable<T> itemsToOrder,
+ TopologicalDependencyFunction<T> dependencyFunction)
+ {
+ if (itemsToOrder == null)
+ throw new ArgumentNullException(nameof(itemsToOrder));
+ if (dependencyFunction == null)
+ throw new ArgumentNullException(nameof(dependencyFunction));
+
+ var itemsToOrderList = itemsToOrder.ToList();
+ if (itemsToOrderList.Count < 2)
+ {
+ return itemsToOrder;
+ }
+
+ int itemsToOrderCount = itemsToOrderList.Count;
+
+ var graph = DependencyGraph<T>.TryCreate(itemsToOrderList, dependencyFunction, EqualityComparer<T>.Default);
+ if (graph == null)
+ {
+ return itemsToOrder;
+ }
+
+ Restart:
+ for (int i = 0; i < itemsToOrderCount; ++i)
+ {
+ for (int j = 0; j < i; ++j)
+ {
+ if (graph.DoesXHaveDirectDependencyOnY(itemsToOrderList[j], itemsToOrderList[i]))
+ {
+ bool jDependsOnI = graph.DoesXHaveTransientDependencyOnY(itemsToOrderList[j], itemsToOrderList[i]);
+ bool iDependsOnJ = graph.DoesXHaveTransientDependencyOnY(itemsToOrderList[i], itemsToOrderList[j]);
+
+ bool circularDependency = jDependsOnI && iDependsOnJ;
+
+ if (!circularDependency)
+ {
+ var t = itemsToOrderList[i];
+ itemsToOrderList.RemoveAt(i);
+
+ itemsToOrderList.Insert(j, t);
+ goto Restart;
+ }
+ }
+ }
+ }
+
+ return itemsToOrderList;
+ }
+
+ private class DependencyGraph<T>
+ {
+ private IEqualityComparer<T> EqualityComparer { get; }
+
+ public IDictionary<T, Node> Nodes { get; }
+
+ public DependencyGraph(IEqualityComparer<T> equalityComparer, int n)
+ {
+ EqualityComparer = equalityComparer ?? throw new ArgumentNullException(nameof(equalityComparer));
+ this.Nodes = new Dictionary<T, Node>(n, equalityComparer);
+ }
+
+ public class Node
+ {
+ private IList<T> _children = new FrugalList<T>();
+
+ public IList<T> Children => _children ?? (_children = new FrugalList<T>());
+ }
+
+ public static DependencyGraph<T> TryCreate(
+ IList<T> items,
+ TopologicalDependencyFunction<T> dependencyFunction,
+ IEqualityComparer<T> equalityComparer)
+ {
+ var graph = new DependencyGraph<T>(equalityComparer, items.Count);
+
+ bool hasDependencies = false;
+
+ for (int position = 0; position < items.Count; ++position)
+ {
+ var element = items[position];
+
+ if (!graph.Nodes.TryGetValue(element, out Node node))
+ {
+ node = new Node();
+ graph.Nodes.Add(element, node);
+ }
+
+ foreach (var anotherElement in items)
+ {
+ if (equalityComparer.Equals(element, anotherElement))
+ {
+ continue;
+ }
+
+ if (dependencyFunction(element, anotherElement))
+ {
+ node.Children.Add(anotherElement);
+ hasDependencies = true;
+ }
+ }
+ }
+
+ if (!hasDependencies)
+ {
+ return null;
+ }
+
+ return graph;
+ }
+
+ public bool DoesXHaveDirectDependencyOnY(T x, T y)
+ {
+ if (Nodes.TryGetValue(x, out Node node))
+ {
+ if (node.Children.Contains(y, EqualityComparer))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private class DependencyWalker
+ {
+ private readonly DependencyGraph<T> _graph;
+ private readonly HashSet<T> _visitedNodes;
+
+ public DependencyWalker(DependencyGraph<T> graph)
+ {
+ _graph = graph;
+ _visitedNodes = new HashSet<T>(graph.EqualityComparer);
+ }
+
+ public bool DoesXHaveTransientDependencyOnY(T x, T y)
+ {
+ if (!_visitedNodes.Add(x))
+ {
+ return false;
+ }
+
+ if (_graph.Nodes.TryGetValue(x, out Node node))
+ {
+ if (node.Children.Contains(y, _graph.EqualityComparer))
+ {
+ return true;
+ }
+
+ foreach (var i in node.Children)
+ {
+ if (DoesXHaveTransientDependencyOnY(i, y))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+ }
+
+ public bool DoesXHaveTransientDependencyOnY(T x, T y)
+ {
+ var traverser = new DependencyWalker(this);
+ return traverser.DoesXHaveTransientDependencyOnY(x, y);
+ }
+ }
+ }
+}
diff --git a/src/Text/Util/TextUIUtil/DefaultUIThreadOperationExecutor.cs b/src/Text/Util/TextUIUtil/DefaultUIThreadOperationExecutor.cs
new file mode 100644
index 0000000..e022a1c
--- /dev/null
+++ b/src/Text/Util/TextUIUtil/DefaultUIThreadOperationExecutor.cs
@@ -0,0 +1,30 @@
+using System;
+using Microsoft.VisualStudio.Utilities;
+
+namespace Microsoft.VisualStudio.UI.Text.Commanding.Implementation
+{
+ [ExportImplementation(typeof(IUIThreadOperationExecutor))]
+ [Name("default")]
+ internal class DefaultUIThreadOperationExecutor : IUIThreadOperationExecutor
+ {
+ public IUIThreadOperationContext BeginExecute(string title, string description, bool allowCancel, bool showProgress)
+ {
+ return new DefaultUIThreadOperationContext(allowCancel, description);
+ }
+
+ public UIThreadOperationStatus Execute(string title, string description, bool allowCancel, bool showProgress, Action<IUIThreadOperationContext> action)
+ {
+ var context = new DefaultUIThreadOperationContext(allowCancel, description);
+ action(context);
+ return UIThreadOperationStatus.Completed;
+ }
+ }
+
+ internal class DefaultUIThreadOperationContext : AbstractUIThreadOperationContext
+ {
+ public DefaultUIThreadOperationContext(bool allowCancellation, string description)
+ : base(allowCancellation, description)
+ {
+ }
+ }
+}
diff --git a/src/Text/Util/TextUIUtil/UIThreadOperationExecutor.cs b/src/Text/Util/TextUIUtil/UIThreadOperationExecutor.cs
new file mode 100644
index 0000000..e1a92bf
--- /dev/null
+++ b/src/Text/Util/TextUIUtil/UIThreadOperationExecutor.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using Microsoft.VisualStudio.Utilities;
+
+namespace Microsoft.VisualStudio.UI.Text.Commanding.Implementation
+{
+ [Export(typeof(IUIThreadOperationExecutor))]
+ internal class UIThreadOperationExecutor : IUIThreadOperationExecutor
+ {
+ [ImportImplementations(typeof(IUIThreadOperationExecutor))]
+ private IEnumerable<Lazy<IUIThreadOperationExecutor, IOrderable>> _unorderedImplementations;
+
+ private IUIThreadOperationExecutor _bestImpl;
+
+ private IUIThreadOperationExecutor BestImplementation
+ {
+ get
+ {
+ if (_bestImpl == null)
+ {
+ var orderedImpls = Orderer.Order(_unorderedImplementations);
+ if (orderedImpls.Count == 0)
+ {
+ throw new ImportCardinalityMismatchException($"Expected to import at least one export of {typeof(IUIThreadOperationExecutor).FullName}, but got none.");
+ }
+
+ _bestImpl = orderedImpls[0].Value;
+ }
+
+ return _bestImpl;
+ }
+ }
+
+ public IUIThreadOperationContext BeginExecute(string title, string description, bool allowCancel, bool showProgress)
+ {
+ return BestImplementation.BeginExecute(title, description, allowCancel, showProgress);
+ }
+
+ public UIThreadOperationStatus Execute(string title, string description, bool allowCancel, bool showProgress, Action<IUIThreadOperationContext> action)
+ {
+ return BestImplementation.Execute(title, description, allowCancel, showProgress, action);
+ }
+ }
+}