diff --git a/main.go b/main.go index 076a4b7..917149d 100644 --- a/main.go +++ b/main.go @@ -17,35 +17,31 @@ const ( Commands ) -type sessionOrCommand struct { +type commandChoice struct { displayString string command *exec.Cmd } -type Choices [][]sessionOrCommand - -type SelectionSet map[int]struct{} +type Choices []commandChoice type model struct { - activeSection int - cursor []int - choices Choices - selected []SelectionSet - status string + cursor int + choices Choices + selected map[int]struct{} + status string } type statusMsg string -var browserSelection SelectionSet -var commandSelection SelectionSet +var selection map[int]struct{} -func getBrowserSessions() []sessionOrCommand { +func getBrowserSessions() []commandChoice { // 1. List files in $XDG_DATA_HOME/qutebrowser/sessions/ (N.B.: // UserConfigDir() in os) userConfigDir, err := os.UserConfigDir() if err != nil { log.Printf("Error finding user configuration directory: %v", err) - return []sessionOrCommand{} + return []commandChoice{} } log.Printf("INFO userConfigDir: %+v", userConfigDir) fileSystem := os.DirFS(userConfigDir) @@ -53,17 +49,17 @@ func getBrowserSessions() []sessionOrCommand { fileList, err := fs.ReadDir(fileSystem, "local/share/qutebrowser/sessions") if err != nil { log.Printf("Error reading browser sessions directory: %v", err) - return []sessionOrCommand{} + return []commandChoice{} } // 2. Exclude non-YAML files // 3. Wrangle them into this struct array - result := make([]sessionOrCommand, 0) + result := make([]commandChoice, 0) for _, entry := range fileList { if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".yml") { log.Printf("INFO %s", entry.Name()) - result = append(result, sessionOrCommand{ - displayString: strings.TrimSuffix(entry.Name(), ".yml"), - command: exec.Command("qutebrowser", "--restore", strings.TrimSuffix(entry.Name(), ".yml")), + result = append(result, commandChoice{ + displayString: fmt.Sprintf("Qutebrowser session: %s", strings.TrimSuffix(entry.Name(), ".yml")), + command: exec.Command("qutebrowser", "--target", "window", "--restore", strings.TrimSuffix(entry.Name(), ".yml")), }) } } @@ -72,51 +68,54 @@ func getBrowserSessions() []sessionOrCommand { } func initialModel() model { - browserSelection = make(SelectionSet) - commandSelection = make(SelectionSet) return model{ - activeSection: BrowserSessions, - cursor: []int{0, 0}, - choices: Choices{ + cursor: 0, + choices: append( getBrowserSessions(), - []sessionOrCommand{{ + commandChoice{ displayString: "bottom", command: exec.Command("xterm", "-maximized", "-e", "btm", "--group", "--battery", "--color", "gruvbox-light"), - }, { + }, + commandChoice{ displayString: "broot", command: exec.Command("xterm", "-maximized", "-e", "broot"), - }, { + }, + commandChoice{ displayString: "joplin", command: exec.Command("xterm", "-maximized", "-e", "joplin"), - }, { + }, + commandChoice{ displayString: "neomutt", command: exec.Command("xterm", "-maximized", "-e", "neomutt"), - }, { + }, + commandChoice{ displayString: "newsboat", command: exec.Command("xterm", "-maximized", "-e", "newsboat"), - }}, - }, - // An array of maps which indicates which choices are selected. - // We're using the map like a mathematical set. The keys refer to - // the indexes of the `choices` slice, above. - selected: []SelectionSet{browserSelection, commandSelection}, + }, + ), + selected: make(map[int]struct{}), } } func launch(m model) tea.Cmd { return func() tea.Msg { var result statusMsg - for p := 0; p < 2; p++ { - for q, item := range m.choices[p] { - if _, ok := m.selected[p][q]; ok { - log.Printf("INFO launching: %v\n", item.displayString) - result += statusMsg(fmt.Sprintf("Launching command: %v\n", item.displayString)) - err := item.command.Start() + for q, item := range m.choices { + if _, ok := m.selected[q]; ok { + log.Printf("INFO launching: %v\n", item.displayString) + result += statusMsg(fmt.Sprintf("Launching command: %v\n", item.displayString)) + err := item.command.Start() + if err != nil { + result += statusMsg(fmt.Sprintf("%v\n", err)) + log.Fatalf("Error launching: %v\n", err) + } + /* + err = item.command.Wait() if err != nil { result += statusMsg(fmt.Sprintf("%v\n", err)) - log.Fatalf("Error launching: %v\n", err) + log.Fatalf("Error during wait: %v\n", err) } - } + */ } } @@ -137,26 +136,20 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg.String() { case "ctrl+c", "q": return m, tea.Quit - case "tab": - if m.activeSection == BrowserSessions { - m.activeSection = Commands - } else { - m.activeSection = BrowserSessions - } case "up", "k": - if m.cursor[m.activeSection] > 0 { - m.cursor[m.activeSection]-- + if m.cursor > 0 { + m.cursor-- } case "down", "j": - if m.cursor[m.activeSection] < len(m.choices[m.activeSection])-1 { - m.cursor[m.activeSection]++ + if m.cursor < len(m.choices)-1 { + m.cursor++ } case " ": - _, ok := m.selected[m.activeSection][m.cursor[m.activeSection]] + _, ok := m.selected[m.cursor] if ok { - delete(m.selected[m.activeSection], m.cursor[m.activeSection]) + delete(m.selected, m.cursor) } else { - m.selected[m.activeSection][m.cursor[m.activeSection]] = struct{}{} + m.selected[m.cursor] = struct{}{} } case "enter": return m, launch(m) @@ -167,24 +160,22 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } func (m model) View() string { - s := "Let's get started!\n\n" + s := "\nLet's get started!\n\n" - for j := 0; j < 2; j++ { - for i, choice := range m.choices[j] { - cursor := " " - if m.cursor[j] == i { - cursor = ">" - } - - checked := " " - if _, ok := m.selected[j][i]; ok { - checked = "x" - } - - s += fmt.Sprintf("%s [%s] %s\n", cursor, checked, choice.displayString) + for i, choice := range m.choices { + cursor := " " + if m.cursor == i { + cursor = ">" } - s += "\n\n" + + checked := " " + if _, ok := m.selected[i]; ok { + checked = "x" + } + + s += fmt.Sprintf("%s [%s] %s\n", cursor, checked, choice.displayString) } + s += "\n\n" // s += fmt.Sprintf("\n%+v", m.selected) // debug s += fmt.Sprintf("%s\n", m.status)