161 lines
3.9 KiB
Go
161 lines
3.9 KiB
Go
package help
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"forge.lthn.ai/core/cli/pkg/cli"
|
|
gohelp "forge.lthn.ai/core/go-help"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var startHelpServer = func(catalog *gohelp.Catalog, addr string) error {
|
|
return gohelp.NewServer(catalog, addr).ListenAndServe()
|
|
}
|
|
|
|
func AddHelpCommands(root *cli.Command) {
|
|
var searchQuery string
|
|
|
|
helpCmd := &cli.Command{
|
|
Use: "help [topic]",
|
|
Short: "Display help documentation",
|
|
Args: cobra.RangeArgs(0, 1),
|
|
RunE: func(cmd *cli.Command, args []string) error {
|
|
catalog := gohelp.DefaultCatalog()
|
|
|
|
if searchQuery != "" {
|
|
return searchHelpTopics(catalog, searchQuery)
|
|
}
|
|
|
|
if len(args) == 0 {
|
|
return renderTopicList(catalog.List())
|
|
}
|
|
|
|
topic, err := catalog.Get(args[0])
|
|
if err != nil {
|
|
if suggestions := catalog.Search(args[0]); len(suggestions) > 0 {
|
|
if suggestErr := renderSearchResults(suggestions, args[0]); suggestErr != nil {
|
|
return suggestErr
|
|
}
|
|
cli.Blank()
|
|
renderHelpHint(args[0])
|
|
return cli.Err("help topic %q not found", args[0])
|
|
}
|
|
renderHelpHint(args[0])
|
|
return cli.Err("help topic %q not found", args[0])
|
|
}
|
|
|
|
renderTopic(topic)
|
|
return nil
|
|
},
|
|
}
|
|
|
|
searchCmd := &cli.Command{
|
|
Use: "search [query]",
|
|
Short: "Search help topics",
|
|
Args: cobra.ArbitraryArgs,
|
|
}
|
|
var searchCmdQuery string
|
|
searchCmd.Flags().StringVarP(&searchCmdQuery, "query", "q", "", "Search query")
|
|
searchCmd.RunE = func(cmd *cli.Command, args []string) error {
|
|
catalog := gohelp.DefaultCatalog()
|
|
query := strings.TrimSpace(searchCmdQuery)
|
|
if query == "" {
|
|
query = strings.TrimSpace(strings.Join(args, " "))
|
|
}
|
|
if query == "" {
|
|
renderHelpHint("")
|
|
return cli.Err("help search query is required")
|
|
}
|
|
return searchHelpTopics(catalog, query)
|
|
}
|
|
|
|
var serveAddr string
|
|
serveCmd := &cli.Command{
|
|
Use: "serve",
|
|
Short: "Serve help documentation over HTTP",
|
|
Args: cobra.NoArgs,
|
|
RunE: func(cmd *cli.Command, args []string) error {
|
|
return startHelpServer(gohelp.DefaultCatalog(), serveAddr)
|
|
},
|
|
}
|
|
serveCmd.Flags().StringVar(&serveAddr, "addr", ":8080", "HTTP listen address")
|
|
|
|
helpCmd.AddCommand(serveCmd)
|
|
helpCmd.AddCommand(searchCmd)
|
|
helpCmd.Flags().StringVarP(&searchQuery, "search", "s", "", "Search help topics")
|
|
root.AddCommand(helpCmd)
|
|
}
|
|
|
|
func searchHelpTopics(catalog *gohelp.Catalog, query string) error {
|
|
return renderSearchResults(catalog.Search(query), query)
|
|
}
|
|
|
|
func renderSearchResults(results []*gohelp.SearchResult, query string) error {
|
|
if len(results) == 0 {
|
|
renderHelpHint(query)
|
|
return cli.Err("no help topics matched %q", query)
|
|
}
|
|
|
|
cli.Section("Search Results")
|
|
for _, res := range results {
|
|
cli.Println(" %s - %s", res.Topic.ID, res.Topic.Title)
|
|
if snippet := strings.TrimSpace(res.Snippet); snippet != "" {
|
|
cli.Println("%s", cli.DimStr(" "+snippet))
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func renderHelpHint(query string) {
|
|
cli.Hint("browse", "core help")
|
|
if trimmed := strings.TrimSpace(query); trimmed != "" {
|
|
cli.Hint("search", fmt.Sprintf("core help search %q", trimmed))
|
|
}
|
|
}
|
|
|
|
func renderTopicList(topics []*gohelp.Topic) error {
|
|
if len(topics) == 0 {
|
|
return cli.Err("no help topics available")
|
|
}
|
|
|
|
cli.Section("Available Help Topics")
|
|
for _, topic := range topics {
|
|
cli.Println(" %s - %s", topic.ID, topic.Title)
|
|
if summary := topicSummary(topic); summary != "" {
|
|
cli.Println("%s", cli.DimStr(" "+summary))
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func topicSummary(topic *gohelp.Topic) string {
|
|
if topic == nil {
|
|
return ""
|
|
}
|
|
|
|
content := strings.TrimSpace(topic.Content)
|
|
if content == "" {
|
|
return ""
|
|
}
|
|
|
|
scanner := bufio.NewScanner(strings.NewReader(content))
|
|
for scanner.Scan() {
|
|
line := strings.TrimSpace(scanner.Text())
|
|
if line == "" || strings.HasPrefix(line, "#") {
|
|
continue
|
|
}
|
|
return line
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func renderTopic(t *gohelp.Topic) {
|
|
cli.Blank()
|
|
cli.Println("%s", cli.TitleStyle.Render(t.Title))
|
|
cli.Println("%s", strings.Repeat("-", len(t.Title)))
|
|
cli.Blank()
|
|
cli.Println("%s", t.Content)
|
|
cli.Blank()
|
|
}
|