Open QiangF opened 7 years ago
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.
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.
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
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"
}
@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.
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
Awesome, thanks for sharing. I am going to take a look at this when I have sometime tomorrow. Cheers.
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.