287 lines
8.9 KiB
Go
287 lines
8.9 KiB
Go
// Package contribution provides a registry for plugin contribution points.
|
|
package contribution
|
|
|
|
import (
|
|
"sort"
|
|
"sync"
|
|
|
|
"github.com/verstak/verstak-desktop/internal/core/plugin"
|
|
)
|
|
|
|
// Registry tracks all contributions registered by plugins.
|
|
type Registry struct {
|
|
mu sync.RWMutex
|
|
|
|
views []ContributionView
|
|
commands []ContributionCommand
|
|
settingsPanels []ContributionSettingsPanel
|
|
sidebarItems []ContributionSidebarItem
|
|
fileActions []ContributionAction
|
|
noteActions []ContributionAction
|
|
contextMenus []ContributionContextMenuEntry
|
|
searchProviders []ContributionSearchProvider
|
|
activityProviders []ContributionActivityProvider
|
|
statusBarItems []ContributionStatusBarItem
|
|
}
|
|
|
|
type ContributionView struct {
|
|
PluginID string `json:"pluginId"`
|
|
Item plugin.ContributionView `json:"item"`
|
|
}
|
|
|
|
type ContributionCommand struct {
|
|
PluginID string `json:"pluginId"`
|
|
Item plugin.ContributionCommand `json:"item"`
|
|
}
|
|
|
|
type ContributionSettingsPanel struct {
|
|
PluginID string `json:"pluginId"`
|
|
Item plugin.ContributionSettingsPanel `json:"item"`
|
|
}
|
|
|
|
type ContributionSidebarItem struct {
|
|
PluginID string `json:"pluginId"`
|
|
Item plugin.ContributionSidebarItem `json:"item"`
|
|
}
|
|
|
|
type ContributionAction struct {
|
|
PluginID string `json:"pluginId"`
|
|
Item plugin.ContributionAction `json:"item"`
|
|
}
|
|
|
|
type ContributionContextMenuEntry struct {
|
|
PluginID string `json:"pluginId"`
|
|
Item plugin.ContributionContextMenuEntry `json:"item"`
|
|
}
|
|
|
|
type ContributionSearchProvider struct {
|
|
PluginID string `json:"pluginId"`
|
|
Item plugin.ContributionSearchProvider `json:"item"`
|
|
}
|
|
|
|
type ContributionActivityProvider struct {
|
|
PluginID string `json:"pluginId"`
|
|
Item plugin.ContributionActivityProvider `json:"item"`
|
|
}
|
|
|
|
type ContributionStatusBarItem struct {
|
|
PluginID string `json:"pluginId"`
|
|
Item plugin.ContributionStatusBarItem `json:"item"`
|
|
}
|
|
|
|
// NewRegistry creates a new contribution registry.
|
|
func NewRegistry() *Registry {
|
|
return &Registry{}
|
|
}
|
|
|
|
// Register adds all contributions from a plugin.
|
|
func (r *Registry) Register(pluginID string, c *plugin.Contributions) {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
for _, item := range c.Views {
|
|
r.views = append(r.views, ContributionView{PluginID: pluginID, Item: item})
|
|
}
|
|
for _, item := range c.Commands {
|
|
r.commands = append(r.commands, ContributionCommand{PluginID: pluginID, Item: item})
|
|
}
|
|
for _, item := range c.SettingsPanels {
|
|
r.settingsPanels = append(r.settingsPanels, ContributionSettingsPanel{PluginID: pluginID, Item: item})
|
|
}
|
|
for _, item := range c.SidebarItems {
|
|
r.sidebarItems = append(r.sidebarItems, ContributionSidebarItem{PluginID: pluginID, Item: item})
|
|
}
|
|
for _, item := range c.FileActions {
|
|
r.fileActions = append(r.fileActions, ContributionAction{PluginID: pluginID, Item: item})
|
|
}
|
|
for _, item := range c.NoteActions {
|
|
r.noteActions = append(r.noteActions, ContributionAction{PluginID: pluginID, Item: item})
|
|
}
|
|
for _, item := range c.ContextMenuEntries {
|
|
r.contextMenus = append(r.contextMenus, ContributionContextMenuEntry{PluginID: pluginID, Item: item})
|
|
}
|
|
for _, item := range c.SearchProviders {
|
|
r.searchProviders = append(r.searchProviders, ContributionSearchProvider{PluginID: pluginID, Item: item})
|
|
}
|
|
for _, item := range c.ActivityProviders {
|
|
r.activityProviders = append(r.activityProviders, ContributionActivityProvider{PluginID: pluginID, Item: item})
|
|
}
|
|
for _, item := range c.StatusBarItems {
|
|
r.statusBarItems = append(r.statusBarItems, ContributionStatusBarItem{PluginID: pluginID, Item: item})
|
|
}
|
|
}
|
|
|
|
// Unregister removes all contributions from a plugin.
|
|
func (r *Registry) Unregister(pluginID string) {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
r.views = removeViews(r.views, pluginID)
|
|
r.commands = removeCommands(r.commands, pluginID)
|
|
r.settingsPanels = removeSettingsPanels(r.settingsPanels, pluginID)
|
|
r.sidebarItems = removeSidebarItems(r.sidebarItems, pluginID)
|
|
r.fileActions = removeActions(r.fileActions, pluginID)
|
|
r.noteActions = removeActions(r.noteActions, pluginID)
|
|
r.contextMenus = removeContextMenus(r.contextMenus, pluginID)
|
|
r.searchProviders = removeSearchProviders(r.searchProviders, pluginID)
|
|
r.activityProviders = removeActivityProviders(r.activityProviders, pluginID)
|
|
r.statusBarItems = removeStatusBarItems(r.statusBarItems, pluginID)
|
|
}
|
|
|
|
// Getters — sorted for deterministic display.
|
|
|
|
func (r *Registry) Views() []ContributionView {
|
|
r.mu.RLock()
|
|
defer r.mu.RUnlock()
|
|
result := make([]ContributionView, len(r.views))
|
|
copy(result, r.views)
|
|
sort.Slice(result, func(i, j int) bool { return result[i].Item.ID < result[j].Item.ID })
|
|
return result
|
|
}
|
|
|
|
func (r *Registry) Commands() []ContributionCommand {
|
|
r.mu.RLock()
|
|
defer r.mu.RUnlock()
|
|
result := make([]ContributionCommand, len(r.commands))
|
|
copy(result, r.commands)
|
|
sort.Slice(result, func(i, j int) bool { return result[i].Item.ID < result[j].Item.ID })
|
|
return result
|
|
}
|
|
|
|
func (r *Registry) SettingsPanels() []ContributionSettingsPanel {
|
|
r.mu.RLock()
|
|
defer r.mu.RUnlock()
|
|
result := make([]ContributionSettingsPanel, len(r.settingsPanels))
|
|
copy(result, r.settingsPanels)
|
|
sort.Slice(result, func(i, j int) bool { return result[i].Item.ID < result[j].Item.ID })
|
|
return result
|
|
}
|
|
|
|
func (r *Registry) SidebarItems() []ContributionSidebarItem {
|
|
r.mu.RLock()
|
|
defer r.mu.RUnlock()
|
|
result := make([]ContributionSidebarItem, len(r.sidebarItems))
|
|
copy(result, r.sidebarItems)
|
|
sort.Slice(result, func(i, j int) bool { return result[i].Item.ID < result[j].Item.ID })
|
|
return result
|
|
}
|
|
|
|
func (r *Registry) FileActions() []ContributionAction {
|
|
r.mu.RLock()
|
|
defer r.mu.RUnlock()
|
|
result := make([]ContributionAction, len(r.fileActions))
|
|
copy(result, r.fileActions)
|
|
sort.Slice(result, func(i, j int) bool { return result[i].Item.ID < result[j].Item.ID })
|
|
return result
|
|
}
|
|
|
|
func (r *Registry) NoteActions() []ContributionAction {
|
|
r.mu.RLock()
|
|
defer r.mu.RUnlock()
|
|
result := make([]ContributionAction, len(r.noteActions))
|
|
copy(result, r.noteActions)
|
|
sort.Slice(result, func(i, j int) bool { return result[i].Item.ID < result[j].Item.ID })
|
|
return result
|
|
}
|
|
|
|
func (r *Registry) SearchProviders() []ContributionSearchProvider {
|
|
r.mu.RLock()
|
|
defer r.mu.RUnlock()
|
|
result := make([]ContributionSearchProvider, len(r.searchProviders))
|
|
copy(result, r.searchProviders)
|
|
sort.Slice(result, func(i, j int) bool { return result[i].Item.ID < result[j].Item.ID })
|
|
return result
|
|
}
|
|
|
|
// ─── Remove helpers ─────────────────────────────────────────
|
|
|
|
func removeViews(items []ContributionView, pluginID string) []ContributionView {
|
|
var result []ContributionView
|
|
for _, item := range items {
|
|
if item.PluginID != pluginID {
|
|
result = append(result, item)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func removeCommands(items []ContributionCommand, pluginID string) []ContributionCommand {
|
|
var result []ContributionCommand
|
|
for _, item := range items {
|
|
if item.PluginID != pluginID {
|
|
result = append(result, item)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func removeSettingsPanels(items []ContributionSettingsPanel, pluginID string) []ContributionSettingsPanel {
|
|
var result []ContributionSettingsPanel
|
|
for _, item := range items {
|
|
if item.PluginID != pluginID {
|
|
result = append(result, item)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func removeSidebarItems(items []ContributionSidebarItem, pluginID string) []ContributionSidebarItem {
|
|
var result []ContributionSidebarItem
|
|
for _, item := range items {
|
|
if item.PluginID != pluginID {
|
|
result = append(result, item)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func removeActions(items []ContributionAction, pluginID string) []ContributionAction {
|
|
var result []ContributionAction
|
|
for _, item := range items {
|
|
if item.PluginID != pluginID {
|
|
result = append(result, item)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func removeContextMenus(items []ContributionContextMenuEntry, pluginID string) []ContributionContextMenuEntry {
|
|
var result []ContributionContextMenuEntry
|
|
for _, item := range items {
|
|
if item.PluginID != pluginID {
|
|
result = append(result, item)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func removeSearchProviders(items []ContributionSearchProvider, pluginID string) []ContributionSearchProvider {
|
|
var result []ContributionSearchProvider
|
|
for _, item := range items {
|
|
if item.PluginID != pluginID {
|
|
result = append(result, item)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func removeActivityProviders(items []ContributionActivityProvider, pluginID string) []ContributionActivityProvider {
|
|
var result []ContributionActivityProvider
|
|
for _, item := range items {
|
|
if item.PluginID != pluginID {
|
|
result = append(result, item)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func removeStatusBarItems(items []ContributionStatusBarItem, pluginID string) []ContributionStatusBarItem {
|
|
var result []ContributionStatusBarItem
|
|
for _, item := range items {
|
|
if item.PluginID != pluginID {
|
|
result = append(result, item)
|
|
}
|
|
}
|
|
return result
|
|
}
|