Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Conversation

@grokys
Copy link
Contributor

@grokys grokys commented Feb 22, 2018

Previously in Commands

We previously had two separate command architectures

GitHub.VisualStudio.Menus

These classes had some problems:

  1. Defined in GitHub.VisualStudio so couldn't be used in lower-level assemblies
  2. No support for command parameters
  3. The serviceProvider.AddCommandHandler extension method did some sync service requests making it non-ideal for use in AsyncPackages
  4. Can't be bound to UI elements

GitHub.InlineReviews.Commands

These classes were written because of the problems with the prior framework (most notably point 1) but had their own problems:

  1. Were defined in GitHub.InlineReviews so couldn't be used in lower-level assemblies (I'd done that with the intention of doing this consolidation at some point)
  2. Made calls to the Package for services
  3. Used a "magic" MEF registration that could cause all MEF assemblies to be loaded when not needed

This PR

This PR consolidates the two frameworks and attempts to solve the problems of the two. There are the same VsCommand and VsCommand<T> classes as in the GitHub.InlineReviews.Commands namespace, but:

  1. They don't use "magic" package registration
  2. Instead I've added some extension methods to IMenuCommandService to register them and removed the AddCommandHandler extension methods that used sync GetService calls
  3. They implement ICommand so they can be bound in the UI
  4. They inherit directly from OleCommand rather than wrapping an OleCommand so they can be used wherever an OleCommand is accepted
  5. I also added an extension method IMenuCommandService.BindCommand to bind an existing ICommand (such as a ReactiveCommand) to a VS command. This is used in the GitHubPaneViewModel

In addition, I've added a new assembly: GitHub.Services.Vssdk for the implementations. We've been talking about this for a while, and it's really needed here. I originally called it GitHub.Services.VisualStudio but decided to change it to Vssdk because we already use VisualStudio in GitHub.VisualStudio and thought it was clearer that it means "abstractions over the VSSDK"

Fixes #1489

- Moved `VsCommand` etc out of `GitHub.InlineReviews` and into a new assembly `GitHub.Services.Vssdk` which will be a home for services which have a hard VSSDK dependency.
- Moved the command interfaces into `GitHub.Exports`
- Made the `VsCommand` classes inherit from `OleMenuCommand`
- Removed the automatic package registration stuff
And use it in the `GitHubPaneViewModel`.
And remove old menu framework-related code.
As it causes Rx to get loaded.
Thought I'd need it, turns out I didn't.
usageTracker.IncrementCounter(x => x.NumberOfGitHubPaneHelpClicks).Forget();
});
var menuService = (IMenuCommandService)paneServiceProvider.GetService(typeof(IMenuCommandService));
BindNavigatorCommand(menuService, PkgCmdIDList.pullRequestCommand, showPullRequests);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason you're not using the BindCommand extension method?

Copy link
Contributor Author

@grokys grokys Mar 1, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BindNavigatorCommand does use BindCommand. It's just shorthand so we don't have to specify new CommandID(Guids.guidGitHubToolbarCmdSet, PkgCmdIDList.foo) for every command and can just pass the PkgCmdIDList.foo part`.

protected override void QueryStatus()
{
Log.Assert(SelectedTextProvider != null, "Could not get an instance of ISelectedTextProvider");
Visible = !string.IsNullOrWhiteSpace(SelectedTextProvider?.GetSelectedText());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated to this PR, but just while I remember. We should default to selecting all text. I bet most people don't even know this command exists! ;-)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added an issue here: #1534

protected CreateGistCommand(IGitHubServiceProvider serviceProvider)
: base(CommandSet, CommandId)
{
dialogService = new Lazy<IDialogService>(() => serviceProvider.TryGetService<IDialogService>());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious why we're not passing Lazy<IDialogService> (and others) into the importing constructor?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question: basically this is how we were doing it before. I'll try using Lazy<>.

Copy link
Collaborator

@jcansdale jcansdale left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we be loading InlineReviewsPackage on a background thread?

[PackageRegistration(UseManagedResourcesOnly = true)]
[PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
[Guid(Guids.InlineReviewsPackageId)]
[ProvideAutoLoad(UIContextGuids80.SolutionExists)]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will cause the package to auto-load on the Main thread. I think we should be using this:

    [ProvideAutoLoad(UIContextGuids80.SolutionExists, PackageAutoLoadFlags.BackgroundLoad)]

In future we might want to only auto-load when there is an active Git repository (see #1512)?

    [ProvideAutoLoad(Guids.UIContext_Git, PackageAutoLoadFlags.BackgroundLoad)]

{
base.Initialize();
PackageResources.Register(this);
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should be on the Main thread.

@jcansdale jcansdale force-pushed the refactor/vs-commands branch from 4fecb2c to d4daa1e Compare March 14, 2018 10:07
@jcansdale
Copy link
Collaborator

I've made some auto-load related tweaks to the packages that install commands (InlineReviewsPackage and GitHubPackage).

Rather than using await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync() (which can hang in certain circumstances), change package registration to AllowsBackgroundLoading = false. This is the default and will cause the package to initialize on the Main thread. It is then okay to install commands without calling SwitchToMainThreadAsync.

I've changed InlineReviewsPackage to use: [ProvideAutoLoad(Guids.UIContext_Git)]. This will cause it to only load when we're in the context of a Git repo.

There is also some merge related cleanup.

Be explicit about which code needs the Main thread
Don't auto-load in background to prevent potential SwitchToMainThreadAsync hang.

SwitchToMainThreadAsync can hang so use ThreadingHelper.MainThreadDispatcher.InvokeAsync instead.
An old issue crept back in during merge.
@jcansdale jcansdale force-pushed the refactor/vs-commands branch from 9cd28b2 to 1baa921 Compare March 14, 2018 14:51
@jcansdale
Copy link
Collaborator

Forget what I said above about using AllowsBackgroundLoading = false. I've changed it back to using the following:

[PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
[ProvideAutoLoad(Guids.UIContext_Git, PackageAutoLoadFlags.BackgroundLoad)]

I'm using the same workaround as in PullRequestStatusBarPackage to avoid using SwitchToMainThreadAsync. By using await ThreadingHelper.MainThreadDispatcher.InvokeAsync we avoid the potential hang.

@jcansdale jcansdale merged commit f8b3e9d into master Mar 14, 2018
@jcansdale jcansdale deleted the refactor/vs-commands branch March 14, 2018 15:37
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants