forked from deanishe/awgo
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
26 changed files
with
658 additions
and
4,746 deletions.
There are no files selected for viewing
2 changes: 1 addition & 1 deletion
2
examples/fuzzy-custom/.gitignore → examples/bookmarks/.gitignore
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
# ignore binary | ||
/fuzzy-custom | ||
/bookmarks |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
// | ||
// Copyright (c) 2017 Dean Jackson <[email protected]> | ||
// | ||
// MIT Licence. See http://opensource.org/licenses/MIT | ||
// | ||
// Created on 2017-09-17 | ||
// | ||
|
||
package main | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"net/url" | ||
"os" | ||
|
||
"github.com/DHowett/go-plist" | ||
"github.com/deanishe/awgo/fuzzy" | ||
) | ||
|
||
var ( | ||
// Where Safari stores its bookmarks | ||
bookmarksPath = os.ExpandEnv("$HOME/Library/Safari/Bookmarks.plist") | ||
) | ||
|
||
// Bookmarks is a slice of Bookmark structs that implements fuzzy.Interface. | ||
type Bookmarks []*Bookmark | ||
|
||
// Implement sort.Interface. | ||
func (b Bookmarks) Len() int { return len(b) } | ||
func (b Bookmarks) Swap(i, j int) { b[i], b[j] = b[j], b[i] } | ||
func (b Bookmarks) Less(i, j int) bool { return b[i].Title < b[j].Title } | ||
|
||
// SortKey implements fuzzy.Interface. | ||
func (b Bookmarks) SortKey(i int) string { | ||
return fmt.Sprintf("%s %s", b[i].Title, b[i].Domain) | ||
} | ||
|
||
// Filter filters bookmarks against query using a fuzzy.Sorter. | ||
func (b Bookmarks) Filter(query string) Bookmarks { | ||
hits := b[:0] | ||
srt := fuzzy.New(b) | ||
res := srt.Sort(query) | ||
for i, r := range res { | ||
if !r.Match { | ||
continue | ||
} | ||
hits = append(hits, b[i]) | ||
} | ||
return hits | ||
} | ||
|
||
// Bookmark is a Safari bookmark. | ||
type Bookmark struct { | ||
Title string // Bookmark title | ||
Domain string // Domain of URL | ||
URL string // Bookmark URL | ||
} | ||
|
||
// entry is an node in Safari's Bookmarks.plist file. This struct | ||
// matches all types of nodes in the file. | ||
type entry struct { | ||
Title string `plist:"Title"` | ||
Type string `plist:"WebBookmarkType"` | ||
URL string `plist:"URLString"` | ||
UUID string `plist:"WebBookmarkUUID"` | ||
URIDict map[string]string `plist:"URIDictionary"` | ||
Children []*entry `plist:"Children"` | ||
} | ||
|
||
// loadBookmarks parses the Safari bookmarks file. | ||
func loadBookmarks() (Bookmarks, error) { | ||
file, err := os.Open(bookmarksPath) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
root := entry{} | ||
entries := []*entry{} | ||
dec := plist.NewDecoder(file) | ||
if err := dec.Decode(&root); err != nil { | ||
return nil, err | ||
} | ||
|
||
// Recursively parse tree. The children of root are the top-level folders. | ||
for _, e := range root.Children { | ||
if e.Title == "BookmarksBar" || e.Title == "BookmarksMenu" { | ||
entries = append(entries, extractLeafEntries(e)...) | ||
} | ||
} | ||
|
||
// Convert raw entries to bookmarks | ||
bkm := []*Bookmark{} | ||
// Ignore dupes | ||
seen := map[string]bool{} | ||
|
||
for _, e := range entries { | ||
// Use title & URL for dupe key, as you probably want bookmarks | ||
// with different titles to stil be shown, even if the URL is | ||
// a duplicate | ||
key := e.Title + e.URL | ||
if seen[key] == true { | ||
continue | ||
} | ||
|
||
// Convert entry to Bookmark | ||
var title string | ||
if e.Title != "" { | ||
title = e.Title | ||
} else { | ||
title = e.URIDict["title"] | ||
} | ||
u, err := url.Parse(e.URL) | ||
if err != nil { | ||
log.Printf("couldn't parse URL \"%s\" (%s): %v", e.URL, title, err) | ||
continue | ||
} | ||
|
||
seen[key] = true | ||
bkm = append(bkm, &Bookmark{Title: title, Domain: u.Host, URL: e.URL}) | ||
} | ||
|
||
return bkm, nil | ||
} | ||
|
||
// extractLeafEntries recursively finds all bookmarks under root. | ||
func extractLeafEntries(root *entry) []*entry { | ||
entries := []*entry{} | ||
for _, e := range root.Children { | ||
// Bookmarks have type "WebBookmarkTypeLeaf" | ||
if e.Type == "WebBookmarkTypeLeaf" { | ||
entries = append(entries, e) | ||
} else if len(e.Children) > 0 { // Recursively add children | ||
entries = append(entries, extractLeafEntries(e)...) | ||
} | ||
} | ||
return entries | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
// | ||
// Copyright (c) 2017 Dean Jackson <[email protected]> | ||
// | ||
// MIT Licence. See http://opensource.org/licenses/MIT | ||
// | ||
// Created on 2017-09-09 | ||
// | ||
|
||
/* | ||
Workflow bookmarks demonstrates implementing fuzzy.Interface. | ||
It loads your Safari bookmarks from ~/Library/Safari/Bookmarks.plist | ||
into the Bookmarks struct, which implements fuzzy.Interface and a | ||
Filter() method, which returns another Bookmarks struct containing | ||
all bookmarks that match the given query. | ||
See bookmarks.go for the implementation. | ||
*/ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
|
||
"github.com/deanishe/awgo" | ||
"github.com/docopt/docopt-go" | ||
) | ||
|
||
var ( | ||
helpURL = "http://www.deanishe.net" | ||
maxResults = 200 | ||
wf *aw.Workflow | ||
icon = &aw.Icon{ // Icon for bookmark filetype | ||
Value: "com.apple.safari.bookmark", | ||
Type: aw.IconTypeFileType, | ||
} | ||
) | ||
|
||
var ( | ||
usage = `bookmarks [options] [<query>] | ||
Usage: | ||
bookmarks <query> | ||
bookmarks -h|--version | ||
Options: | ||
-h, --help Show this message and exit | ||
--version Show version number and exit | ||
` | ||
) | ||
|
||
func init() { | ||
wf = aw.New(aw.HelpURL(helpURL), aw.MaxResults(maxResults)) | ||
} | ||
|
||
func run() { | ||
var query string | ||
|
||
// Parse command-line arguments | ||
args, err := docopt.Parse(usage, wf.Args(), true, wf.Version(), false) | ||
if err != nil { | ||
wf.Fatal(fmt.Sprintf("couldn't parse CLI flags: %v", err)) | ||
} | ||
|
||
if s, ok := args["<query>"].(string); ok { | ||
query = s | ||
} | ||
log.Printf("[main] query=%s", query) | ||
|
||
// Load bookmarks | ||
bookmarks, err := loadBookmarks() | ||
if err != nil { | ||
wf.FatalError(err) | ||
} | ||
log.Printf("%d total bookmark(s)", len(bookmarks)) | ||
|
||
// Filter bookmarks based on user query | ||
if query != "" { | ||
bookmarks = bookmarks.Filter(query) | ||
} | ||
|
||
// Generate results for Alfred | ||
for _, b := range bookmarks { | ||
wf.NewItem(b.Title). | ||
Subtitle(b.URL). | ||
Arg(b.URL). | ||
UID(b.Title + b.URL). | ||
Icon(icon). | ||
Valid(true) | ||
} | ||
|
||
wf.WarnEmpty("No matching bookmarks", "Try a different query?") | ||
wf.SendFeedback() | ||
} | ||
|
||
func main() { | ||
wf.Run(run) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.