178 lines
5.3 KiB
Go
178 lines
5.3 KiB
Go
package repl
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"github.com/LazyBachelor/LazyPM/pkg/cli/commands"
|
|
"github.com/c-bata/go-prompt"
|
|
"github.com/muesli/reflow/truncate"
|
|
)
|
|
|
|
var rootSuggestions = []prompt.Suggest{
|
|
{Text: "pm", Description: "Project Management System"},
|
|
{Text: "exit", Description: "Exit pm CLI"},
|
|
{Text: "help", Description: "Show help information"},
|
|
{Text: "title", Description: "Print the welcome title"},
|
|
{Text: "git", Description: "Version control system"},
|
|
}
|
|
|
|
var baseSuggestions = []prompt.Suggest{
|
|
{Text: "help", Description: "Show help information"},
|
|
{Text: "delete", Description: "Delete an issue by ID"},
|
|
{Text: "create", Description: "Create a new issue with title"},
|
|
{Text: "update", Description: "Update an existing issue by ID"},
|
|
{Text: "describe", Description: "Get issue details by ID"},
|
|
{Text: "list", Description: "List all issues"},
|
|
}
|
|
|
|
var createFlags = []prompt.Suggest{
|
|
{Text: "--desc", Description: "Issue description"},
|
|
{Text: "--status", Description: "Issue status (open, closed, in_progress)"},
|
|
{Text: "--type", Description: "Issue type (bug, feature, task)"},
|
|
{Text: "--priority", Description: "Issue priority (0-5)"},
|
|
}
|
|
|
|
var updateFlags = []prompt.Suggest{
|
|
{Text: "--title", Description: "New issue title"},
|
|
{Text: "--desc", Description: "New issue description"},
|
|
{Text: "--status", Description: "New issue status (open, closed, in_progress)"},
|
|
{Text: "--type", Description: "New issue type (bug, feature, task)"},
|
|
{Text: "--priority", Description: "New issue priority (0-5)"},
|
|
}
|
|
|
|
var listFlags = []prompt.Suggest{
|
|
{Text: "--title", Description: "Filter by title"},
|
|
{Text: "--desc", Description: "Filter by description"},
|
|
{Text: "--status", Description: "Filter by status (open, closed, in_progress)"},
|
|
{Text: "--type", Description: "Filter by type (bug, feature, task)"},
|
|
{Text: "--priority", Description: "Filter by priority (0-5)"},
|
|
{Text: "--limit", Description: "Limit number of results"},
|
|
}
|
|
|
|
var statusValues = []prompt.Suggest{
|
|
{Text: "open", Description: "Open status"},
|
|
{Text: "closed", Description: "Closed status"},
|
|
{Text: "in_progress", Description: "In progress status"},
|
|
}
|
|
|
|
var typeValues = []prompt.Suggest{
|
|
{Text: "bug", Description: "Bug issue type"},
|
|
{Text: "feature", Description: "Feature issue type"},
|
|
{Text: "task", Description: "Task issue type"},
|
|
}
|
|
|
|
var priorityValues = []prompt.Suggest{
|
|
{Text: "0", Description: "Lowest priority"},
|
|
{Text: "1", Description: "Low priority"},
|
|
{Text: "2", Description: "Medium-low priority"},
|
|
{Text: "3", Description: "Medium priority"},
|
|
{Text: "4", Description: "High priority"},
|
|
{Text: "5", Description: "Highest priority"},
|
|
}
|
|
|
|
func commandSuggestions(words []string) []prompt.Suggest {
|
|
if len(words) == 0 {
|
|
return baseSuggestions
|
|
}
|
|
return filterByPrefix(baseSuggestions, words[0])
|
|
}
|
|
|
|
func flagSuggestions(cmd string, words []string, text string) []prompt.Suggest {
|
|
lastWord, prevWord := parseWords(words, text)
|
|
|
|
if values := getFlagValues(prevWord); values != nil {
|
|
return filterByPrefix(values, lastWord)
|
|
}
|
|
|
|
if cmd == "describe" || cmd == "delete" || cmd == "del" || cmd == "rm" || cmd == "remove" || cmd == "get" || cmd == "read" {
|
|
// For ID-oriented commands, pass the current partial argument (lastWord)
|
|
// so issueIDSuggestions can distinguish between completing the command
|
|
// name and completing the ID itself.
|
|
return issueIDSuggestions(lastWord, len(words) >= 2)
|
|
}
|
|
|
|
// Update/edit: suggest issue IDs when typing the ID, flags after ID is provided
|
|
if cmd == "update" || cmd == "edit" {
|
|
if len(words) >= 2 && (lastWord == "" || strings.HasPrefix(lastWord, "-")) {
|
|
// ID already provided (trailing space) or typing a flag -> suggest update flags
|
|
flags := updateFlags
|
|
if lastWord == "" {
|
|
return flags
|
|
}
|
|
return filterByPrefix(flags, lastWord)
|
|
}
|
|
return issueIDSuggestions(lastWord, len(words) >= 2)
|
|
}
|
|
|
|
var flags []prompt.Suggest
|
|
switch cmd {
|
|
case "create", "add":
|
|
flags = createFlags
|
|
case "list", "ls", "search":
|
|
flags = listFlags
|
|
default:
|
|
return nil
|
|
}
|
|
|
|
if lastWord == "" {
|
|
return flags
|
|
}
|
|
return filterByPrefix(flags, lastWord)
|
|
}
|
|
|
|
func issueIDSuggestions(partial string, hasCommand bool) []prompt.Suggest {
|
|
// Only show suggestions if we've typed the command already
|
|
if !hasCommand {
|
|
return nil
|
|
}
|
|
|
|
issues, _ := commands.GetIssueCompletions(context.Background(), partial)
|
|
|
|
var suggestions []prompt.Suggest
|
|
for _, issue := range issues {
|
|
suggestions = append(suggestions, prompt.Suggest{
|
|
Text: issue.ID,
|
|
Description: truncate.String(issue.Title, 20),
|
|
})
|
|
}
|
|
return suggestions
|
|
}
|
|
|
|
func parseWords(words []string, text string) (lastWord, prevWord string) {
|
|
if len(words) > 0 && !strings.HasSuffix(text, " ") {
|
|
lastWord = words[len(words)-1]
|
|
if len(words) >= 2 {
|
|
prevWord = words[len(words)-2]
|
|
}
|
|
} else if len(words) >= 1 {
|
|
prevWord = words[len(words)-1]
|
|
}
|
|
return
|
|
}
|
|
|
|
func getFlagValues(flag string) []prompt.Suggest {
|
|
switch flag {
|
|
case "-s", "--status":
|
|
return statusValues
|
|
case "-t", "--type":
|
|
return typeValues
|
|
case "-p", "--priority":
|
|
return priorityValues
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func filterByPrefix(suggestions []prompt.Suggest, prefix string) []prompt.Suggest {
|
|
if prefix == "" {
|
|
return suggestions
|
|
}
|
|
var filtered []prompt.Suggest
|
|
for _, s := range suggestions {
|
|
if strings.HasPrefix(s.Text, prefix) {
|
|
filtered = append(filtered, s)
|
|
}
|
|
}
|
|
return filtered
|
|
}
|