Skip to content

Commit

Permalink
fzf integration
Browse files Browse the repository at this point in the history
  • Loading branch information
ttscoff committed Sep 16, 2021
1 parent 5be30be commit 5e238cb
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 57 deletions.
35 changes: 35 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# CHANGELOG

## 1.2.4

- Way better searching, progressively fuzzier until a match is found
- Only ever show the best result, no point in showing a ton of matches
- Use fzf if available, for searching and for filtering multiple matches

## 1.2.3

- Bugfixes

## 1.2.2

- OR query when AND failed was way too broad, replaced
with phrase and no boolean
- Added debugging

## 1.2.1

- Colorize output
- List all known questions with -l

## 1.2.0

- Add -p switch to pull answer from clipboard when adding a question
- Improve option parsing
- Allow -e when adding answer to open new question file in editor
- Allow multi-line input when adding interactively
- Allow '#atag' to translate to 'tag:atag' in spotlight search
- Allow configuration via environment variables

## 1.1.1

- Only opens url/copies code from one note if there are multiple answers
21 changes: 19 additions & 2 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Quick Question
# Quick Question #

[Pretty version](http://ttscoff.github.com/QuickQuestion/)

Expand Down Expand Up @@ -28,6 +28,23 @@ I use it with nvALT and sync to Simplenote and Dropbox, which means I can enter
: By nature, you don't need anything special to use this system with NV. You just need to save your notes to plain text files if you want them to be accessible to `mdfind` and other system tools.
: To get simple command line access to the question in your NV folder, set up the configuration in the `qq` script above to point to your NV notes folder. This will allow you to run speedy queries confined to just that directory, and enter notes quickly from other sources. Notes entered from the command line or LaunchBar/Alfred will show up instantly in your NV notes list.


### CONFIGURATION ###

Configuration is done via environment variables:

QQ_NOTES_DIR - Path to Markdown files
QQ_NOTES_EXT - Extension of answer files (default md)
QQ_NOTES_PRE - Prefix of question files (default ??)
QQ_EDITOR - Text editor to use (default $EDITOR)
QQ_USE_FZF - If fzf is available, it will be used by default. Set this
to false to override

Example:

export QQ_NOTES_DIR="/Users/ttscoff/Dropbox/Notes"
export QQ_NOTES_EXT="md"

### Usage ###

**Command line**
Expand Down Expand Up @@ -59,7 +76,7 @@ Keep your questions in a natural language format, but avoid contractions and use

When querying, only use operative terms to get the best results. "Where did I leave my glasses" will return poor results if the question you labeled it with was "Where did I put my glasses". Instead, query "where glasses" and you'll find the note instantly.

Running `qq` at the command line with no arguments will list all of the questions and their answers in your archive. This can be handy for using with `grep` to parse out pieces of questions and/or answers that you couldn't find otherwise.
Running `qq -l` at the command line with no arguments will list all of the questions and their answers in your archive. This can be handy for using with `grep` to parse out pieces of questions and/or answers that you couldn't find otherwise.

### Additional meta ###

Expand Down
152 changes: 97 additions & 55 deletions qq
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/bin/bash
QQ_VERSION=1.2.3
# Usage:
# Ask a question using natural language
# $ qq [search terms]
Expand Down Expand Up @@ -36,32 +37,14 @@
# export QQ_NOTES_DIR="/Users/ttscoff/Dropbox/Notes"
# export QQ_NOTES_EXT="md"
#
# CHANGELOG
# 1.2.2
# - OR query when AND failed was way too broad, replaced
# with phrase and no boolean
# - Added debugging
#
#
# 1.2.1
# - Colorize output
# - List all known questions with -l
#
# 1.2.0
# - Add -p switch to pull answer from clipboard when adding a question
# - Improve option parsing
# - Allow -e when adding answer to open new question file in editor
# - Allow multi-line input when adding interactively
# - Allow '#atag' to translate to 'tag:atag' in spotlight search
# - Allow configuration via environment variables
#
# 1.1.1
# - Only opens url/copies code from one note if there are multiple answers

: ${QQ_NOTES_DIR:="$HOME/Dropbox/Notes/nvALT2.2"}
: ${QQ_NOTES_EXT:="md"}
: ${QQ_NOTES_PRE:="??"}
: ${QQ_EDITOR:=$EDITOR}
_USE_FZF=false
which fzf &>/dev/null
[[ $? == 0 ]] && _USE_FZF=true
: ${QQ_USE_FZF:=$_USE_FZF}

_QQ_DEBUG=false

Expand Down Expand Up @@ -93,7 +76,7 @@ __qq () {

OPTIND=1

while getopts "acdeh?lp" opt; do
while getopts "acdeh?lpv" opt; do
case $opt in
a)
ADDING=true
Expand All @@ -120,14 +103,18 @@ __qq () {
ADDING=true
PASTING=true
;;
v)
echo "$0 v$QQ_VERSION"
return
;;
esac
done

shift $((OPTIND-1))
[ "${1:-}" = "--" ] && shift

if $HELPING; then
exit 0
return
fi

HAS_COPIED_TEXT=false
Expand Down Expand Up @@ -168,12 +155,13 @@ __qq () {
else
if [[ $# == 0 ]]; then
__qq_help
exit 0
return
fi
QQINPUTQUERY=$(__qq_query_include_all "${*%\?}")
local QQORIGINALQUERY="$*"
local QQINPUTQUERY=$(__qq_query_include_all "${*%\?}")
__qq_debug "Attempting to find ALL options: ${QQINPUTQUERY}"
QQQUERY="mdfind -onlyin '$NOTESDIR' -interpret '(kind:text OR kind:markdown) AND filename:$NOTESEXT AND filename:$NOTESPRE ${QQINPUTQUERY}${EXCLUDEQQQUERY}'"
RESULTS=$(eval $QQQUERY)
local RESULTS=$(eval $QQQUERY)

if [[ "$RESULTS" == "" ]]; then
QQINPUTQUERY=$(__qq_query_include_all OR "${*%\?}")
Expand All @@ -182,41 +170,83 @@ __qq () {
RESULTS=$(eval $QQQUERY)
fi

if [[ "$RESULTS" == "" ]]; then

if $QQ_USE_FZF; then
QQQUERY='ls "${NOTESDIR}??"*.$NOTESEXT|fzf -i --tiebreak=length,begin -f "$(__qq_remove_stopwords "$*")"'
__qq_debug "We have fzf, trying: ${QQQUERY}"
RESULTS=$(eval $QQQUERY)
RESULTS=$(echo "$RESULTS" | head -n 1)
fi
fi

if [[ "$RESULTS" == "" ]]; then
__qq_debug "Well, jeeze, I guess we'll try grepping for the question with the most matching words"
declare -a WORDS=( $* )
local MAX=${#WORDS}
local RX=$(__qq_query_regex "$*")
for i in $(seq $MAX 2); do
RESULTS=$(ls "${NOTESDIR}??"*.$NOTESEXT|grep -iE "${RX}{$i}")
if [[ "$RESULTS" != "" ]]; then
__qq_debug "Ooh, found a match containing $i of the words: ${RX}{$i}"
break
fi
done
fi

if [[ "$RESULTS" == "" ]]; then
echo "$(__qc red)Sorry, I don't know the answer to that question.$(__qc reset)"
exit 2
else
# Sort results by length, assuming shortest result is best match
RESULTS=$(echo "$RESULTS" | awk '{ print length, $0 }' | sort -n -s | cut -d" " -f2-)

if $QQ_USE_FZF; then
ANSWER=$(echo -e "$RESULTS"|fzf -i --prompt="Select a question > " -1 -q "$QQORIGINALQUERY")
RESULTS=""
else
ANSWER=$(echo -e "$RESULTS" | head -n 1)
RESULTS=$(echo -e "$RESULTS" | sed '1d' | head -n 5)
fi

if $EDITING; then
ANSWER=$(echo "$RESULTS"|head -n 1)
ANSWER=$(echo "$RESULTS" | awk '{ print length, $0 }' | sort -n -s | cut -d" " -f2- | head -n 1)
$QQEDITOR "$ANSWER"
else
echo -e "$RESULTS" | while read LINE; do
if [[ "$LINE" =~ ^$ ]]; then
echo "$(__qc red)Sorry, I don't know the answer to that question.$(__qc reset)"
exit 1;
fi
QUESTION=`basename "$LINE" ".$NOTESEXT"`
echo -e "\n"
echo -n "$(__qc yellow)Q: $(__qc white)"
NOTESPREESC=`echo "$NOTESPRE"|sed -E 's/([\?\!\$\`\"]) ?/\\\\\1/g'`
echo "$QUESTION"|sed -E "s/$NOTESPREESC ?//g"|sed -E 's/([^\?])$/\1?/'
echo -n "$(__qc yellow)A: $(__qc white)"
cat "$LINE"|sed -E 's/@\([^\)]+\) ?//g'|sed -E 's/@copy\(([^\)]+)\)/\1/'|sed -E 's/@open\(([^\)+]*)\)/Related URL: \1/'|sed -E 's/@[^\( ]+ ?//g' # |sed -E 's/^[ ]*|[ ]*$//g'
if [[ `cat "$LINE"|grep -E '@copy\('` && $HAS_COPIED_TEXT == false ]]; then
cat "$LINE"|grep '@copy('|sed -E 's/.*@copy\(([^\)]+)\).*/\1/'|tr -d '\n'|pbcopy
echo "Example in clipboard"
HAS_COPIED_TEXT=true
fi

if [[ `cat "$LINE"|grep -E '@open\('` && $HAS_OPENED_URL == false ]]; then
url=$(cat "$LINE"|grep '@open('|sed -E 's/.*@open\(([^\)]+)\).*/\1 /'|tr -d '\n')
open -g $url
echo "Opened URL"
HAS_OPENED_URL=true
fi
done
__qc reset
return
fi

if [[ "$ANSWER" =~ ^$ ]]; then
echo "$(__qc red)Sorry, I don't know the answer to that question.$(__qc reset)"
exit 1;
fi

QUESTION=`basename "$ANSWER" ".$NOTESEXT"`
echo -e "\n"
echo -n "$(__qc yellow)Q: $(__qc white)"
NOTESPREESC=`echo "$NOTESPRE"|sed -E 's/([\?\!\$\`\"]) ?/\\\\\1/g'`
echo "$QUESTION"|sed -E "s/$NOTESPREESC ?//g"|sed -E 's/([^\?])$/\1?/'
echo -n "$(__qc yellow)A: $(__qc white)"
cat "$ANSWER"|sed -E 's/@\([^\)]+\) ?//g'|sed -E 's/@copy\(([^\)]+)\)/\1/'|sed -E 's/@open\(([^\)+]*)\)/Related URL: \1/'|sed -E 's/@[^\( ]+ ?//g' # |sed -E 's/^[ ]*|[ ]*$//g'
if [[ `cat "$ANSWER"|grep -E '@copy\('` && $HAS_COPIED_TEXT == false ]]; then
cat "$ANSWER"|grep '@copy('|sed -E 's/.*@copy\(([^\)]+)\).*/\1/'|tr -d '\n'|pbcopy
echo -e "\n$(__qc green)Example in clipboard"
HAS_COPIED_TEXT=true
fi

if [[ `cat "$ANSWER"|grep -E '@open\('` && $HAS_OPENED_URL == false ]]; then
url=$(cat "$ANSWER"|grep '@open('|sed -E 's/.*@open\(([^\)]+)\).*/\1 /'|tr -d '\n')
open -g $url
echo -e "\n$(__qc green)Opened URL"
HAS_OPENED_URL=true
fi

if [[ "$RESULTS" != "" ]]; then
echo "$(__qc gray)----------------------"
echo "$(__qc yellow)Other results included:"
echo -e "$(__qc cyan)$RESULTS"
fi

__qc reset
fi
fi
exit 0
Expand Down Expand Up @@ -254,6 +284,18 @@ __qq_query_include_all () {
fi
}

__qq_query_regex () {
if [[ "$*" != "" ]]; then
declare -a query_array=( $* )
local query="(.*("
for i in ${query_array[@]}; do
local stripped=$(echo "$i" | sed -E 's/[^A-Z0-9 ]/.?/gi')
query="${query}${stripped}|"
done
echo -n "$query"|sed -e 's/|$//' -e 's/$/).*)/'
fi
}

__qq_query_exclude_all () {
local input="$1"
local OLDIFS=$IFS
Expand Down

0 comments on commit 5e238cb

Please sign in to comment.