Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Additional entry points for new plugins #6

Open
mast-eu opened this issue Apr 21, 2019 · 22 comments
Open

Additional entry points for new plugins #6

mast-eu opened this issue Apr 21, 2019 · 22 comments
Labels
type: discussion Something needs to be clarified
Milestone

Comments

@mast-eu
Copy link
Member

mast-eu commented Apr 21, 2019

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
Copy link
Member

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

@RussKie
Copy link
Member

RussKie commented Apr 23, 2019

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

@mast-eu

This comment has been minimized.

@gerhardol

This comment has been minimized.

@pmiossec

This comment has been minimized.

@gerhardol

This comment has been minimized.

@RussKie

This comment has been minimized.

@pmiossec

This comment has been minimized.

@mast-eu mast-eu transferred this issue from gitextensions/gitextensions Jun 3, 2019
@mast-eu mast-eu added the type: discussion Something needs to be clarified label Jun 3, 2019
@RussKie
Copy link
Member

RussKie commented Jun 4, 2019

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

@maraf
Copy link
Member

maraf commented Jun 4, 2019

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
Copy link
Member

RussKie commented Jun 4, 2019

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

@maraf
Copy link
Member

maraf commented Jun 4, 2019

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
Copy link
Member

RussKie commented Jun 5, 2019

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
Copy link
Member

maraf commented Jun 5, 2019

@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
Copy link
Member

RussKie commented Jun 6, 2019

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
Copy link
Member

maraf commented Jun 6, 2019

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
Copy link
Member

RussKie commented Jun 6, 2019 via email

@ghost
Copy link

ghost commented Oct 25, 2020

We could do it here
gitextensions/gitextensions#8574

@mast-eu
Copy link
Member Author

mast-eu commented Oct 29, 2020

We could do it here
gitextensions/gitextensions#8574

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

@ghost
Copy link

ghost commented Oct 29, 2020

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
Copy link

ghost commented Oct 29, 2020

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
Copy link

ghost commented Oct 29, 2020

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()
    {
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: discussion Something needs to be clarified
Projects
None yet
Development

No branches or pull requests

5 participants