diff --git a/inc/file-management b/inc/file-management new file mode 100644 index 0000000..24b34a5 --- /dev/null +++ b/inc/file-management @@ -0,0 +1,83 @@ +#!/usr/bin/env bash + +# This file contains functions relating to file operations such as new, edit and deleting files + +newFile(){ + vecho "newFile $*" + cd "$dataDir" || return + # While there is a - at the begining + local title="$*" + if [ -z "$title" ]; then + echo -n "Enter a title: " + read -r title + fi + local filename + filename="$(escapeFilename "$title.md")" + [ -e "$filename" ] && die "$filename already exists" + echo -e "--- +Title: $title +Tags: + - +--- +" > "$filename" + echo "INSERT INTO items (filename, title, type) + VALUES ( '$(safeSQL "$filename")', '$(safeSQL "$title")', 'normal' );" | + sqlite3 "${sqliteFile}" + editFile "$filename" +} + +# Takes the filename as a parameter +editFile(){ + vecho "editFile $*" + cd "$dataDir" || return + local filename + local oldTitle + local newTitle + filename="$(findFile "$*")" + [ ! -e "$filename" ] && exit 1 + oldTitle="$(getYamlTitle "$filename")" + "$editor" "$filename" + newTitle="$(getYamlTitle "$filename")" + getYamlTags "$filename" | assignTags "$filename" + if [ "$newTitle" != "$oldTitle" ]; then + vecho "Changed title" + local newfilename + newfilename="$(escapeFilename "$newTitle.md")" + if [ -e "$newfilename" ]; then + echo -e "${YELLOW}File name $newfilename already exists${NC}" + echo -e "Please fix manually" + exit 1 + else + mv "$filename" "$newfilename" + echo "UPDATE items + SET (filename,title) = ('$(safeSQL "$newfilename")','$(safeSQL "$newTitle")') + WHERE filename = '$(safeSQL "$filename")';" | + sqlite3 "${sqliteFile}" + gitChange "$newfilename" + fi + else + gitChange "$filename" + fi +} + +deleteFile(){ + cd "$dataDir" || return + local filename + local fileID + local rsp + filename="$(findFile "$1")" + fileID="$(findFileId "$filename")" + [ ! -e "$filename" ] && exit 1 + echo -n "Are you sure? [yN] " + read -r rsp + if [[ "$(echo "$rsp" | tr '[:upper:]' '[:lower:]')" = "y"* ]]; then + rm "$filename" + # This deletes the file from the sql database and any tag links + echo "DELETE FROM items + WHERE id = '$(safeSQL "$fileID")'; + DELETE FROM links + WHERE itemID = '$(safeSQL "$fileID")';" | + sqlite3 --column --header "${sqliteFile}" + fi +} + diff --git a/inc/init b/inc/init new file mode 100644 index 0000000..a04089f --- /dev/null +++ b/inc/init @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +initKnowledgeBase(){ + local output + necho -e "${YELLOW}Initialising Knowledge base${NC}" + vecho "Directory: $dataDir" + if [ "$verbose" -gt 0 ]; then + output="/dev/stdout" + else + output="/dev/null" + fi + [ -e "$dataDir" ] && die "$dataDir already exists" + mkdir -p "$dataDir" + if [ "$dogit" -gt 0 ]; then + git init "$dataDir" > "$output" + + # TODO: make gitignore use new sqlite file + echo "/knowledgebase.sqlite3" >> "${dataDir}/.gitignore" + + git -C "$dataDir" add .gitignore > "$output" + git -C "$dataDir" commit -m "Knowledge base initialised" > output + fi + vecho "Creating Database" + echo 'CREATE TABLE items + (id integer primary key, filename text, title text, type text); + CREATE TABLE tags + (id integer primary key, name text); + CREATE TABLE links + (id integer primary key, itemID integer, tagID integer); ' | + sqlite3 "${sqliteFile}" + necho -e "${GREEN}Initialised Knowledge base${NC}" +} + diff --git a/inc/tag-management b/inc/tag-management new file mode 100644 index 0000000..4c5921b --- /dev/null +++ b/inc/tag-management @@ -0,0 +1,66 @@ +#!/usr/bin/env bash + +assignTags(){ + local filename + local tags + local tagIDs + local tagIDsOr + local fileID + filename="$(findFile "$1")" + [ ! -e "$filename" ] && exit 1 + tags="$(cat - | sed '/^$/d')" + fileID="$(findFileId "$filename")" + # If there are tags + if [ -n "$tags" ]; then + local values + local orlist + while read -r line; do + values+=",('$(safeSQL "$line")')" + orlist+=" OR name = '$(safeSQL "$line")'" + done <<<"$(echo "$tags")" + values="$(echo "$values" | sed 's/^,//')" + orlist="$(echo "$orlist" | sed 's/^ OR //')" + + # Ensure that all the tags exist + echo "INSERT INTO tags (name) VALUES $values + EXCEPT SELECT name FROM tags;" | + sqlite3 "${sqliteFile}" + + # Get the tag ids we need to assosiate with the current file + tagIDs="$(echo "SELECT id FROM tags WHERE $orlist" | + sqlite3 "${sqliteFile}")" + + #Loop through them all + while read -r tagID; do + tagIDsOr+=" OR tagID = $(safeSQL "$tagID")" + + # Check the tag is already linkded with the file + local existing + existing="$(echo "SELECT id FROM links + WHERE itemID = $(safeSQL "$fileID") + AND tagID = $(safeSQL "$tagID")" | + sqlite3 "${sqliteFile}" + )" + + # If not, add a link + if [ -z "$existing" ]; then + echo "INSERT INTO links (itemID,tagID) + VALUES ($(safeSQL "$fileID"),$(safeSQL "$tagID"))" | + sqlite3 "${sqliteFile}" + fi + done <<<"$(echo "$tagIDs")" + tagIDsOr="$(echo "$tagIDsOr" | sed 's/^ OR //')" + + # Delete any links that are not in the list + echo "DELETE FROM links + WHERE itemID = $(safeSQL "$fileID") + AND NOT ( $tagIDsOr )" | + sqlite3 "${sqliteFile}" + + else # If there are no tags, simply delete any that are referenced + echo "DELETE FROM links WHERE itemID = '$(safeSQL "$fileID")'" | + sqlite3 "${sqliteFile}" + fi + + +} diff --git a/inc/yaml b/inc/yaml new file mode 100644 index 0000000..86551f6 --- /dev/null +++ b/inc/yaml @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +getYamlBlock(){ + vecho "getYamlBlock $*" + cd "$dataDir" || return + local filename + filename="$(findFile "$1")" + + sed -n '1 { /^---/ { :a N; /\n---/! ba; p} }' "$filename" | + sed '1d;$d;s/\t/ /g' +} + +getYamlTitle(){ + vecho "getYamlTitle $*" + cd "$dataDir" || return + getYamlBlock "$1" | yq -r '.Title' +} + +getYamlTags(){ + vecho "getYamlTitle $*" + cd "$dataDir" || return + getYamlBlock "$1" | yq -r '.Tags | join("\n")' +} + diff --git a/kb b/kb index c7ec06b..567a1f5 100755 --- a/kb +++ b/kb @@ -23,6 +23,13 @@ NC='\033[0m' # Provide a variable with the location of this script. #scriptPath="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +# Source files +# ################################################## + +source "inc/init" +source "inc/tag-management" +source "inc/file-management" +source "inc/yaml" # Utility functions # ################################################## @@ -111,63 +118,17 @@ checkDependencies(){ return $ret } -initKnowledgeBase(){ - local output - necho -e "${YELLOW}Initialising Knowledge base${NC}" - vecho "Directory: $dataDir" - if [ "$verbose" -gt 0 ]; then - output="/dev/stdout" - else - output="/dev/null" - fi - [ -e "$dataDir" ] && die "$dataDir already exists" - mkdir -p "$dataDir" - if [ "$dogit" -gt 0 ]; then - git init "$dataDir" > "$output" - - # TODO: make gitignore use new sqlite file - echo "/knowledgebase.sqlite3" >> "${dataDir}/.gitignore" - - git -C "$dataDir" add .gitignore > "$output" - git -C "$dataDir" commit -m "Knowledge base initialised" > output - fi - vecho "Creating Database" - echo 'CREATE TABLE items - (id integer primary key, filename text, title text, type text); - CREATE TABLE tags - (id integer primary key, name text); - CREATE TABLE links - (id integer primary key, itemID integer, tagID integer); ' | - sqlite3 "${sqliteFile}" - necho -e "${GREEN}Initialised Knowledge base${NC}" -} - # This will create a fresh database based on the files in the folder makedb(){ [ -f "$sqliteFile" ] && die "sqlite file already exists\ndelete it first if you want to" echo "Still need to implement this" } -getYamlBlock(){ - vecho "getYamlBlock $*" - cd "$dataDir" || return - local filename - filename="$(findFile "$1")" - - sed -n '1 { /^---/ { :a N; /\n---/! ba; p} }' "$filename" | - sed '1d;$d;s/\t/ /g' -} - -getYamlTitle(){ - vecho "getYamlTitle $*" - cd "$dataDir" || return - getYamlBlock "$1" | yq -r '.Title' -} - -getYamlTags(){ - vecho "getYamlTitle $*" - cd "$dataDir" || return - getYamlBlock "$1" | yq -r '.Tags | join("\n")' +# Escapes ' and \ characters +safeSQL(){ + echo "$1" | + sed 's/\\/\\\\/g' | + sed "s/'/\\\'/g" } # Makes file names safe @@ -187,103 +148,6 @@ findFileId(){ } -# Escapes ' and \ characters -safeSQL(){ - echo "$1" | - sed 's/\\/\\\\/g' | - sed "s/'/\\\'/g" -} - - -assignTags(){ - local filename - local tags - local tagIDs - local tagIDsOr - local fileID - filename="$(findFile "$1")" - [ ! -e "$filename" ] && exit 1 - tags="$(cat - | sed '/^$/d')" - fileID="$(findFileId "$filename")" - # If there are tags - if [ -n "$tags" ]; then - local values - local orlist - while read -r line; do - values+=",('$(safeSQL "$line")')" - orlist+=" OR name = '$(safeSQL "$line")'" - done <<<"$(echo "$tags")" - values="$(echo "$values" | sed 's/^,//')" - orlist="$(echo "$orlist" | sed 's/^ OR //')" - - # Ensure that all the tags exist - echo "INSERT INTO tags (name) VALUES $values - EXCEPT SELECT name FROM tags;" | - sqlite3 "${sqliteFile}" - - # Get the tag ids we need to assosiate with the current file - tagIDs="$(echo "SELECT id FROM tags WHERE $orlist" | - sqlite3 "${sqliteFile}")" - - #Loop through them all - while read -r tagID; do - tagIDsOr+=" OR tagID = $(safeSQL "$tagID")" - - # Check the tag is already linkded with the file - local existing - existing="$(echo "SELECT id FROM links - WHERE itemID = $(safeSQL "$fileID") - AND tagID = $(safeSQL "$tagID")" | - sqlite3 "${sqliteFile}" - )" - - # If not, add a link - if [ -z "$existing" ]; then - echo "INSERT INTO links (itemID,tagID) - VALUES ($(safeSQL "$fileID"),$(safeSQL "$tagID"))" | - sqlite3 "${sqliteFile}" - fi - done <<<"$(echo "$tagIDs")" - tagIDsOr="$(echo "$tagIDsOr" | sed 's/^ OR //')" - - # Delete any links that are not in the list - echo "DELETE FROM links - WHERE itemID = $(safeSQL "$fileID") - AND NOT ( $tagIDsOr )" | - sqlite3 "${sqliteFile}" - - else # If there are no tags, simply delete any that are referenced - echo "DELETE FROM links WHERE itemID = '$(safeSQL "$fileID")'" | - sqlite3 "${sqliteFile}" - fi - - -} - -newFile(){ - vecho "newFile $*" - cd "$dataDir" || return - # While there is a - at the begining - local title="$*" - if [ -z "$title" ]; then - echo -n "Enter a title: " - read -r title - fi - local filename - filename="$(escapeFilename "$title.md")" - [ -e "$filename" ] && die "$filename already exists" - echo -e "--- -Title: $title -Tags: - - ---- -" > "$filename" - echo "INSERT INTO items (filename, title, type) - VALUES ( '$(safeSQL "$filename")', '$(safeSQL "$title")', 'normal' );" | - sqlite3 "${sqliteFile}" - editFile "$filename" -} - findFile(){ vecho "findFile $*" cd "$dataDir" || return @@ -313,7 +177,7 @@ findFile(){ fi } -dogit(){ +externalgit(){ cd "$dataDir" || return git "$@" } @@ -337,40 +201,6 @@ gitChange(){ fi } -# Takes the filename as a parameter -editFile(){ - vecho "editFile $*" - cd "$dataDir" || return - local filename - local oldTitle - local newTitle - filename="$(findFile "$*")" - [ ! -e "$filename" ] && exit 1 - oldTitle="$(getYamlTitle "$filename")" - "$editor" "$filename" - newTitle="$(getYamlTitle "$filename")" - getYamlTags "$filename" | assignTags "$filename" - if [ "$newTitle" != "$oldTitle" ]; then - vecho "Changed title" - local newfilename - newfilename="$(escapeFilename "$newTitle.md")" - if [ -e "$newfilename" ]; then - echo -e "${YELLOW}File name $newfilename already exists${NC}" - echo -e "Please fix manually" - exit 1 - else - mv "$filename" "$newfilename" - echo "UPDATE items - SET (filename,title) = ('$(safeSQL "$newfilename")','$(safeSQL "$newTitle")') - WHERE filename = '$(safeSQL "$filename")';" | - sqlite3 "${sqliteFile}" - gitChange "$newfilename" - fi - else - gitChange "$filename" - fi -} - listEntries(){ vecho "listEntries $*" cd "$dataDir" || return @@ -413,7 +243,7 @@ fzfPreview(){ tags="$(echo "$1" | awk -F ' +' '{print $5}')" if [ "$type" = "normal" ]; then - bat --color=always --style=numbers "$filename" + bat --color=always --style=full "$filename" fi } @@ -428,27 +258,6 @@ viewFile(){ bat --color=always --style=full "$filename" } -deleteFile(){ - cd "$dataDir" || return - local filename - local fileID - local rsp - filename="$(findFile "$1")" - fileID="$(findFileId "$filename")" - [ ! -e "$filename" ] && exit 1 - echo -n "Are you sure? [yN] " - read -r rsp - if [[ "$(echo "$rsp" | tr '[:upper:]' '[:lower:]')" = "y"* ]]; then - rm "$filename" - # This deletes the file from the sql database and any tag links - echo "DELETE FROM items - WHERE id = '$(safeSQL "$fileID")'; - DELETE FROM links - WHERE itemID = '$(safeSQL "$fileID")';" | - sqlite3 --column --header "${sqliteFile}" - fi -} - doDeepSearch(){ cd "$dataDir" || return local query="$1" @@ -462,7 +271,7 @@ doDeepPreview(){ local line file="$(echo "$1" | cut -d ':' -f 1)" line="$(echo "$1" | cut -d ':' -f 2)" - bat --color=always --style=numbers -H "$line" "$file" + bat --color=always --style=full -H "$line" "$file" } deepSearch(){ @@ -496,7 +305,7 @@ mainScript() { fuzzy) fuzzySelect "${args[@]:1}"; safeExit ;; deepsearch) deepSearch "${args[@]:1}"; safeExit ;; del|delete) deleteFile "${args[@]:1}"; safeExit ;; - git) dogit "${args[@]:1}"; safeExit ;; + git) externalgit "${args[@]:1}"; safeExit ;; *) usage >&2; safeExit ;; esac