206 lines
4.6 KiB
Go
206 lines
4.6 KiB
Go
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
|
|
"github.com/spf13/cobra"
|
|
"github.com/yeho/doks/internal/config"
|
|
"github.com/yeho/doks/internal/registry"
|
|
"github.com/yeho/doks/internal/search"
|
|
"github.com/yeho/doks/internal/tui"
|
|
)
|
|
|
|
var (
|
|
Version = "dev" // Set at build time via ldflags
|
|
|
|
keyFlag string
|
|
searchFlag string
|
|
versionFlag bool
|
|
cfg *config.Config
|
|
)
|
|
|
|
var rootCmd = &cobra.Command{
|
|
Use: "doks",
|
|
Short: "A terminal app for quick access to personal notes",
|
|
Long: `doks is a terminal application for quickly accessing short notes
|
|
and manuals. Access documents by key or search through content.`,
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
if versionFlag {
|
|
fmt.Println(Version)
|
|
return
|
|
}
|
|
if keyFlag != "" {
|
|
retrieveByKey(keyFlag)
|
|
return
|
|
}
|
|
if searchFlag != "" {
|
|
searchDocuments(searchFlag)
|
|
return
|
|
}
|
|
cmd.Help()
|
|
},
|
|
}
|
|
|
|
func Execute() {
|
|
if err := rootCmd.Execute(); err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func init() {
|
|
cobra.OnInitialize(initConfig)
|
|
rootCmd.Flags().StringVarP(&keyFlag, "key", "k", "", "Retrieve document by key")
|
|
rootCmd.Flags().StringVarP(&searchFlag, "search", "s", "", "Search documents by text")
|
|
rootCmd.Flags().BoolVarP(&versionFlag, "version", "v", false, "Print version")
|
|
|
|
// Register key completion
|
|
rootCmd.RegisterFlagCompletionFunc("key", completeKeys)
|
|
}
|
|
|
|
func completeKeys(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
|
// Need to initialize config for completion
|
|
if cfg == nil {
|
|
var err error
|
|
cfg, err = config.Load()
|
|
if err != nil {
|
|
return nil, cobra.ShellCompDirectiveError
|
|
}
|
|
}
|
|
|
|
reg, err := registry.Load(cfg.RegistryPath())
|
|
if err != nil {
|
|
return nil, cobra.ShellCompDirectiveError
|
|
}
|
|
|
|
keys := []string{}
|
|
for _, entry := range reg.List() {
|
|
keys = append(keys, entry.Key)
|
|
}
|
|
return keys, cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
func initConfig() {
|
|
var err error
|
|
cfg, err = config.Load()
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error loading config: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func retrieveByKey(key string) {
|
|
reg, err := registry.Load(cfg.RegistryPath())
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error loading registry: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
entry, err := reg.Get(key)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Document not found: %s\n", key)
|
|
os.Exit(1)
|
|
}
|
|
|
|
filePath := cfg.FilePath(entry.Filename)
|
|
displayFile(filePath, 0)
|
|
}
|
|
|
|
func searchDocuments(query string) {
|
|
reg, err := registry.Load(cfg.RegistryPath())
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error loading registry: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
searcher := search.NewTextSearcher(cfg.FilesDir(), reg)
|
|
opts := search.Options{
|
|
MaxResults: cfg.Display.MaxResults,
|
|
ContextLines: cfg.Display.ContextLines,
|
|
}
|
|
|
|
results, err := searcher.Search(query, opts)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error searching: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
if len(results) == 0 {
|
|
fmt.Printf("No results found for '%s'\n", query)
|
|
return
|
|
}
|
|
|
|
// If only one result, show it directly
|
|
if len(results) == 1 {
|
|
showResult(&results[0])
|
|
return
|
|
}
|
|
|
|
// Multiple results - show TUI
|
|
selected, err := tui.Run(results, query)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error running TUI: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
if selected != nil {
|
|
showResult(selected)
|
|
}
|
|
}
|
|
|
|
func showResult(result *search.Result) {
|
|
fmt.Printf("--- %s (line %d) ---\n", result.Key, result.LineNumber)
|
|
displayFile(result.FilePath, result.LineNumber)
|
|
}
|
|
|
|
func displayFile(filePath string, highlightLine int) {
|
|
// Try glow first for Markdown rendering
|
|
if glowPath, err := exec.LookPath("glow"); err == nil {
|
|
cmd := exec.Command(glowPath, filePath)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
if err := cmd.Run(); err == nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
// Fallback to bat for syntax highlighting
|
|
if batPath, err := exec.LookPath("bat"); err == nil {
|
|
args := []string{
|
|
"--paging=never",
|
|
"--color=always",
|
|
}
|
|
if highlightLine > 0 {
|
|
args = append(args, fmt.Sprintf("--highlight-line=%d", highlightLine))
|
|
}
|
|
args = append(args, filePath)
|
|
|
|
cmd := exec.Command(batPath, args...)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
if err := cmd.Run(); err == nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
// Fallback to cat
|
|
if catPath, err := exec.LookPath("cat"); err == nil {
|
|
cmd := exec.Command(catPath, filePath)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
if err := cmd.Run(); err == nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
// Final fallback to plain Go file read
|
|
content, err := os.ReadFile(filePath)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error reading file: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
fmt.Print(string(content))
|
|
}
|