Try Live
Add Docs
Rankings
Pricing
Enterprise
Docs
Install
Install
Docs
Pricing
Enterprise
More...
More...
Try Live
Rankings
Add Docs
Go Fuzzyfinder
https://github.com/ktr0731/go-fuzzyfinder
Admin
Go Fuzzyfinder is a Go library that provides fuzzy-finding functionality with an fzf-like terminal
...
Tokens:
6,140
Snippets:
15
Trust Score:
9.9
Update:
6 months ago
Context
Skills
Chat
Benchmark
89.4
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# go-fuzzyfinder go-fuzzyfinder is a Go library that provides an interactive fuzzy-finding interface with a terminal user interface similar to fzf. It enables developers to embed powerful fuzzy-search capabilities directly into their Go applications without requiring external dependencies or command-line tools. The library handles all terminal rendering, input processing, and fuzzy matching algorithms internally. The library solves common problems faced when integrating fuzzy-finders into Go applications: maintaining references to complex objects (not just strings), avoiding external tool dependencies, and providing rich preview functionality for selected items. It supports both single and multi-select modes, hot-reloading of dynamic data sources, customizable matching modes, and extensive keyboard navigation. ## API Reference ### Find - Single item selection with fuzzy search Interactive fuzzy finder that returns the index of a single selected item from a slice. Displays a searchable list with optional preview window and keyboard navigation (arrow keys, Ctrl+J/K, page up/down). Returns ErrAbort if user cancels with Esc/Ctrl+C/Ctrl+D. ```go package main import ( "fmt" "log" fuzzyfinder "github.com/ktr0731/go-fuzzyfinder" ) type Track struct { Name string Album string Artist string } func main() { tracks := []Track{ {"Bohemian Rhapsody", "A Night at the Opera", "Queen"}, {"Stairway to Heaven", "Led Zeppelin IV", "Led Zeppelin"}, {"Hotel California", "Hotel California", "Eagles"}, {"Imagine", "Imagine", "John Lennon"}, } idx, err := fuzzyfinder.Find( tracks, func(i int) string { return tracks[i].Name }, fuzzyfinder.WithPreviewWindow(func(i, w, h int) string { if i == -1 { return "" } return fmt.Sprintf("Track: %s\nArtist: %s\nAlbum: %s", tracks[i].Name, tracks[i].Artist, tracks[i].Album) }), ) if err != nil { if err == fuzzyfinder.ErrAbort { log.Fatal("Selection cancelled") } log.Fatal(err) } fmt.Printf("Selected: %s by %s\n", tracks[idx].Name, tracks[idx].Artist) } ``` ### FindMulti - Multiple item selection with tab toggling Multi-select fuzzy finder that allows users to toggle selections with Tab key and returns indices of all selected items. If no items are explicitly selected via Tab, returns the currently highlighted item. Selected items are marked with '>' indicator and returned in selection order. ```go package main import ( "fmt" "log" fuzzyfinder "github.com/ktr0731/go-fuzzyfinder" ) type Task struct { ID int Title string Priority string Status string } func main() { tasks := []Task{ {1, "Fix authentication bug", "High", "Open"}, {2, "Update documentation", "Low", "Open"}, {3, "Refactor API handler", "Medium", "In Progress"}, {4, "Add unit tests", "High", "Open"}, {5, "Deploy to production", "Critical", "Blocked"}, } idxs, err := fuzzyfinder.FindMulti( tasks, func(i int) string { return fmt.Sprintf("[%s] %s", tasks[i].Priority, tasks[i].Title) }, fuzzyfinder.WithPreviewWindow(func(i, w, h int) string { if i == -1 { return "Select tasks to work on (Tab to select, Enter to confirm)" } return fmt.Sprintf( "Task #%d\n"+ "Title: %s\n"+ "Priority: %s\n"+ "Status: %s", tasks[i].ID, tasks[i].Title, tasks[i].Priority, tasks[i].Status, ) }), ) if err != nil { log.Fatal(err) } fmt.Println("Selected tasks:") for _, idx := range idxs { fmt.Printf(" - Task #%d: %s\n", tasks[idx].ID, tasks[idx].Title) } } ``` ### WithMode - Configure matching sensitivity Controls case-sensitivity behavior for fuzzy matching. ModeSmart (default) starts case-insensitive and switches to case-sensitive when uppercase is typed. ModeCaseSensitive always requires exact case matching. ModeCaseInsensitive ignores case entirely. ```go package main import ( "fmt" "log" fuzzyfinder "github.com/ktr0731/go-fuzzyfinder" ) func main() { files := []string{ "README.md", "readme.txt", "Config.yaml", "config.json", "Makefile", "makefile", } // Case-insensitive: typing "readme" matches both "README.md" and "readme.txt" idx, err := fuzzyfinder.Find( files, func(i int) string { return files[i] }, fuzzyfinder.WithMode(fuzzyfinder.ModeCaseInsensitive), ) if err != nil { log.Fatal(err) } fmt.Printf("Selected (case-insensitive): %s\n", files[idx]) // Case-sensitive: typing "README" only matches "README.md" idx, err = fuzzyfinder.Find( files, func(i int) string { return files[i] }, fuzzyfinder.WithMode(fuzzyfinder.ModeCaseSensitive), ) if err != nil { log.Fatal(err) } fmt.Printf("Selected (case-sensitive): %s\n", files[idx]) } ``` ### WithPreselected - Pre-select items on initialization Marks items as initially selected based on a predicate function. In Find mode, positions cursor at first matching item. In FindMulti mode, all matching items are pre-selected with Tab marks. Useful for implementing "edit selection" workflows or highlighting recommended choices. ```go package main import ( "fmt" "log" fuzzyfinder "github.com/ktr0731/go-fuzzyfinder" ) type Feature struct { Name string Enabled bool Premium bool } func main() { features := []Feature{ {"Email notifications", true, false}, {"Dark mode", true, false}, {"Real-time sync", false, true}, {"Advanced analytics", false, true}, {"Two-factor auth", true, false}, {"API access", false, true}, } // Pre-select all currently enabled features for bulk editing idxs, err := fuzzyfinder.FindMulti( features, func(i int) string { status := "[ ]" if features[i].Enabled { status = "[✓]" } premium := "" if features[i].Premium { premium = " (Premium)" } return fmt.Sprintf("%s %s%s", status, features[i].Name, premium) }, fuzzyfinder.WithPreselected(func(i int) bool { return features[i].Enabled }), fuzzyfinder.WithPreviewWindow(func(i, w, h int) string { if i == -1 { return "Select features to enable/disable" } status := "Disabled" if features[i].Enabled { status = "Enabled" } tier := "Free" if features[i].Premium { tier = "Premium only" } return fmt.Sprintf("%s\nStatus: %s\nTier: %s", features[i].Name, status, tier) }), ) if err != nil { log.Fatal(err) } fmt.Println("New feature configuration:") enabledSet := make(map[int]bool) for _, idx := range idxs { enabledSet[idx] = true } for i, f := range features { enabled := enabledSet[i] fmt.Printf(" %s: %v\n", f.Name, enabled) } } ``` ### WithHotReloadLock - Dynamic data source with live updates Enables automatic reloading when slice contents change during selection. Requires passing slice pointer instead of value, and a sync.Locker for thread-safe access. Polls for changes every 30ms. Never lock within itemFunc - locking is handled internally. Use RLocker in preview functions only. ```go package main import ( "bufio" "fmt" "log" "os" "sync" "time" fuzzyfinder "github.com/ktr0731/go-fuzzyfinder" ) type LogEntry struct { Timestamp time.Time Level string Message string } func main() { var logs []LogEntry var mu sync.RWMutex // Simulate log streaming in background go func() { scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { mu.Lock() logs = append(logs, LogEntry{ Timestamp: time.Now(), Level: "INFO", Message: scanner.Text(), }) mu.Unlock() time.Sleep(100 * time.Millisecond) } }() // Alternative: simulate periodic log generation go func() { ticker := time.NewTicker(500 * time.Millisecond) defer ticker.Stop() counter := 0 for range ticker.C { mu.Lock() counter++ logs = append(logs, LogEntry{ Timestamp: time.Now(), Level: "DEBUG", Message: fmt.Sprintf("Background task %d completed", counter), }) mu.Unlock() } }() // Wait for some initial data time.Sleep(1 * time.Second) idxs, err := fuzzyfinder.FindMulti( &logs, // Note: pointer to slice func(i int) string { // Do NOT lock here - handled internally return fmt.Sprintf("[%s] %s", logs[i].Level, logs[i].Message) }, fuzzyfinder.WithHotReloadLock(mu.RLocker()), fuzzyfinder.WithPreviewWindow(func(i, w, h int) string { if i == -1 { return "Log viewer - list updates in real-time" } // Safe to lock in preview function mu.RLock() defer mu.RUnlock() return fmt.Sprintf( "Time: %s\nLevel: %s\nMessage: %s", logs[i].Timestamp.Format("15:04:05.000"), logs[i].Level, logs[i].Message, ) }), ) if err != nil { log.Fatal(err) } mu.RLock() defer mu.RUnlock() fmt.Println("Selected log entries:") for _, idx := range idxs { fmt.Printf(" [%s] %s: %s\n", logs[idx].Timestamp.Format("15:04:05"), logs[idx].Level, logs[idx].Message, ) } } ``` ### WithQuery - Initialize with search query Sets initial input query string when finder opens. Immediately filters items to matches and positions cursor appropriately. Useful for implementing search suggestions, restoring previous searches, or pre-filtering large datasets. ```go package main import ( "fmt" "log" fuzzyfinder "github.com/ktr0731/go-fuzzyfinder" ) func main() { packages := []string{ "github.com/gorilla/mux", "github.com/gin-gonic/gin", "github.com/labstack/echo", "github.com/gofiber/fiber", "github.com/valyala/fasthttp", "golang.org/x/net", "golang.org/x/sync", "golang.org/x/crypto", } // User previously searched for "gin", restore their search previousSearch := "gin" idx, err := fuzzyfinder.Find( packages, func(i int) string { return packages[i] }, fuzzyfinder.WithQuery(previousSearch), fuzzyfinder.WithPreviewWindow(func(i, w, h int) string { if i == -1 { return "" } return fmt.Sprintf("Package: %s\n\nPress Enter to select", packages[i]) }), ) if err != nil { log.Fatal(err) } fmt.Printf("Selected package: %s\n", packages[idx]) } ``` ### WithContext - Graceful cancellation from parent Allows external cancellation of the finder using context. When context is cancelled, Find/FindMulti immediately exits and returns the context error. Essential for implementing timeouts, signal handling, or coordinated shutdown in server applications. ```go package main import ( "context" "fmt" "log" "os" "os/signal" "syscall" "time" fuzzyfinder "github.com/ktr0731/go-fuzzyfinder" ) func main() { items := []string{ "Process transaction", "Generate report", "Export data", "Send notifications", "Archive old records", } // Example 1: Timeout after 30 seconds ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() idx, err := fuzzyfinder.Find( items, func(i int) string { return items[i] }, fuzzyfinder.WithContext(ctx), ) if err != nil { if err == context.DeadlineExceeded { log.Fatal("Selection timed out after 30 seconds") } log.Fatal(err) } fmt.Printf("Selected: %s\n", items[idx]) // Example 2: Cancel on SIGINT/SIGTERM ctx2, cancel2 := context.WithCancel(context.Background()) defer cancel2() sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) go func() { <-sigChan fmt.Println("\nReceived interrupt signal, closing...") cancel2() }() idx, err = fuzzyfinder.Find( items, func(i int) string { return items[i] }, fuzzyfinder.WithContext(ctx2), ) if err != nil { if err == context.Canceled { log.Fatal("Selection cancelled by signal") } log.Fatal(err) } fmt.Printf("Selected: %s\n", items[idx]) } ``` ### WithCursorPosition - Set initial cursor location Controls whether cursor starts at bottom (default) or top of the filtered list. CursorPositionTop combined with descending-sorted lists creates "most recent first" selection experience. Overridden by WithPreselected when preset items exist. ```go package main import ( "fmt" "log" "sort" "time" fuzzyfinder "github.com/ktr0731/go-fuzzyfinder" ) type Commit struct { Hash string Date time.Time Author string Message string } func main() { commits := []Commit{ {"a1b2c3d", time.Now().Add(-24 * time.Hour), "alice", "Fix bug in parser"}, {"e4f5g6h", time.Now().Add(-12 * time.Hour), "bob", "Add new feature"}, {"i7j8k9l", time.Now().Add(-6 * time.Hour), "carol", "Update docs"}, {"m1n2o3p", time.Now().Add(-2 * time.Hour), "dave", "Refactor middleware"}, {"q4r5s6t", time.Now().Add(-1 * time.Hour), "eve", "Bump dependencies"}, } // Sort by date descending (newest first) sort.Slice(commits, func(i, j int) bool { return commits[i].Date.After(commits[j].Date) }) idx, err := fuzzyfinder.Find( commits, func(i int) string { return fmt.Sprintf("%s - %s", commits[i].Hash[:7], commits[i].Message) }, fuzzyfinder.WithCursorPosition(fuzzyfinder.CursorPositionTop), fuzzyfinder.WithPreviewWindow(func(i, w, h int) string { if i == -1 { return "" } return fmt.Sprintf( "Commit: %s\n"+ "Author: %s\n"+ "Date: %s\n"+ "Message: %s", commits[i].Hash, commits[i].Author, commits[i].Date.Format("2006-01-02 15:04:05"), commits[i].Message, ) }), ) if err != nil { log.Fatal(err) } fmt.Printf("Selected commit: %s by %s\n", commits[idx].Hash, commits[idx].Author) } ``` ### WithHeader - Display static header text Shows a colored header line above the item list. Useful for displaying instructions, current context, filter criteria, or item count summaries. Text is truncated with "..." if it exceeds terminal width. Rendered in green color. ```go package main import ( "fmt" "log" fuzzyfinder "github.com/ktr0731/go-fuzzyfinder" ) type Server struct { Name string Status string Region string } func main() { servers := []Server{ {"web-01", "running", "us-east-1"}, {"web-02", "running", "us-east-1"}, {"db-01", "stopped", "us-west-2"}, {"cache-01", "running", "eu-west-1"}, {"worker-01", "running", "ap-south-1"}, } runningCount := 0 for _, s := range servers { if s.Status == "running" { runningCount++ } } idxs, err := fuzzyfinder.FindMulti( servers, func(i int) string { return fmt.Sprintf("[%s] %s (%s)", servers[i].Status, servers[i].Name, servers[i].Region) }, fuzzyfinder.WithHeader(fmt.Sprintf("Servers: %d running, %d total | Select servers to restart", runningCount, len(servers))), fuzzyfinder.WithPreviewWindow(func(i, w, h int) string { if i == -1 { return "" } return fmt.Sprintf( "Server: %s\n"+ "Status: %s\n"+ "Region: %s\n\n"+ "Use Tab to select multiple servers", servers[i].Name, servers[i].Status, servers[i].Region, ) }), ) if err != nil { log.Fatal(err) } fmt.Println("Restarting servers:") for _, idx := range idxs { fmt.Printf(" - %s (%s)\n", servers[idx].Name, servers[idx].Region) } } ``` ### WithPromptString - Customize prompt indicator Changes the prompt symbol displayed before user input. Default is "> ". Useful for matching brand style, indicating special modes, or improving visual hierarchy. Common patterns: "$ ", "? ", "→ ", or "Search: ". ```go package main import ( "fmt" "log" fuzzyfinder "github.com/ktr0731/go-fuzzyfinder" ) func main() { commands := []string{ "git status", "git log", "git diff", "git add .", "git commit -m", "git push origin main", "git pull origin main", "git checkout -b", } idx, err := fuzzyfinder.Find( commands, func(i int) string { return commands[i] }, fuzzyfinder.WithPromptString("$ "), fuzzyfinder.WithHeader("Git Command Selector"), ) if err != nil { log.Fatal(err) } fmt.Printf("Execute: %s\n", commands[idx]) } ``` ### WithSelectOne - Auto-select when single match Automatically returns the item without user interaction if exactly one item exists or matches the initial query. Useful for implementing smart shortcuts: if search is unambiguous, proceed immediately; otherwise show picker. Significantly improves UX in automation-friendly CLIs. ```go package main import ( "fmt" "log" "os" fuzzyfinder "github.com/ktr0731/go-fuzzyfinder" ) type Environment struct { Name string URL string } func main() { envs := []Environment{ {"development", "http://localhost:3000"}, {"staging", "https://staging.example.com"}, {"production", "https://example.com"}, } // User provided partial input via CLI flag userQuery := os.Getenv("ENV_QUERY") // e.g., "prod" idx, err := fuzzyfinder.Find( envs, func(i int) string { return envs[i].Name }, fuzzyfinder.WithQuery(userQuery), fuzzyfinder.WithSelectOne(), // Auto-select if only "production" matches "prod" fuzzyfinder.WithPreviewWindow(func(i, w, h int) string { if i == -1 { return "" } return fmt.Sprintf("Environment: %s\nURL: %s", envs[i].Name, envs[i].URL) }), ) if err != nil { log.Fatal(err) } fmt.Printf("Deploying to: %s (%s)\n", envs[idx].Name, envs[idx].URL) } ``` ## Summary go-fuzzyfinder enables building interactive terminal applications with rich selection interfaces. The library is ideal for CLI tools requiring user choice from dynamic lists: deployment targets, log filtering, task selection, file browsing, or configuration management. It eliminates the need for external fuzzy-finder binaries while providing full programmatic control over displayed data and selection behavior. Common integration patterns include wrapping Find/FindMulti in application-specific functions that add domain logic (filtering, sorting, validation), combining with cobra/cli frameworks for command hierarchies, using hot-reload for monitoring dashboards, and leveraging preview windows to display detailed entity information. The library handles all terminal complexity, letting developers focus on application logic rather than TUI implementation details.