From 91548853b7163a9744dda4c75c062fe6eb57be4d Mon Sep 17 00:00:00 2001 From: Victor Häggqvist Date: Tue, 2 Feb 2016 03:13:47 +0100 Subject: init --- Godeps/Godeps.json | 20 ++++++ Godeps/Readme | 5 ++ README.md | 39 +++++++++++ config.ini.default | 3 + goboom.go | 196 +++++++++++++++++++++++++++++++++++++++++++++++++++++ goboom_run | 2 + goboom_run_debug | 2 + 7 files changed, 267 insertions(+) create mode 100644 Godeps/Godeps.json create mode 100644 Godeps/Readme create mode 100644 README.md create mode 100644 config.ini.default create mode 100644 goboom.go create mode 100755 goboom_run create mode 100755 goboom_run_debug diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json new file mode 100644 index 0000000..7a10d89 --- /dev/null +++ b/Godeps/Godeps.json @@ -0,0 +1,20 @@ +{ + "ImportPath": "snilius.com/goboom", + "GoVersion": "go1.5.3", + "Deps": [ + { + "ImportPath": "github.com/go-ini/ini", + "Comment": "v1.8.6", + "Rev": "afbd495e5aaea13597b5e14fe514ddeaa4d76fc3" + }, + { + "ImportPath": "github.com/gocarina/gocsv", + "Rev": "c9648aef1bdc65989a5db138c92442e3b1370ecc" + }, + { + "ImportPath": "github.com/ogier/pflag", + "Comment": "v0.0.1-7-g45c278a", + "Rev": "45c278ab3607870051a2ea9040bb85fcb8557481" + } + ] +} diff --git a/Godeps/Readme b/Godeps/Readme new file mode 100644 index 0000000..4cdaa53 --- /dev/null +++ b/Godeps/Readme @@ -0,0 +1,5 @@ +This directory tree is generated automatically by godep. + +Please do not edit. + +See https://github.com/tools/godep for more information. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c937601 --- /dev/null +++ b/README.md @@ -0,0 +1,39 @@ +# goboom +goboom is wrapper around the dmenu. goboom is the successor and rewrite of xboomx in Go. + +goboom sorts commands to launch according to their launch frequency. +In other words - if you launch Chromium and KeePassX all the time - they will appear in the list of commands first. + +## Building +goboom is build using Go 1.5 vendoring and godeps. + + export GO15VENDOREXPERIMENT=1 + godep get github.com/victorhaggqvist/goboom + go build goboom.go + +## Install + +```sh +sudo cp goboom /usr/bin +sudo cp goboom_run /usr/bin +mkdir -p ~/.goboom +cp config.ini.default ~/.goboom/config.ini +``` + +### Migration from xboomx +goboom will look for a config file in `~/.goboom`. +You will need to will need to convert you old config file to ini-format and name it `config.ini`, see the bundeled default ini-file for guidence. + +goboom uses a csv-file instead sqlite as datastore. You will need to export your xboomx db as so. At this point there is not a provided tool to do so, but you can easely export it with something like [sqlitebrowser](http://sqlitebrowser.org/). + +The contents of your exported database should look along the lines of this. + + name,count + chroimum,13 + keepassx,17 + gimp,4 + + +## License + +GPL v3 diff --git a/config.ini.default b/config.ini.default new file mode 100644 index 0000000..e00c592 --- /dev/null +++ b/config.ini.default @@ -0,0 +1,3 @@ +dmenu_params = -b -i -nb black -nf orange -sb black -p ">" +ignore = X,su + diff --git a/goboom.go b/goboom.go new file mode 100644 index 0000000..1948534 --- /dev/null +++ b/goboom.go @@ -0,0 +1,196 @@ +package main + +import ( + "bufio" + "fmt" + "io/ioutil" + "os" + "os/user" + "path/filepath" + "runtime" + "sort" + "strings" + + "github.com/go-ini/ini" + "github.com/gocarina/gocsv" + flag "github.com/ogier/pflag" +) + +const version = 0 + +type Runnable struct { + Cmd string `csv:"name"` + Count int `csv:"count"` +} + +type Config struct { + DmenuParams string + Ignore []string `delim:","` +} + +type CmdList []*Runnable + +func (c CmdList) Len() int { + return len(c) +} + +func (c CmdList) Swap(i, j int) { + c[i], c[j] = c[j], c[i] +} + +func (c CmdList) Less(i, j int) bool { + return c[i].Count > c[j].Count +} + +var ( + prePhase bool + postPhase bool + launcher bool + dbFilePath string + config Config +) + +func isInIgnoreList(item string) bool { + for _, i := range config.Ignore { + if i == item { + return true + } + } + return false +} + +func addIfNotContains(items []string, item string) []string { + for _, i := range items { + if i == item { + return items + } + } + return append(items, item) +} + +func openDB() map[string]int { + list := CmdList{} + file, _ := os.Open(dbFilePath) + if err := gocsv.UnmarshalFile(file, &list); err != nil { + panic(err) + } + + itemSet := make(map[string]int) + for _, item := range list { + itemSet[item.Cmd] = item.Count + } + return itemSet +} + +func generatePath() []string { + sysPath := os.Getenv("PATH") + paths := strings.Split(sysPath, ":") + + pathItems := sort.StringSlice{} + + for _, p := range paths { + if _, err := os.Stat(p); err == nil { + files, _ := ioutil.ReadDir(p) + for _, f := range files { + if !isInIgnoreList(f.Name()) { + pathItems = addIfNotContains(pathItems, f.Name()) + } + } + } + } + + sort.Sort(pathItems) + return pathItems +} + +func rankPath(pathItems []string) CmdList { + itemSet := openDB() + + rankedItems := CmdList{} + for _, item := range pathItems { + count, exists := itemSet[item] + if !exists { + count = 0 + } + rankedItems = append(rankedItems, &Runnable{item, count}) + } + + sort.Sort(rankedItems) + return rankedItems +} + +func loadIni() { + usr, _ := user.Current() + configPath := filepath.Join(usr.HomeDir, ".goboom") + iniFile := filepath.Join(configPath, "config.ini") + dbFilePath = filepath.Join(configPath, "rankdb.csv") + + if _, err := os.Stat(configPath); err != nil { + err := os.Mkdir(configPath, os.ModePerm) + if err != nil { + fmt.Println("failed to create config dir") + } + } + + config = Config{} + if err := ini.MapToWithMapper(&config, ini.TitleUnderscore, iniFile); err != nil { + panic(err) + } +} + +func updateRank(runnable string) { + itemSet := openDB() + + count, exists := itemSet[runnable] + if !exists { + itemSet[runnable] = 1 + } else { + itemSet[runnable] = count + 1 + } + + writeDB(itemSet) +} + +func writeDB(itemSet map[string]int) { + items := CmdList{} + for runnable, count := range itemSet { + items = append(items, &Runnable{runnable, count}) + } + + file, err := os.OpenFile(dbFilePath, os.O_RDWR|os.O_CREATE, os.ModePerm) + if err != nil { + panic(err) + } + if err = gocsv.MarshalFile(&items, file); err != nil { + panic(err) + } +} + +func main() { + flag.BoolVar(&prePhase, "pre", false, "Generate dmenu in") + flag.BoolVar(&launcher, "launcher", false, "Output launcher command") + flag.BoolVar(&postPhase, "post", false, "Update ranking DB") + flag.Parse() + + loadIni() + + if prePhase { + pathList := generatePath() + sortedList := rankPath(pathList) + for _, item := range sortedList { + fmt.Println(item.Cmd) + } + } else if postPhase { + scanner := bufio.NewScanner(os.Stdin) + scanner.Scan() + input := scanner.Text() + updateRank(input) + fmt.Print(input) + } else if launcher { + fmt.Print("dmenu " + config.DmenuParams) + } else { + fmt.Printf("goboom v%d (%s/%s/%s)\n", version, runtime.GOOS, runtime.GOARCH, runtime.Version()) + fmt.Println("\nTo actually use goboom execute goboom_run\n") + flag.Usage() + } +} diff --git a/goboom_run b/goboom_run new file mode 100755 index 0000000..6958843 --- /dev/null +++ b/goboom_run @@ -0,0 +1,2 @@ +#!/bin/sh +goboom --pre | eval $(goboom --launcher) | goboom --post | xargs -I {} sh -c 'exec {} &' diff --git a/goboom_run_debug b/goboom_run_debug new file mode 100755 index 0000000..b5bad4f --- /dev/null +++ b/goboom_run_debug @@ -0,0 +1,2 @@ +#!/bin/sh +./goboom --pre | eval $(./goboom --launcher) | ./goboom --post | xargs -I {} sh -c 'exec {} &' -- cgit v1.2.3