Skip to content

Commit db08019

Browse files
committed
Add plugin management API endpoints and CLI flag
- Expose new HTTP endpoints in the server for: - Listing all plugins (`/plugins`) - Listing plugin categories (`/plugin-categories`) - Validating plugin selections (`/validate-plugins`) - Add `--additional-plugins` CLI flag to vim-bootstrap generator for specifying extra plugins - Wire up backend logic to support plugin data and validation fixed: #101 fixed: #100 fixed: #98 fixed: #96 fixed: #92 fixed: #80 Signed-off-by: Avelino <31996+avelino@users.noreply.github.com>
1 parent 6fd450a commit db08019

File tree

10 files changed

+1052
-22
lines changed

10 files changed

+1052
-22
lines changed

cmd/vim-bootstrap-server/vim-bootstrap-server.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ func main() {
2525
r.HandleFunc("/langs", web.HandleLangs)
2626
r.HandleFunc("/frameworks", web.HandleFrameworks)
2727
r.HandleFunc("/themes", web.HandleThemes)
28+
r.HandleFunc("/plugins", web.HandlePlugins)
29+
r.HandleFunc("/plugin-categories", web.HandlePluginCategories)
30+
r.HandleFunc("/validate-plugins", web.HandleValidatePlugins).Methods("POST")
2831
r.PathPrefix("/assets").Handler(
2932
http.StripPrefix("/assets", http.FileServer(http.Dir("./template/assets/"))))
3033
n.UseHandler(r)

cmd/vim-bootstrap/vim-bootstrap.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,16 @@ func main() {
1414
frameworks := flag.String("frameworks", "", "Set frameworks used: vue,react,django")
1515
theme := flag.String("theme", "molokai", "Set colorscheme: dracula, molokai, codedark")
1616
editor := flag.String("editor", "vim", "Set editor: vim or nvim")
17+
additionalPlugins := flag.String("additional-plugins", "", "Set additional plugins: fzf,vim-easymotion,vim-surround")
1718
flag.Parse()
1819

1920
obj := generate.Object{
20-
Frameworks: strings.Split(*frameworks, ","),
21-
Language: strings.Split(*langs, ","),
22-
Theme: *theme,
23-
Editor: *editor,
24-
Version: web.HashCommit(),
21+
Frameworks: strings.Split(*frameworks, ","),
22+
Language: strings.Split(*langs, ","),
23+
AdditionalPlugins: strings.Split(*additionalPlugins, ","),
24+
Theme: *theme,
25+
Editor: *editor,
26+
Version: web.HashCommit(),
2527
}
2628
gen := generate.Generate(&obj)
2729
fmt.Println(gen)

generate/generate.go

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,18 @@ type Theme struct {
2424

2525
// Object ...
2626
type Object struct {
27-
Language []string
28-
Frameworks []string
29-
Theme string
30-
BufferLang map[string]string
31-
BufferFramework map[string]string
32-
BufferTheme Theme
33-
BufferBundle map[string]string
34-
Editor string
35-
Config *Config
36-
Version string
27+
Language []string
28+
Frameworks []string
29+
Theme string
30+
AdditionalPlugins []string
31+
BufferLang map[string]string
32+
BufferFramework map[string]string
33+
BufferTheme Theme
34+
BufferBundle map[string]string
35+
BufferAdditional map[string]Plugin
36+
Editor string
37+
Config *Config
38+
Version string
3739
}
3840

3941
//go:embed vim_template/vimrc
@@ -120,6 +122,10 @@ func Generate(obj *Object) (buffer string) {
120122
}
121123
obj.BufferBundle = mBundle
122124

125+
// Load additional plugins
126+
additionalPlugins, _ := GetAdditionalPluginsConfig(obj.AdditionalPlugins)
127+
obj.BufferAdditional = additionalPlugins
128+
123129
var vimBuffer bytes.Buffer
124130
t := template.Must(template.New("vimrc").Parse(vimrc))
125131
t.Execute(&vimBuffer, obj)

generate/plugins.go

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
package generate
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
)
9+
10+
// Plugin represents an additional vim plugin
11+
type Plugin struct {
12+
Name string `json:"name"`
13+
Description string `json:"description"`
14+
Category string `json:"category"`
15+
Bundle string `json:"bundle"` // Plug command
16+
Config string `json:"config"` // Vim configuration
17+
Dependencies []string `json:"dependencies"` // Required plugins
18+
Conflicts []string `json:"conflicts"` // Conflicting plugins
19+
Tags []string `json:"tags"` // Search tags
20+
}
21+
22+
// PluginCategory represents a category of plugins
23+
type PluginCategory struct {
24+
Name string `json:"name"`
25+
Description string `json:"description"`
26+
Plugins []Plugin `json:"plugins"`
27+
}
28+
29+
// PluginDatabase represents the entire plugin database
30+
type PluginDatabase struct {
31+
Categories []PluginCategory `json:"categories"`
32+
}
33+
34+
// loadPluginsJSON loads the plugin database from JSON file
35+
func loadPluginsJSON() string {
36+
// Try to load from the plugins.json file
37+
jsonPath := filepath.Join("generate", "plugins.json")
38+
if data, err := os.ReadFile(jsonPath); err == nil {
39+
return string(data)
40+
}
41+
42+
// Fallback to embedded data
43+
return `{
44+
"categories": [
45+
{
46+
"name": "File Management",
47+
"description": "Plugins for file and buffer management",
48+
"plugins": [
49+
{
50+
"name": "fzf",
51+
"description": "Fuzzy finder for files, buffers, and more",
52+
"category": "File Management",
53+
"bundle": "Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --bin' }\nPlug 'junegunn/fzf.vim'",
54+
"config": "let g:fzf_layout = { 'down': '~40%' }\nlet g:fzf_preview_window = 'right:60%'\nlet g:fzf_action = {\n \\ 'ctrl-t': 'tab split',\n \\ 'ctrl-x': 'split',\n \\ 'ctrl-v': 'vsplit'\n \\ }",
55+
"dependencies": [],
56+
"conflicts": [],
57+
"tags": ["fuzzy", "search", "files", "buffers"]
58+
},
59+
{
60+
"name": "vim-easymotion",
61+
"description": "Vim motions on speed!",
62+
"category": "File Management",
63+
"bundle": "Plug 'easymotion/vim-easymotion'",
64+
"config": "let g:EasyMotion_do_mapping = 0\nlet g:EasyMotion_smartcase = 1\nmap <Leader>j <Plug>(easymotion-j)\nmap <Leader>k <Plug>(easymotion-k)",
65+
"dependencies": [],
66+
"conflicts": [],
67+
"tags": ["motion", "navigation", "jump"]
68+
}
69+
]
70+
}
71+
]
72+
}`
73+
}
74+
75+
var pluginDB *PluginDatabase
76+
77+
// GetPluginDatabase returns the plugin database
78+
func GetPluginDatabase() *PluginDatabase {
79+
if pluginDB == nil {
80+
pluginDB = &PluginDatabase{}
81+
pluginsJSON := loadPluginsJSON()
82+
if err := json.Unmarshal([]byte(pluginsJSON), pluginDB); err != nil {
83+
// Fallback to empty database if JSON parsing fails
84+
pluginDB = &PluginDatabase{Categories: []PluginCategory{}}
85+
}
86+
}
87+
return pluginDB
88+
}
89+
90+
// ListPluginCategories returns all available plugin categories
91+
func ListPluginCategories() []PluginCategory {
92+
db := GetPluginDatabase()
93+
return db.Categories
94+
}
95+
96+
// ListPlugins returns all available plugins
97+
func ListPlugins() []Plugin {
98+
db := GetPluginDatabase()
99+
var plugins []Plugin
100+
for _, category := range db.Categories {
101+
plugins = append(plugins, category.Plugins...)
102+
}
103+
return plugins
104+
}
105+
106+
// GetPluginsByCategory returns plugins for a specific category
107+
func GetPluginsByCategory(categoryName string) []Plugin {
108+
db := GetPluginDatabase()
109+
for _, category := range db.Categories {
110+
if category.Name == categoryName {
111+
return category.Plugins
112+
}
113+
}
114+
return []Plugin{}
115+
}
116+
117+
// GetPluginByName returns a specific plugin by name
118+
func GetPluginByName(name string) *Plugin {
119+
plugins := ListPlugins()
120+
for _, plugin := range plugins {
121+
if plugin.Name == name {
122+
return &plugin
123+
}
124+
}
125+
return nil
126+
}
127+
128+
// ValidatePluginSelection validates a list of selected plugins
129+
func ValidatePluginSelection(selectedPlugins []string) ([]string, []string, error) {
130+
var warnings []string
131+
var errors []string
132+
133+
plugins := ListPlugins()
134+
pluginMap := make(map[string]Plugin)
135+
for _, plugin := range plugins {
136+
pluginMap[plugin.Name] = plugin
137+
}
138+
139+
// Check if all selected plugins exist
140+
for _, pluginName := range selectedPlugins {
141+
if _, exists := pluginMap[pluginName]; !exists {
142+
errors = append(errors, fmt.Sprintf("Plugin '%s' not found", pluginName))
143+
}
144+
}
145+
146+
// Check for conflicts
147+
for i, pluginName1 := range selectedPlugins {
148+
plugin1, exists1 := pluginMap[pluginName1]
149+
if !exists1 {
150+
continue
151+
}
152+
153+
for j, pluginName2 := range selectedPlugins {
154+
if i >= j {
155+
continue
156+
}
157+
158+
plugin2, exists2 := pluginMap[pluginName2]
159+
if !exists2 {
160+
continue
161+
}
162+
163+
// Check if plugin1 conflicts with plugin2
164+
for _, conflict := range plugin1.Conflicts {
165+
if conflict == pluginName2 {
166+
warnings = append(warnings, fmt.Sprintf("Plugin '%s' may conflict with '%s'", pluginName1, pluginName2))
167+
}
168+
}
169+
170+
// Check if plugin2 conflicts with plugin1
171+
for _, conflict := range plugin2.Conflicts {
172+
if conflict == pluginName1 {
173+
warnings = append(warnings, fmt.Sprintf("Plugin '%s' may conflict with '%s'", pluginName2, pluginName1))
174+
}
175+
}
176+
}
177+
}
178+
179+
// Check dependencies
180+
for _, pluginName := range selectedPlugins {
181+
plugin, exists := pluginMap[pluginName]
182+
if !exists {
183+
continue
184+
}
185+
186+
for _, dep := range plugin.Dependencies {
187+
found := false
188+
for _, selected := range selectedPlugins {
189+
if selected == dep {
190+
found = true
191+
break
192+
}
193+
}
194+
if !found {
195+
warnings = append(warnings, fmt.Sprintf("Plugin '%s' requires '%s' (not selected)", pluginName, dep))
196+
}
197+
}
198+
}
199+
200+
return warnings, errors, nil
201+
}
202+
203+
// GetAdditionalPluginsConfig returns the configuration for selected additional plugins
204+
func GetAdditionalPluginsConfig(selectedPlugins []string) (map[string]Plugin, error) {
205+
config := make(map[string]Plugin)
206+
207+
for _, pluginName := range selectedPlugins {
208+
plugin := GetPluginByName(pluginName)
209+
if plugin != nil {
210+
config[pluginName] = *plugin
211+
}
212+
}
213+
214+
return config, nil
215+
}

0 commit comments

Comments
 (0)