From 7b977b3f75f477c8431a520beb23b7c5233113e4 Mon Sep 17 00:00:00 2001 From: Radhi Fadlillah Date: Wed, 31 Jan 2018 10:59:27 +0700 Subject: [PATCH] Add command for opening bookmarks --- cmd/open.go | 111 +++++++++++++++++++++++++++++++++++++++++++ cmd/utils.go | 7 +++ database/database.go | 1 + database/sqlite.go | 57 ++++++++++++++++++++++ 4 files changed, 176 insertions(+) create mode 100644 cmd/open.go diff --git a/cmd/open.go b/cmd/open.go new file mode 100644 index 00000000..6b3e73ed --- /dev/null +++ b/cmd/open.go @@ -0,0 +1,111 @@ +package cmd + +import ( + "fmt" + "github.com/spf13/cobra" + "os/exec" + "strings" +) + +var ( + openCmd = &cobra.Command{ + Use: "open [indices]", + Short: "Open the saved bookmarks.", + Long: "Open bookmarks in browser. " + + "Accepts space-separated list of indices (e.g. 5 6 23 4 110 45), hyphenated range (e.g. 100-200) or both (e.g. 1-3 7 9). " + + "If no arguments, ALL bookmarks will be opened.", + Run: func(cmd *cobra.Command, args []string) { + // Read flags + cacheOnly, _ := cmd.Flags().GetBool("cache") + trimSpace, _ := cmd.Flags().GetBool("trim-space") + skipConfirmation, _ := cmd.Flags().GetBool("yes") + + // If no arguments, confirm to user + if len(args) == 0 && !skipConfirmation { + confirmOpen := "" + fmt.Print("Open ALL bookmarks? (y/n): ") + fmt.Scanln(&confirmOpen) + + if confirmOpen != "y" { + return + } + } + + if cacheOnly { + openBookmarksCache(trimSpace, args...) + } else { + openBookmarks(args...) + } + }, + } +) + +func init() { + openCmd.Flags().BoolP("yes", "y", false, "Skip confirmation prompt and open ALL bookmarks") + openCmd.Flags().BoolP("cache", "c", false, "Open the bookmark's cache in text-only mode") + openCmd.Flags().Bool("trim-space", false, "Trim all spaces and newlines from the bookmark's cache") + rootCmd.AddCommand(openCmd) +} + +func openBookmarks(args ...string) { + // Read bookmarks from database + bookmarks, err := DB.GetBookmarks(args...) + if err != nil { + cError.Println(err) + return + } + + if len(bookmarks) == 0 { + if len(args) > 0 { + cError.Println("No matching index found") + } else { + cError.Println("No saved bookmarks yet") + } + return + } + + // Open in browser + for _, book := range bookmarks { + exec.Command("xdg-open", book.URL).Run() + if err != nil { + cError.Printf("Failed to open %s: %v\n", book.URL, err) + } + } +} + +func openBookmarksCache(trimSpace bool, args ...string) { + // Read bookmark content from database + bookmarks, err := DB.GetBookmarksContent(args...) + if err != nil { + cError.Println(err) + return + } + + // Get terminal width + termWidth := getTerminalWidth() + if termWidth < 50 { + termWidth = 50 + } + + // Show bookmarks content + for _, book := range bookmarks { + if trimSpace { + words := strings.Fields(book.Content) + book.Content = strings.Join(words, " ") + } + + cIndex.Printf("%d. ", book.ID) + cTitle.Println(book.Title) + fmt.Println() + + if book.Content == "" { + cError.Println("This bookmark doesn't have any cached content") + } else { + fmt.Println(book.Content) + } + + fmt.Println() + cSymbol.Println(strings.Repeat("-", termWidth)) + fmt.Println() + } +} diff --git a/cmd/utils.go b/cmd/utils.go index dbeea222..97d8f2c8 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -2,6 +2,8 @@ package cmd import ( "github.com/fatih/color" + "golang.org/x/crypto/ssh/terminal" + "os" ) var ( @@ -14,3 +16,8 @@ var ( cExcerpt = color.New(color.FgHiWhite) cTag = color.New(color.FgHiBlue) ) + +func getTerminalWidth() int { + width, _, _ := terminal.GetSize(int(os.Stdin.Fd())) + return width +} diff --git a/database/database.go b/database/database.go index f2ba3ac9..b0e46eae 100644 --- a/database/database.go +++ b/database/database.go @@ -10,6 +10,7 @@ type Database interface { GetBookmarks(indices ...string) ([]model.Bookmark, error) DeleteBookmarks(indices ...string) ([]int, []int, error) SearchBookmarks(keyword string, tags ...string) ([]model.Bookmark, error) + GetBookmarksContent(indices ...string) ([]model.Bookmark, error) } func checkError(err error) { diff --git a/database/sqlite.go b/database/sqlite.go index a2929ea7..bf27c47d 100644 --- a/database/sqlite.go +++ b/database/sqlite.go @@ -429,3 +429,60 @@ func (db *SQLiteDatabase) SearchBookmarks(keyword string, tags ...string) ([]mod return bookmarks, nil } + +func (db *SQLiteDatabase) GetBookmarksContent(indices ...string) ([]model.Bookmark, error) { + // Convert list of index to int + listIndex := []int{} + errInvalidIndex := fmt.Errorf("Index is not valid") + + for _, strIndex := range indices { + if strings.Contains(strIndex, "-") { + parts := strings.Split(strIndex, "-") + if len(parts) != 2 { + return nil, errInvalidIndex + } + + minIndex, errMin := strconv.Atoi(parts[0]) + maxIndex, errMax := strconv.Atoi(parts[1]) + if errMin != nil || errMax != nil || minIndex < 1 || minIndex > maxIndex { + return nil, errInvalidIndex + } + + for i := minIndex; i <= maxIndex; i++ { + listIndex = append(listIndex, i) + } + } else { + index, err := strconv.Atoi(strIndex) + if err != nil || index < 1 { + return nil, errInvalidIndex + } + + listIndex = append(listIndex, index) + } + } + + // Prepare where clause + args := []interface{}{} + whereClause := " WHERE 1" + + if len(listIndex) > 0 { + whereClause = " WHERE docid IN (" + for _, idx := range listIndex { + args = append(args, idx) + whereClause += "?," + } + + whereClause = whereClause[:len(whereClause)-1] + whereClause += ")" + } + + bookmarks := []model.Bookmark{} + err := db.Select(&bookmarks, + `SELECT docid id, title, content, html + FROM bookmark_content`+whereClause, args...) + if err != nil && err != sql.ErrNoRows { + return nil, err + } + + return bookmarks, nil +}