verstak-desktop/internal/core/contribution/registry.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
}