Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: auto complete tags #20

Open
QiangF opened this issue May 12, 2017 · 7 comments
Open

Feature request: auto complete tags #20

QiangF opened this issue May 12, 2017 · 7 comments

Comments

@QiangF
Copy link

QiangF commented May 12, 2017

I use firefox's bookmark manager, it has the convenience of auto complete tags and make the tagging system more consistant, i.e. few typos. But manage bookmarks in firefox is too slow when the number of bookmarks grow. Auto complete tags will save typing in case of a very long tag as well.

@QiangF
Copy link
Author

QiangF commented May 12, 2017

Maybe tagging can be done in a tagging loop.
keep asking for a tag, get the user's input, print matches, use tab to autocomplete common part, quit adding tags with esc.

@QiangF
Copy link
Author

QiangF commented May 13, 2017

I have a skeleton here:

function listTags {
    buku -t
}

function formatTags {
}

function searchAndSelectTags {
    listTags |
    formatTags |
    searchAndAddAsYouType
}

The output of buku -t is:

 1. bookmark (1)
 2. hello (1)

The nuber in the beginning and at the end has to be removed.
searchAndAddAsYouType will continuously adding tags.

@AndreiUlmeyda
Copy link
Owner

Hi there!
Thanks for the suggestion. Unfortunately, I am terribly short on time at the moment since I'm dealing with a bit of a medical situation.

My original plan was to avoid adding features to this project because it is written in bash and because I did not find a satisfactory method for unit testing. But rather to port the project to haskell first and continue from a code base that is less likely to deteriorate while growing.

But since this will be considerably delayed as well, I think the feature should be added here.

And I will do so, when I find the time. That could be soon, or it could take months. I am sorry I can't be more precise at the moment :/

Until then

@QiangF
Copy link
Author

QiangF commented Dec 28, 2017

I am still working on the script, I think fzf is much better than peco, I am trying to hack askUserForTags with the following:

function searchAsYouType {
    # to accept the current typed string, press ctrl-m
    # to copy selected lines to clipboard, press ctrl-w
    if [[ "$forceMultilineSelection" == true ]]; then
        fzf --multi --cycle --bind 'tab:toggle-up,btab:toggle-down,ctrl-m:print-query,ctrl-w:execute-multi(echo {} | xclip -selection clipboard)+abort'
    else
        fzf --cycle --bind 'tab:toggle-up,btab:toggle-down,ctrl-m:print-query,ctrl-y:execute(echo {} | xclip -selection clipboard)+abort'
    fi
}

function searchTagAsYouType {
    # display a header on top of all candidates
    fzf --multi --cycle --header "$1" --bind 'tab:toggle-up,btab:toggle-down,ctrl-m:print-query,ctrl-w:execute-multi(echo {} | xclip -selection clipboard)+abort'
}


function listTags {
    buku -t
}

function extractTags {
    # The output of buku -t is:
    # 1. bookmark (1)
    # 2. hello (1)
}

function joinSelections {
    # concatenate tag with ,
    selection="$1"
    output=''
    for element in ${selection[@]}; do
        output=$output,$element
    done
    echo $output
}

function myAddTags {
    # ask for tag in a loop
    tags=''
    while true
        # display already selected tags as fzf header, which is shown on the top of the list
        newTag=$(searchTagAsYouType $tags $@)
        newTag=joinSelections $newTag
        # join tags with ','
        tags=$tags,$newTag
        # fzf returns 130 if Interrupted with CTRL-C or ESC
        # use that to break out of the loop
        if [ $? = 130 ] ; then
            break ;
        fi
    done
}

function askUserForTags {
    listTags |
    extractTags |
    myAddTags |
    echo "$tags"
}

@vredesbyyrd
Copy link

@QiangF

Did you ever get some form of this working? Oil is a great tool, but auto-completion of tags name would make it that much better and solve a couple workflow issues I have run into.

Also, are you using 'fzf' in place of 'peco' entirely?

cheers.

@QiangF
Copy link
Author

QiangF commented Aug 28, 2018

I am still working on it:

#!/bin/bash

# globals
LIBDIR=$HOME/.dotfiles/bin/oil
mode="open"
forceMultilineSelection=true
declare -a selection

# parse command line flags
function parseFlags {
    parsingLibraryError="error: 'getopt' missing"
    helpText="-t/--tag, -T/--title, -d/--delete"

    # check if getopt is available
    getopt --test > /dev/null
    if [[ $? -ne 4 ]]; then
        echo "$parsingLibraryError"
        exit 1
    fi

    # specify valid flags
    shortOptions=hatTdn
    longOptions=help,add,tag,title,delete,no-multilineSelection

    parsed=$(getopt --options $shortOptions --longoptions $longOptions --name "$0" -- "$@")

    # exit if parsing arguments failed
    if [[ $? -ne 0 ]]; then
        exit 1
    fi

    while true; do
        case "$1" in
            -h|--help) echo "$helpText"; exit 0;;
            -a|--add) mode="add"; skipSelection=true; shift;;
            -t|--tag) mode="tag"; shift;;
            -T|--title) mode="title"; shift;;
            -d|--delete) mode="delete"; shift;;
            -n|--no-multilineSelection) forceMultilineSelection=false; shift;;
            *) break;;
        esac
    done
}

function updateTitle {
    index="${1#*|}"
    buku --print "$index"
    read -r -p "Enter a new title for the bookmark above: " newTitle
    if [[ -n $newTitle ]]; then
        buku --tacit --update "$index" --title "$newTitle"
    else
        echo "The new title was empty, no update was done."
    fi
}

function updateTitles {
    forEachBookmark updateTitle
}

function deleteBookmark {
    url="${urlAndIndex%|*}"
    withoutTrailingSlash="${url%/}"
    buku --sany "$withoutTrailingSlash" --delete
}

function deleteBookmarks {
    forEachBookmark deleteBookmark
}

function tagBookmark {
    index="${1#*|}"
    buku --update "$index" --tag "$tags"
}

function addTag {
    index="${1#*|}"
    buku --update "$index" --tag + "$tags"
}

function removeTag {
    index="${1#*|}"
    buku --update "$index" --tag + "$tags"
}

function tagBookmarks {
    tags=$(askUserForTags)
    forEachBookmark tagBookmark
}

function openInBrowser {
    url=${urlAndIndex%|*}
    xdg-open "$url"
}

function forEachBookmark {
    operation=$1
    for urlAndIndex in ${selection[@]}; do
        ${operation} "$urlAndIndex"
    done
}

function openBookmarks {
    forEachBookmark openInBrowser
}

function exitOnEmptySelection {
    if [[ -z "${selection[@]}" ]]; then
        exit 0
    fi
}

function formatColumns {
    awk -f "$LIBDIR"/format-columns.filter
}

function jsonToLine {
    jq -fr "$LIBDIR"/json-to-line.filter
}

function bookmarksAsJson {
    buku --print --json
}

function searchAndSelectBookmarks {
    bookmarksAsJson |
    jsonToLine |
    formatColumns |
    searchAsYouType
}

function askForTitleAndTagsThenAdd {
    url=$1
    read -p "Enter a title to set a custom one, leave empty otherwise: " customTitle
    tags=$(askUserForTags)
    addBookmark="buku --add $url"
    if [[ -n "$customTitle" ]]; then
        addBookmark="$addBookmark --title $customTitle"
    fi
    if [[ -n "$tags" ]]; then
        addBookmark="$addBookmark --tag $tags"
    fi
    $addBookmark
}

function addBookmarkFromClipboard {
    urlFromClipboard=$(xsel)

    if [[ -n "$urlFromClipboard" ]]; then
        echo "URL is:"
        echo "$urlFromClipboard"
        # If the URL contains characters like ;, & or brackets they may be interpreted
        # specially by the shell. To avoid it, add the URL within single or double quotes.
        askForTitleAndTagsThenAdd "'$urlFromClipboard'"
    else
        echo "Clipboard is empty, not adding any bookmarks."
        exit 0
    fi

    exit 0
}

function processBookmarks {
    if [[ "$mode" == "add" ]]; then
        addBookmarkFromClipboard
        exit 0
    fi

    selection=$(searchAndSelectBookmarks)
    echo "selection is:"
    echo "$selection"
    exitOnEmptySelection

    case "$mode" in
        open) openBookmarks;;
        tag) tagBookmarks;;
        title) updateTitles;;
        delete) deleteBookmarks;;
    esac
}

# q
# modify askUserForTags to allow multiple tags with auto completion
# askUserForTagsWithCompletion

function searchAsYouType {
    # to accept the current typed string, press ctrl-m
    # to copy selected lines to clipboard, press ctrl-w
    if [[ "$forceMultilineSelection" == true ]]; then
        fzf --multi --cycle --bind 'tab:toggle-up,btab:toggle-down,ctrl-w:execute-multi(echo {} | xclip -selection clipboard)+abort'
    else
        fzf --cycle --bind 'tab:toggle-up,btab:toggle-down,ctrl-y:execute(echo {} | xclip -selection clipboard)+abort'
    fi
}

function askUserForTags {
    read -p "Input the comma separated list of tags to add: " tags
    echo "$tags"
}

# run
parseFlags "$@"
processBookmarks

@vredesbyyrd
Copy link

Awesome, thanks for sharing. I am going to take a look at this when I have sometime tomorrow. Cheers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants