Skip to content

Instantly share code, notes, and snippets.

@maciakl
Last active October 1, 2024 05:53
Show Gist options
  • Save maciakl/b7f65bf40a1a78c06e6b0b058d76234f to your computer and use it in GitHub Desktop.
Save maciakl/b7f65bf40a1a78c06e6b0b058d76234f to your computer and use it in GitHub Desktop.
My CLi bookmarking function for PowerShell. It allows you to store a current path as a bookmark and then quickly go back to it later.

Bookmarks for the CLI

This powershell function allows you to bookmark a path and then effortlessly return to it at a later time.

Philosophy

Imagine having a short list of bookmarks available in your CLI. You have one for your current project, one for your other current project, one for your Neovim setup, one for this thing you jave to do this week and never have to touch again, and etc. Imagine being able to jump between them with one simple command.

Now imagine you only have 10 bookmark slots, so you have to be judicious as to what you bookmark, and how you maintain the list, so that it continues being useful.

Why not use symlinks?

Symlinks tend to be permanent. They are easy to set up, but equally easy to never delete, so you end up with a lot of clutter in your home directory. A limited list of 10 bookmarks forces you to actively prune it, and make decisions about to what should be on it, based on how often you use it.

Usage

To use j, first browse to a desired location, then run:

j set 1

This will set the current path as bookmark 1.

You can then run:

j 1

This will jump to that bookmark. Alternatively you can run the command without any arguments:

j

This will print out a list and let you choose a bookmark to jump to.

You can overwrite any bokmark by simply setting it again. Note that bookmarks must be numerical.

To delete an existing bookmark run:

j delete 1

This will delete bookmark 1.

Dependencies

The j script uses the following:

  • Skate for storing your bookmarks
  • Gum for TUI elements.

Windows

To install the dependencies run:

winget install skate
winget install gum

Mac

To install the dependencies run:

brew install skate
brew install gum

Internals

Under the hood the script saves your bookmarks in a Skate db named @j. If something goes wrong you can always delete bookmarks from there manually. You can also use the sync functionality built into Skate to have the bookmarks distributed across multiple machines (though that may not be as useful as it may seem).

Installation

Windows & Powershell

Copy and paste the function into your $PROFILE file our source it there.

Mac/Linux & ZSH

Put jmp file somewhere in your path, then in your .zshrc file add the folliwing:

function j() {
    answer=$(jmp $@)
    if [ -n "$answer" ]; cd $answer
}
function j {
if ($args.Length -eq 0) {
$answer = gum spin --show-output -- skate list "@j" | gum choose
$answer = $answer -split "`t"
$key = -join("", $answer[0], "@j")
$path = gum spin --show-output -- skate get $key
if ($path -ne "Key cannot be empty" -and $path -ne "" -and $path -ne $null) {
cd $path
}
} else {
# check if first argument is a number
if ($args[0] -match '^\d+$') {
$num = -join("", $args[0], "@j")
$path = gum spin --show-output skate get $num
if ($path -ne "Key not found") {
cd $path
} else {
Write-Host "Bookmark not found"
return
}
} else {
if ($args[0] -eq 'set' -or $args[0] -eq 'delete' -and $args.Length -eq 2) {
# check if second argument is a number between 0 and 9
if ($args[1] -match '^\d+$' -and $args[1] -ge 0 -and $args[1] -le 9) {
if ($args[0] -eq 'set') {
gum spin -- skate set $key $path
$path = (Get-Location).Path
$key = -join("", $args[1], "@j")
gum spin -- skate set $key $path
} elseif ($args[0] -eq 'delete') {
$in = $args[1]
$key = -join("", $in, "@j")
$path = gum spin --show-output -- skate get $key
if ($path -ne "Key not found") {
$message = "Deleting bookmark $in`: $path"
gum style --foreground 212 $message
gum confirm
if ($?) { gum spin -- skate delete $key }
} else {
Write-Host "Bookmark $in not found"
return
}
}
} else {
Write-Host "Invalid argument: bookmark must be a number between 0 and 9"
}
} else {
Write-Host "Invalid argument: try set, delete or a number"
}
}
}
}
#!/bin/zsh
# check if no arguments
if [[ -z "$1" ]]; then
# save output of a command into a variable
answer=$(skate list @j | gum choose)
# split answer on tabs and take the first part
key=$(echo $answer | cut -f1)
mypath=$(skate get $key"@j")
# if path is not equal to Key cannot be empty
if [[ $mypath != "Key cannot be empty" ]]; then
echo $mypath
fi
else
# if there is one argument check if it's a number
if [[ $1 =~ ^[0-9]+$ ]]; then
answer=$(skate get $1"@j")
# if the answer is not equal to Key not found
if [[ $answer != "Key not found" ]]; then
echo "$answer"
else
>&2 echo "Bookmark not found"
fi
else
# check if the first argument is set or delete
if [[ $1 == "set" ]]; then
# check if there are two arguments
if [[ $# -eq 2 ]]; then
#check if the second argument is a number between 0 and 9
if [[ $2 =~ ^[0-9]$ ]]; then
skate set $2"@j" $(pwd)
else
>&2 echo "Bookmark must be a number between 0 and 9"
fi
else
>&2 echo "Usage: j set [0-9]"
fi
elif [[ $1 == "delete" ]]; then
# check if there are two arguments
if [[ $# -eq 2 ]]; then
# check if the second argument is a number between 0 and 9
if [[ $2 =~ ^[0-9]$ ]]; then
mypath=$(skate get $2"@j") 2> /dev/null
if [[ ! -z $mypath ]]; then
>&2 gum style --foreground=212 "Deleting bookmark: $2 $mypath"
gum confirm && skate delete $2"@j"
else
>&2 echo "Bookmark not found"
fi
else
>&2 echo "Bookmark must be a number between 0 and 9"
fi
else
>&2 echo "Usage: j delete [0-9]"
fi
else
>&2 echo "Usage: j [set|delete] [0-9]"
fi
fi
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment