Skip to content

Instantly share code, notes, and snippets.

@bechampion
Created June 3, 2024 12:59
Show Gist options
  • Save bechampion/d12ee2f65c5339f3cabdfab09532984d to your computer and use it in GitHub Desktop.
Save bechampion/d12ee2f65c5339f3cabdfab09532984d to your computer and use it in GitHub Desktop.
package main
import (
"fmt"
"math/rand"
"os"
"strconv"
"strings"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/google/uuid"
"github.com/sahilm/fuzzy"
)
var (
left = [...]string{
"admiring", "adoring", "affectionate", "agitated", "amazing", "angry", "awesome", "beautiful",
"blissful", "bold", "boring", "brave", "busy", "charming", "clever", "compassionate", "competent",
"condescending", "confident", "cool", "cranky", "crazy", "dazzling", "determined", "distracted",
"dreamy", "eager", "ecstatic", "elastic", "elated", "elegant", "eloquent", "epic", "exciting",
"fervent", "festive", "flamboyant", "focused", "friendly", "frosty", "funny", "gallant", "gifted",
"goofy", "gracious", "great", "happy", "hardcore", "heuristic", "hopeful", "hungry", "infallible",
"inspiring", "intelligent", "interesting", "jolly", "jovial", "keen", "kind", "laughing", "loving",
"lucid", "magical", "modest", "musing", "mystifying", "naughty", "nervous", "nice", "nifty",
"nostalgic", "objective", "optimistic", "peaceful", "pedantic", "pensive", "practical", "priceless",
"quirky", "quizzical", "recursing", "relaxed", "reverent", "romantic", "sad", "serene", "sharp",
"silly", "sleepy", "stoic", "strange", "stupefied", "suspicious", "sweet", "tender", "thirsty",
"trusting", "unruffled", "upbeat", "vibrant", "vigilant", "vigorous", "wizardly", "wonderful",
"xenodochial", "youthful", "zealous", "zen",
}
right = [...]string{
"agnesi", "albattani", "allen", "almeida", "antonelli", "archimedes", "ardinghelli", "aryabhata",
"austin", "babbage", "banach", "banzai", "bardeen", "bartik", "bassi", "beaver", "bell", "benz",
"bhabha", "bhaskara", "black", "blackburn", "blackwell", "bohr", "booth", "borg", "bose", "bouman",
"boyd", "brahmagupta", "brattain", "brown", "buck", "burnell", "cannon", "carson", "cartwright",
"carver", "cerf", "chandrasekhar", "chaplygin", "chatelet", "chatterjee", "chaum", "chebyshev",
"clarke", "cohen", "colden", "cori", "cray", "curie", "lovelace", "lumiere", "mahavira", "margulis",
"maxwell", "mayer", "mccarthy", "mcclintock", "mclaren", "mclean", "mcnulty", "meitner", "mendel",
"mendeleev", "meninsky", "merkle", "mestorf", "mirzakhani", "montalcini", "moore", "morse", "moser",
"murdock", "napier", "nash", "neumann", "newton", "nightingale", "nobel", "noether", "northcutt",
"noyce", "panini", "pare", "pascal", "pasteur", "payne", "perlman", "pike", "poincare", "poitras",
"proskuriakova", "ptolemy", "raman", "ramanujan", "rhodes", "ride", "ritchie", "robinson", "roentgen",
"rosalind", "rubin", "saha", "sammet", "sanderson", "satoshi", "shamir", "shannon", "shaw", "shirley",
"shockley", "shtern", "sinoussi", "snyder", "solomon", "spence", "stonebraker", "sutherland", "swanson",
"swartz", "swirles", "taussig", "tesla", "tharp", "thompson", "torvalds", "tu", "turing", "varahamihira",
"vaughan", "villani", "visvesvaraya", "volhard", "wescoff", "wilbur", "wiles", "williams", "williamson",
"wilson", "wing", "wozniak", "wright", "wu", "yalow", "yonath", "zhukovsky",
}
)
func GetRandomName(retry int) string {
begin:
name := left[rand.Intn(len(left))] + "_" + right[rand.Intn(len(right))] //nolint:gosec // G404: Use of weak random number generator (math/rand instead of crypto/rand)
if name == "boring_wozniak" /* Steve Wozniak is not boring */ {
goto begin
}
if retry > 0 {
name += strconv.Itoa(rand.Intn(10)) //nolint:gosec // G404: Use of weak random number generator (math/rand instead of crypto/rand)
}
return name
}
func GiveMeNames(len int) []string {
var response []string
for i := 0; i < len; i++ {
response = append(response, GetRandomName(1))
}
return response
}
var dockerInstanceNames = GiveMeNames(1000)
type match struct {
text string
score int
}
type model struct {
input string
cursor int
suggestions []match
selected int
width int
height int
}
func initialModel() model {
return model{
input: "",
cursor: 0,
suggestions: getSuggestions(""),
selected: -1,
}
}
func getSuggestions(input string) []match {
var suggestions []match
if input == "" {
for _, name := range dockerInstanceNames {
suggestions = append(suggestions, match{text: name, score: 100})
}
return suggestions
}
matches := fuzzy.Find(input, dockerInstanceNames)
for _, matchx := range matches {
score := calculateMatchScore(matchx.MatchedIndexes, len(input), len(dockerInstanceNames[matchx.Index]))
suggestions = append(suggestions, match{text: dockerInstanceNames[matchx.Index], score: score})
}
return suggestions
}
func calculateMatchScore(matchedIndexes []int, inputLength, textLength int) int {
return len(matchedIndexes) * 100 / textLength
}
func (m model) Init() tea.Cmd {
return nil
}
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case "ctrl+c", "esc":
return m, tea.Quit
case "ctrl+l":
fmt.Print("\033[H\033[2J")
m.input = ""
case "ctrl+w":
m.input = deleteLastWord(m.input)
case "backspace":
if len(m.input) > 0 {
m.input = m.input[:len(m.input)-1]
}
case "enter":
return m, tea.Quit
case "up":
if m.selected > 0 {
m.selected--
}
case "ctrl+k":
if m.selected > 0 {
m.selected--
}
case "ctrl+j":
if m.selected < len(m.suggestions)-1 {
m.selected++
}
case "ctrl+u":
if m.selected > 0 {
m.selected--
}
case "ctrl+d":
if m.selected < len(m.suggestions)-1 {
m.selected++
}
case "down":
if m.selected < len(m.suggestions)-1 {
m.selected++
}
default:
m.input += msg.String()
}
m.suggestions = getSuggestions(m.input)
if m.selected >= len(m.suggestions) {
m.selected = len(m.suggestions) - 1
}
case tea.WindowSizeMsg:
m.width = msg.Width
m.height = msg.Height
}
return m, nil
}
func deleteLastWord(input string) string {
if len(input) == 0 {
return input
}
words := strings.Fields(input)
if len(words) == 0 {
return ""
}
return strings.Join(words[:len(words)-1], " ") + " "
}
func greenText(text string, index int) string {
colors := []lipgloss.Color{
lipgloss.Color("#006400"), // Dark Green
lipgloss.Color("#008000"), // Green
lipgloss.Color("#228B22"), // Forest Green
lipgloss.Color("#32CD32"), // Lime Green
lipgloss.Color("#3CB371"), // Medium Sea Green
lipgloss.Color("#66CDAA"), // Medium Aquamarine
lipgloss.Color("#7CFC00"), // Lawn Green
}
var coloredText strings.Builder
for i, char := range text {
color := colors[(index+i)%len(colors)]
coloredText.WriteString(lipgloss.NewStyle().Foreground(color).Render(string(char)))
}
return coloredText.String()
}
func (m model) View() string {
var b strings.Builder
// Center the input text on the screen
if m.input != "" {
screenWidth := m.width
padding := (screenWidth - len(m.input)) / 2
b.WriteString(strings.Repeat(" ", padding) + m.input + "\n\n")
}
// Use 80% of the screen height for the suggestions
listHeight := int(float64(m.height) * 0.8)
b.WriteString("Start typing: " + m.input + "\n\n")
if len(m.suggestions) > 0 {
for i, suggestion := range m.suggestions[:min(len(m.suggestions), listHeight)] {
cursor := " " // no cursor
if m.selected == i {
cursor = ">" // cursor
}
// Generate UUID for each suggestion
id := uuid.New()
suggestionWithID := fmt.Sprintf("%s %s", suggestion.text, id.String())
line := fmt.Sprintf("%s %3d%% %s", cursor, suggestion.score, greenText(suggestionWithID, i))
if m.selected == i {
line = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("205")).Background(lipgloss.Color("235")).Render(line) // Dark gray background
} else {
line = lipgloss.NewStyle().Render(line)
}
b.WriteString(line + "\n")
}
} else {
b.WriteString("No suggestions found.\n")
}
return b.String()
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
func main() {
p := tea.NewProgram(initialModel())
if err := p.Start(); err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(1)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment