gitextensions / gitextensions.extensibility

Interfaces and type definitions required for building Git Extensions plugins.
MIT License
4 stars 2 forks source link

Additional entry points for new plugins #6

Open mast-eu opened 5 years ago

mast-eu commented 5 years ago

I want to start a discussion about new plugin entry points, to see which ones are of common interest and feasible.

Currently the entry points for plugins are:

  1. Execute(): Runs when a plugin's name in the Plugins menu is clicked. This usually shows a dialog for further interactions.
  2. Register(): Runs every time when a repo is loaded.
  3. Unregister(): Runs when a repo is unloaded, either by switching to another repo or by closing GE.

These work well for the existing plugins, but IMHO can be a limitation for new ones. Thus, some ideas for further, optional entry points:

  1. Allow plugins to add second level menu items under Plugins > plugin_name, to run different actions without the need for an own dialog.
  2. Perform actions directly from the dashboard, before a repo is opened. Maybe in the form of using the Plugins menu there as well, but passing an additional argument to the plugin to distinguish whether the call comes from the main form or from the dashboard.
  3. A plugin-specific toolbox in the main toolbar.
  4. From the revision graph context menu, to perform actions on the selected commit.
  5. From the left panel, to perform actions on the selected repo / branch / tag.

All of these should be optional. Each plugin should declare which ones (if any) it implements. This way, backwards compatibility is ensured and the existing plugins continue to work as before.

gerhardol commented 5 years ago

Please coordinate the thinking with moving out plugins from the main application, like in #6415 and #5574

RussKie commented 5 years ago

I created a new project to track all issues related to the topic.

mast-eu commented 5 years ago

@RussKie: Maybe it's just me, but I do not find any project. Where exactly is it?

gerhardol commented 5 years ago

I do not find any project

https://github.com/orgs/gitextensions/projects/3#card-20603187

pmiossec commented 5 years ago

I don't know if it's on purpose but that's not a public project... (page 404)

gerhardol commented 5 years ago

I don't know if it's on purpose but that's not a public project... (page 404)

@RussKie can change

RussKie commented 5 years ago

Please try again

pmiossec commented 5 years ago

That's Ok. 👍

RussKie commented 5 years ago

Few extra API good to have:

NB: names/signatures are open to discussion

maraf commented 5 years ago

Few extra API good to have:

  • HasUI: bool to indicate whether a plugin should have a menu (e.g. JIRA integration plugin doesn't have a UI)
  • IsConfigurable: bool to indicate whether a plugin can be configured by a user

    • LoadSettings: ? - to load settings to present in a settings UI
    • SaveSettings: ? - to save settings

NB: names/signatures are open to discussion

What about implement this by querying interfaces? Like if Plugin class implements interface IPluginsMenuItemProvider or ISettingsContainer..

Or go even further and use more MEF exports, like VS?

RussKie commented 5 years ago

I am not that familiar with MEF, but that may as well work. Will probably be nicer to deal with too.

maraf commented 5 years ago

Hypothetical example: Support for extending GE main menu.

Contract:

public interface IMainMenuFactory
{
    ToolStripMenuItem Create(Context context);
}

Plugin implementation:

[Export(typeof(IMainMenuFactory))]
[Order(After = GitExtensions.MainMenuDefaultItems.Commands)]
public class BundleBackuperMainMenuFactory : IMainMenuFactory
{
    public ToolStripMenuItem Create(Context context)
    {
        var item = new ToolStripMenuItem("Bundles", OnClick);
        //...
        return item;
    }
}

Consument (somewhere deeply inside FormBrowse.cs):

class FormBrowse
{
    //...

    [ImportMany(typeof(IMainMenuFactory))
    public IEnumerable<Lazy<IMainMenuFactory, OrderMetadata>> MainMenuFactories { get; set; }

    //...

    private void InitializeMainMenu()
    {
        InitializeDefaultMainMenu();

        var context = new Context();
        var factories = MainMenuFactories);
        foreach (var factory in MainMenuFactories)
        {
            var item = factory.Value.Create(context);
            var index = OrderMetadataSorder.GetPosition(factory.Metadata);
            mainMenu.Items.Insert(index, item);
        }
    }

    //...
}

(These snippets possibly won't compile)

RussKie commented 5 years ago

I'm a bit hesitant allowing plugins create top level menus, this may get unruly - what if a user installed a dozen of plugins?

I think all plugins should reside under "Plugins" menu, as we currently have. If a plugin has a UI, then a plugin gets added to the list.

At least for now I don't think we should overcomplicate the implementation.

maraf commented 5 years ago

@RussKie It was primarily meant as an example of how to make extension points using MEF.

About extending main menu, I see your point. But on the other hand, for both plugins I have on GitHub, "Bundle Backuper" and "Solution Runner", it quite fits. Do you have some alternative suggestions?

RussKie commented 5 years ago

Do you have some alternative suggestions?

Keep them under "Plugins" menu. We could allow each plugin to add buttons to a special plugin toolstrip, however a user must be able to toggle buttons (from settings).

maraf commented 5 years ago

Keep them under "Plugins" menu.

My terrible UX-me says it won't be so easy to use, but ok.

We could allow each plugin to add buttons to a special plugin toolstrip, however a user must be able to toggle buttons (from settings).

Settings for menu will be really cool.

RussKie commented 5 years ago

That's how many other applications deal with plugins - Notepad++ or VS2019 to name a few

On Thu, 6 Jun 2019 at 16:22, Marek Fišera notifications@github.com wrote:

Keep them under "Plugins" menu.

My terrible UX-me says it won't be so easy to use, but ok.

We could allow each plugin to add buttons to a special plugin toolstrip, however a user must be able to toggle buttons (from settings).

Settings for menu will be really cool.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/gitextensions/gitextensions.extensibility/issues/6?email_source=notifications&email_token=ABBTEXTXWTEYRAHDVSKMAUDPZCULHA5CNFSM4HSSSM52YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODXB3JTA#issuecomment-499365068, or mute the thread https://github.com/notifications/unsubscribe-auth/ABBTEXSFKCKZK6F56OMA3A3PZCULHANCNFSM4HSSSM5Q .

ghost commented 4 years ago

We could do it here https://github.com/gitextensions/gitextensions/pull/8574

mast-eu commented 4 years ago

We could do it here gitextensions/gitextensions#8574

I'm not sure yet. Do you already have something specific in mind?

ghost commented 4 years ago

I'm not sure yet. Do you already have something specific in mind?

Please see the changes. There are also thoughts to implement all dependencies in one method.

ghost commented 4 years ago
public class GitUIEventArgs : CancelEventArgs
{
    public GitUIEventArgs(IWin32Window ownerForm, IGitUICommands gitUICommands)
        : base(cancel: false)
    {
        OwnerForm = ownerForm;
        GitUICommands = gitUICommands;
    }

    public IGitUICommands GitUICommands { get; }

    public IWin32Window OwnerForm { get; }

    public IGitModule GitModule => GitUICommands.GitModule;
}

I don't see what it is for. IGitUICommands really needed only . Owner form is always active form. Isn't that so?

public bool Execute(GitUIEventArgs args) => public bool Execute()

ghost commented 4 years ago
public interface IGitUICommands
{
    event EventHandler<GitUIPostActionEventArgs> PostCommit;
    event EventHandler<GitUIEventArgs> PostRepositoryChanged;
    event EventHandler<GitUIPostActionEventArgs> PostSettings;
    event EventHandler<GitUIPostActionEventArgs> PostUpdateSubmodules;
    event EventHandler<GitUIEventArgs> PostBrowseInitialize;
    event EventHandler<GitUIEventArgs> PostRegisterPlugin;
    event EventHandler<GitUIEventArgs> PreCommit;

We can use the interface as in the case of subscribing to the registration event.

public sealed class SomePlugin: IPostCommitHandler
{
    public void OnPostCommit()
    {
    }
}