mirror of
https://github.com/denisidoro/navi.git
synced 2026-01-23 02:14:19 +00:00
Add support for Alfred (#347)
This commit is contained in:
parent
019adf82cf
commit
c7f919eaa0
19 changed files with 850 additions and 54 deletions
23
.github/workflows/cd.yml
vendored
23
.github/workflows/cd.yml
vendored
|
|
@ -6,8 +6,27 @@ on:
|
|||
- '*'
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: Publish for ${{ matrix.os }}
|
||||
|
||||
alfred:
|
||||
name: Publish Alfred workflow
|
||||
os: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Zip
|
||||
run: scripts/action workflow
|
||||
- name: Get the version
|
||||
id: get_version
|
||||
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
|
||||
- name: Upload workflow to release
|
||||
uses: svenstaro/upload-release-action@v1-release
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: target/workflow/navi.zip
|
||||
tag: ${{ github.ref }}
|
||||
asset_name: navi-${{ steps.get_version.outputs.VERSION }}.alfredworkflow
|
||||
|
||||
binary:
|
||||
name: Publish binary for ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
|
|
|
|||
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -174,7 +174,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "navi"
|
||||
version = "2.5.2"
|
||||
version = "2.6.0"
|
||||
dependencies = [
|
||||
"anyhow 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "navi"
|
||||
version = "2.5.2"
|
||||
version = "2.6.0"
|
||||
authors = ["Denis Isidoro <denis_isidoro@live.com>"]
|
||||
edition = "2018"
|
||||
description = "An interactive cheatsheet tool for the command-line"
|
||||
|
|
|
|||
13
README.md
13
README.md
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
> :information_source: If you're here because you upgraded **navi** and are having some issues, please check [this thread](https://github.com/denisidoro/navi/issues/201).
|
||||
|
||||
An interactive cheatsheet tool for the command-line.
|
||||
An interactive cheatsheet tool for the command-line and application launchers.
|
||||
|
||||

|
||||

|
||||
|
||||
**navi** allows you to browse through cheatsheets (that you may write yourself or download from maintainers) and execute commands. Argument suggestions are prompted to you.
|
||||
|
||||
|
|
@ -13,7 +13,9 @@ An interactive cheatsheet tool for the command-line.
|
|||
- it will spare you from knowing CLIs by heart;
|
||||
- it will teach you new one-liners.
|
||||
|
||||
It uses [fzf](https://github.com/junegunn/fzf) or [skim](https://github.com/lotabout/skim) under the hood and it can be either used as a command or as a shell widget (*à la* Ctrl-R).
|
||||
It uses [fzf](https://github.com/junegunn/fzf), [skim](https://github.com/lotabout/skim), or [Alfred](https://www.alfredapp.com/) under the hood and it can be either used as a command or as a shell widget (*à la* Ctrl-R).
|
||||
|
||||

|
||||
|
||||
Table of contents
|
||||
-----------------
|
||||
|
|
@ -27,6 +29,7 @@ Table of contents
|
|||
* [Other package managers](#other-package-managers)
|
||||
* [Usage](#usage)
|
||||
* [Shell widget](#shell-widget)
|
||||
* [Alfred](#alfred)
|
||||
* [More options](#more-options)
|
||||
* [Trying out online](#trying-out-online)
|
||||
* [Cheatsheets](#cheatsheets)
|
||||
|
|
@ -112,6 +115,8 @@ navi widget fish | source
|
|||
|
||||
By default, `Ctrl+G` is assigned to launching **navi**.
|
||||
|
||||
### Alfred
|
||||
|
||||
### More options
|
||||
|
||||
Please refer to `navi --help` for more details.
|
||||
|
|
@ -248,7 +253,7 @@ $ jsons: find . -iname '*.json' -type f -print --- --multi
|
|||
List customization
|
||||
------------------
|
||||
|
||||
Lists can be stylized with the [$FZF_DEFAULT_OPTS](https://github.com/junegunn/fzf#layout) environment variable or similar variables or parameters ( please refer to `navi --help`). This way, you can change the [color scheme](https://github.com/junegunn/fzf/wiki/Color-schemes), for example.
|
||||
Lists can be stylized with the [$FZF_DEFAULT_OPTS](https://github.com/junegunn/fzf#layout) environment variable or similar variables/parameters (please refer to `navi --help`). This way, you can change the [color scheme](https://github.com/junegunn/fzf/wiki/Color-schemes), for example.
|
||||
|
||||
|
||||
Related projects
|
||||
|
|
|
|||
9
alfred/alfred.bash
Executable file
9
alfred/alfred.bash
Executable file
|
|
@ -0,0 +1,9 @@
|
|||
#!/bin/bash
|
||||
|
||||
source "${HOME}/.bashrc"
|
||||
|
||||
if [ -n "${snippet:-}" ]; then
|
||||
navi alfred suggestions
|
||||
else
|
||||
navi alfred start
|
||||
fi
|
||||
15
alfred/alfred2.bash
Executable file
15
alfred/alfred2.bash
Executable file
|
|
@ -0,0 +1,15 @@
|
|||
#!/bin/bash
|
||||
|
||||
_interpolate() {
|
||||
local -r snippet="$1"
|
||||
local -r varname="$2"
|
||||
local -r value="${!varname}"
|
||||
|
||||
echo "$snippet" | sed "s/<${varname}>/${value}/g"
|
||||
}
|
||||
|
||||
if [ -n "${varname:-}" ]; then
|
||||
echo -n "$(_interpolate "$snippet" "$varname" || echo "")"
|
||||
else
|
||||
echo -n "$snippet"
|
||||
fi
|
||||
485
alfred/info.plist
Normal file
485
alfred/info.plist
Normal file
|
|
@ -0,0 +1,485 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>bundleid</key>
|
||||
<string>com.github.denisidoro.navi</string>
|
||||
<key>category</key>
|
||||
<string>Tools</string>
|
||||
<key>connections</key>
|
||||
<dict>
|
||||
<key>3CE4BEC1-BEBE-4A11-B182-E63693AE2FE6</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>destinationuid</key>
|
||||
<string>98658166-32FC-4F9D-8247-52BEDE6394EE</string>
|
||||
<key>modifiers</key>
|
||||
<integer>0</integer>
|
||||
<key>modifiersubtext</key>
|
||||
<string></string>
|
||||
<key>vitoclose</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</array>
|
||||
<key>55C46852-4807-4374-95AB-CC055F4ECB7C</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>destinationuid</key>
|
||||
<string>7DD3BDE5-A157-42E5-9376-F681FB50A4EE</string>
|
||||
<key>modifiers</key>
|
||||
<integer>0</integer>
|
||||
<key>modifiersubtext</key>
|
||||
<string></string>
|
||||
<key>vitoclose</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</array>
|
||||
<key>7DD3BDE5-A157-42E5-9376-F681FB50A4EE</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>destinationuid</key>
|
||||
<string>3CE4BEC1-BEBE-4A11-B182-E63693AE2FE6</string>
|
||||
<key>modifiers</key>
|
||||
<integer>0</integer>
|
||||
<key>modifiersubtext</key>
|
||||
<string></string>
|
||||
<key>vitoclose</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</array>
|
||||
<key>98658166-32FC-4F9D-8247-52BEDE6394EE</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>destinationuid</key>
|
||||
<string>B55D209F-7FF3-4C23-AA39-BA8E37B91452</string>
|
||||
<key>modifiers</key>
|
||||
<integer>0</integer>
|
||||
<key>modifiersubtext</key>
|
||||
<string></string>
|
||||
<key>vitoclose</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</array>
|
||||
<key>B55D209F-7FF3-4C23-AA39-BA8E37B91452</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>destinationuid</key>
|
||||
<string>93437EDA-0308-467D-BE3F-6855C638D49D</string>
|
||||
<key>modifiers</key>
|
||||
<integer>0</integer>
|
||||
<key>modifiersubtext</key>
|
||||
<string></string>
|
||||
<key>sourceoutputuid</key>
|
||||
<string>F78DD511-9EC2-4272-8946-C6E3E269DE2E</string>
|
||||
<key>vitoclose</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>destinationuid</key>
|
||||
<string>529BC67C-C77D-41CA-B095-996CC1317556</string>
|
||||
<key>modifiers</key>
|
||||
<integer>0</integer>
|
||||
<key>modifiersubtext</key>
|
||||
<string></string>
|
||||
<key>vitoclose</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</array>
|
||||
<key>F05BFEA8-AEE7-486A-9A54-B3162A233BAC</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>destinationuid</key>
|
||||
<string>7DD3BDE5-A157-42E5-9376-F681FB50A4EE</string>
|
||||
<key>modifiers</key>
|
||||
<integer>0</integer>
|
||||
<key>modifiersubtext</key>
|
||||
<string></string>
|
||||
<key>vitoclose</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<key>createdby</key>
|
||||
<string>Denis Isidoro</string>
|
||||
<key>description</key>
|
||||
<string>Integration with navi, the interactive cheatsheet tool for the command-line</string>
|
||||
<key>disabled</key>
|
||||
<false/>
|
||||
<key>name</key>
|
||||
<string>navi</string>
|
||||
<key>objects</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>config</key>
|
||||
<dict>
|
||||
<key>externaltriggerid</key>
|
||||
<string>play</string>
|
||||
<key>passinputasargument</key>
|
||||
<false/>
|
||||
<key>passvariables</key>
|
||||
<true/>
|
||||
<key>workflowbundleid</key>
|
||||
<string>self</string>
|
||||
</dict>
|
||||
<key>type</key>
|
||||
<string>alfred.workflow.output.callexternaltrigger</string>
|
||||
<key>uid</key>
|
||||
<string>93437EDA-0308-467D-BE3F-6855C638D49D</string>
|
||||
<key>version</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>config</key>
|
||||
<dict>
|
||||
<key>alfredfiltersresults</key>
|
||||
<true/>
|
||||
<key>alfredfiltersresultsmatchmode</key>
|
||||
<integer>0</integer>
|
||||
<key>argumenttreatemptyqueryasnil</key>
|
||||
<false/>
|
||||
<key>argumenttrimmode</key>
|
||||
<integer>0</integer>
|
||||
<key>argumenttype</key>
|
||||
<integer>1</integer>
|
||||
<key>escaping</key>
|
||||
<integer>102</integer>
|
||||
<key>keyword</key>
|
||||
<string>n</string>
|
||||
<key>queuedelaycustom</key>
|
||||
<integer>3</integer>
|
||||
<key>queuedelayimmediatelyinitially</key>
|
||||
<false/>
|
||||
<key>queuedelaymode</key>
|
||||
<integer>0</integer>
|
||||
<key>queuemode</key>
|
||||
<integer>1</integer>
|
||||
<key>runningsubtext</key>
|
||||
<string>Generating results...</string>
|
||||
<key>script</key>
|
||||
<string>#!/bin/bash
|
||||
#
|
||||
# Alfred Script Filter JSON format
|
||||
#
|
||||
# This example demonstrates all fields available for populating results.
|
||||
#
|
||||
# For an in-depth explanation, use the (?) help button to the bottom left.
|
||||
#
|
||||
|
||||
_print_snippets() {
|
||||
|
||||
IFS=$'\n'
|
||||
|
||||
items="{\"items\": ["
|
||||
uid="$RANDOM"
|
||||
|
||||
snippets="$(cat uber.cheat | grep '^#' | sed 's/^# //')"
|
||||
|
||||
for line in $snippets; do
|
||||
|
||||
# title="$(echo "$line" | tr -d '"')"
|
||||
title="$(echo "$line" | tr -d '"')"
|
||||
uid="$title"
|
||||
|
||||
item=$(cat <<EOF
|
||||
{
|
||||
"uid": "$uid",
|
||||
"type": "file",
|
||||
"title": "$title",
|
||||
"subtitle": "subtitle",
|
||||
"arg": "echo <foo> <bar>",
|
||||
"autocomplete": "Desktop",
|
||||
"icon": {
|
||||
"type": "fileicon",
|
||||
"path": "~/Desktop"
|
||||
}
|
||||
},
|
||||
EOF
|
||||
)
|
||||
|
||||
items="$items $item"
|
||||
|
||||
done
|
||||
|
||||
echo "${items%?} ]}"
|
||||
|
||||
}
|
||||
|
||||
_interpolate() {
|
||||
|
||||
snippet="$1"
|
||||
|
||||
IFS=$'\n'
|
||||
|
||||
items="{\"items\": ["
|
||||
uid="$RANDOM"
|
||||
|
||||
snippets="foo
|
||||
bar"
|
||||
|
||||
for line in $snippets; do
|
||||
|
||||
# title="$(echo "$line" | tr -d '"')"
|
||||
title="$(echo "$line" | tr -d '"')"
|
||||
uid="$title"
|
||||
|
||||
item=$(cat <<EOF
|
||||
{
|
||||
"uid": "$uid",
|
||||
"type": "file",
|
||||
"title": "lorem",
|
||||
"subtitle": "subtitle",
|
||||
"arg": "ipsum",
|
||||
"autocomplete": "Desktop",
|
||||
"icon": {
|
||||
"type": "fileicon",
|
||||
"path": "~/Desktop"
|
||||
}
|
||||
},
|
||||
EOF
|
||||
)
|
||||
|
||||
items="$items $item"
|
||||
|
||||
done
|
||||
|
||||
echo "${items%?} ]}"
|
||||
|
||||
}
|
||||
|
||||
if [ -n "${snippet:-} "]; then
|
||||
_interpolate "$snippet"
|
||||
elif [ -n "${query:-} "]; then
|
||||
_interpolate "$query"
|
||||
else
|
||||
_print_snippets
|
||||
fi
|
||||
</string>
|
||||
<key>scriptargtype</key>
|
||||
<integer>1</integer>
|
||||
<key>scriptfile</key>
|
||||
<string>alfred.bash</string>
|
||||
<key>subtext</key>
|
||||
<string></string>
|
||||
<key>title</key>
|
||||
<string>navi</string>
|
||||
<key>type</key>
|
||||
<integer>8</integer>
|
||||
<key>withspace</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>type</key>
|
||||
<string>alfred.workflow.input.scriptfilter</string>
|
||||
<key>uid</key>
|
||||
<string>7DD3BDE5-A157-42E5-9376-F681FB50A4EE</string>
|
||||
<key>version</key>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>config</key>
|
||||
<dict>
|
||||
<key>triggerid</key>
|
||||
<string>play</string>
|
||||
</dict>
|
||||
<key>type</key>
|
||||
<string>alfred.workflow.trigger.external</string>
|
||||
<key>uid</key>
|
||||
<string>55C46852-4807-4374-95AB-CC055F4ECB7C</string>
|
||||
<key>version</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>config</key>
|
||||
<dict>
|
||||
<key>concurrently</key>
|
||||
<false/>
|
||||
<key>escaping</key>
|
||||
<integer>102</integer>
|
||||
<key>script</key>
|
||||
<string>echo asdsds</string>
|
||||
<key>scriptargtype</key>
|
||||
<integer>1</integer>
|
||||
<key>scriptfile</key>
|
||||
<string>alfred2.bash</string>
|
||||
<key>type</key>
|
||||
<integer>8</integer>
|
||||
</dict>
|
||||
<key>type</key>
|
||||
<string>alfred.workflow.action.script</string>
|
||||
<key>uid</key>
|
||||
<string>3CE4BEC1-BEBE-4A11-B182-E63693AE2FE6</string>
|
||||
<key>version</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>config</key>
|
||||
<dict>
|
||||
<key>conditions</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>inputstring</key>
|
||||
<string>{var:snippet}</string>
|
||||
<key>matchcasesensitive</key>
|
||||
<false/>
|
||||
<key>matchmode</key>
|
||||
<integer>4</integer>
|
||||
<key>matchstring</key>
|
||||
<string><[\w\d_\-]+></string>
|
||||
<key>outputlabel</key>
|
||||
<string>variable left</string>
|
||||
<key>uid</key>
|
||||
<string>F78DD511-9EC2-4272-8946-C6E3E269DE2E</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>elselabel</key>
|
||||
<string>no variables</string>
|
||||
</dict>
|
||||
<key>type</key>
|
||||
<string>alfred.workflow.utility.conditional</string>
|
||||
<key>uid</key>
|
||||
<string>B55D209F-7FF3-4C23-AA39-BA8E37B91452</string>
|
||||
<key>version</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>config</key>
|
||||
<dict>
|
||||
<key>argument</key>
|
||||
<string></string>
|
||||
<key>variables</key>
|
||||
<dict>
|
||||
<key>snippet</key>
|
||||
<string>{query}</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>type</key>
|
||||
<string>alfred.workflow.utility.argument</string>
|
||||
<key>uid</key>
|
||||
<string>98658166-32FC-4F9D-8247-52BEDE6394EE</string>
|
||||
<key>version</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>config</key>
|
||||
<dict>
|
||||
<key>concurrently</key>
|
||||
<true/>
|
||||
<key>escaping</key>
|
||||
<integer>0</integer>
|
||||
<key>script</key>
|
||||
<string>source $HOME/.bashrc
|
||||
eval "$snippet"</string>
|
||||
<key>scriptargtype</key>
|
||||
<integer>0</integer>
|
||||
<key>scriptfile</key>
|
||||
<string>navi</string>
|
||||
<key>type</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>type</key>
|
||||
<string>alfred.workflow.action.script</string>
|
||||
<key>uid</key>
|
||||
<string>529BC67C-C77D-41CA-B095-996CC1317556</string>
|
||||
<key>version</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>config</key>
|
||||
<dict>
|
||||
<key>action</key>
|
||||
<integer>0</integer>
|
||||
<key>argument</key>
|
||||
<integer>0</integer>
|
||||
<key>focusedappvariable</key>
|
||||
<false/>
|
||||
<key>focusedappvariablename</key>
|
||||
<string></string>
|
||||
<key>hotkey</key>
|
||||
<integer>6</integer>
|
||||
<key>hotmod</key>
|
||||
<integer>1966080</integer>
|
||||
<key>hotstring</key>
|
||||
<string>Z</string>
|
||||
<key>leftcursor</key>
|
||||
<false/>
|
||||
<key>modsmode</key>
|
||||
<integer>0</integer>
|
||||
<key>relatedAppsMode</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>type</key>
|
||||
<string>alfred.workflow.trigger.hotkey</string>
|
||||
<key>uid</key>
|
||||
<string>F05BFEA8-AEE7-486A-9A54-B3162A233BAC</string>
|
||||
<key>version</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>readme</key>
|
||||
<string></string>
|
||||
<key>uidata</key>
|
||||
<dict>
|
||||
<key>3CE4BEC1-BEBE-4A11-B182-E63693AE2FE6</key>
|
||||
<dict>
|
||||
<key>xpos</key>
|
||||
<integer>355</integer>
|
||||
<key>ypos</key>
|
||||
<integer>170</integer>
|
||||
</dict>
|
||||
<key>529BC67C-C77D-41CA-B095-996CC1317556</key>
|
||||
<dict>
|
||||
<key>xpos</key>
|
||||
<integer>835</integer>
|
||||
<key>ypos</key>
|
||||
<integer>285</integer>
|
||||
</dict>
|
||||
<key>55C46852-4807-4374-95AB-CC055F4ECB7C</key>
|
||||
<dict>
|
||||
<key>xpos</key>
|
||||
<integer>10</integer>
|
||||
<key>ypos</key>
|
||||
<integer>170</integer>
|
||||
</dict>
|
||||
<key>7DD3BDE5-A157-42E5-9376-F681FB50A4EE</key>
|
||||
<dict>
|
||||
<key>xpos</key>
|
||||
<integer>175</integer>
|
||||
<key>ypos</key>
|
||||
<integer>170</integer>
|
||||
</dict>
|
||||
<key>93437EDA-0308-467D-BE3F-6855C638D49D</key>
|
||||
<dict>
|
||||
<key>xpos</key>
|
||||
<integer>830</integer>
|
||||
<key>ypos</key>
|
||||
<integer>155</integer>
|
||||
</dict>
|
||||
<key>98658166-32FC-4F9D-8247-52BEDE6394EE</key>
|
||||
<dict>
|
||||
<key>xpos</key>
|
||||
<integer>535</integer>
|
||||
<key>ypos</key>
|
||||
<integer>200</integer>
|
||||
</dict>
|
||||
<key>B55D209F-7FF3-4C23-AA39-BA8E37B91452</key>
|
||||
<dict>
|
||||
<key>xpos</key>
|
||||
<integer>640</integer>
|
||||
<key>ypos</key>
|
||||
<integer>190</integer>
|
||||
</dict>
|
||||
<key>F05BFEA8-AEE7-486A-9A54-B3162A233BAC</key>
|
||||
<dict>
|
||||
<key>xpos</key>
|
||||
<integer>10</integer>
|
||||
<key>ypos</key>
|
||||
<integer>310</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>variablesdontexport</key>
|
||||
<array/>
|
||||
<key>version</key>
|
||||
<string>0.1.0</string>
|
||||
<key>webaddress</key>
|
||||
<string>https://github.com/denisidoro</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -35,14 +35,31 @@ release() {
|
|||
|
||||
cp "$bin_path" "$TAR_DIR"
|
||||
|
||||
cd "${NAVI_HOME}/target/tar"
|
||||
cd "$TAR_DIR"
|
||||
tar -czf navi.tar.gz *
|
||||
|
||||
}
|
||||
|
||||
workflow() {
|
||||
|
||||
WORKFLOW_DIR="${NAVI_HOME}/target/workflow"
|
||||
|
||||
cd "$NAVI_HOME"
|
||||
|
||||
rm -rf "${NAVI_HOME}/target" 2> /dev/null || true
|
||||
|
||||
mkdir -p "$WORKFLOW_DIR" 2> /dev/null || true
|
||||
|
||||
cp -r "${NAVI_HOME}/alfred" "$WORKFLOW_DIR"
|
||||
cd "$WORKFLOW_DIR"
|
||||
zip -r "navi.zip" "."
|
||||
|
||||
}
|
||||
|
||||
cmd="$1"
|
||||
shift
|
||||
|
||||
case "$cmd" in
|
||||
"release") release "$@" ;;
|
||||
"workflow") workflow "$@" ;;
|
||||
esac
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use crate::structures::item::Item;
|
||||
use crate::terminal;
|
||||
use regex::Regex;
|
||||
use std::cmp::max;
|
||||
|
|
@ -12,11 +13,10 @@ pub const LINE_SEPARATOR: &str = " \x15 ";
|
|||
pub const DELIMITER: &str = r" ⠀";
|
||||
|
||||
lazy_static! {
|
||||
pub static ref WIDTHS: (usize, usize) = get_widths();
|
||||
pub static ref NEWLINE_REGEX: Regex = Regex::new(r"\\\s+").expect("Invalid regex");
|
||||
}
|
||||
|
||||
fn get_widths() -> (usize, usize) {
|
||||
pub fn get_widths() -> (usize, usize) {
|
||||
let width = terminal::width();
|
||||
let tag_width = max(4, width * 20 / 100);
|
||||
let comment_width = max(4, width * 40 / 100);
|
||||
|
|
@ -57,23 +57,61 @@ fn limit_str(text: &str, length: usize) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn format_line(
|
||||
tags: &str,
|
||||
comment: &str,
|
||||
snippet: &str,
|
||||
tag_width: usize,
|
||||
comment_width: usize,
|
||||
) -> String {
|
||||
format!(
|
||||
pub trait Writer {
|
||||
fn write(&mut self, item: Item) -> String;
|
||||
}
|
||||
|
||||
pub struct FinderWriter {
|
||||
pub tag_width: usize,
|
||||
pub comment_width: usize,
|
||||
}
|
||||
|
||||
pub struct AlfredWriter {
|
||||
pub is_first: bool,
|
||||
}
|
||||
|
||||
impl Writer for FinderWriter {
|
||||
fn write(&mut self, item: Item) -> String {
|
||||
format!(
|
||||
"{tag_color}{tags_short}{delimiter}{comment_color}{comment_short}{delimiter}{snippet_color}{snippet_short}{delimiter}{tags}{delimiter}{comment}{delimiter}{snippet}{delimiter}\n",
|
||||
tags_short = limit_str(tags, tag_width),
|
||||
comment_short = limit_str(comment, comment_width),
|
||||
snippet_short = fix_newlines(snippet),
|
||||
tags_short = limit_str(item.tags, self.tag_width),
|
||||
comment_short = limit_str(item.comment, self.comment_width),
|
||||
snippet_short = fix_newlines(item.snippet),
|
||||
comment_color = color::Fg(COMMENT_COLOR),
|
||||
tag_color = color::Fg(TAG_COLOR),
|
||||
snippet_color = color::Fg(SNIPPET_COLOR),
|
||||
tags = tags,
|
||||
comment = comment,
|
||||
tags = item.tags,
|
||||
comment = item.comment,
|
||||
delimiter = DELIMITER,
|
||||
snippet = &snippet)
|
||||
snippet = &item.snippet)
|
||||
}
|
||||
}
|
||||
|
||||
fn escape_for_json(txt: &str) -> String {
|
||||
txt.replace('\\', "\\\\")
|
||||
.replace('"', "\\\"")
|
||||
.replace(NEWLINE_ESCAPE_CHAR, " ")
|
||||
}
|
||||
|
||||
impl Writer for AlfredWriter {
|
||||
fn write(&mut self, item: Item) -> String {
|
||||
let prefix = if self.is_first {
|
||||
self.is_first = false;
|
||||
""
|
||||
} else {
|
||||
","
|
||||
};
|
||||
|
||||
let tags = escape_for_json(item.tags);
|
||||
let comment = escape_for_json(item.comment);
|
||||
let snippet = escape_for_json(item.snippet);
|
||||
|
||||
format!(
|
||||
r#"{prefix}{{"type":"file","title":"{comment}","match":"{comment} {tags} {snippet}","subtitle":"{tags} :: {snippet}","variables":{{"tags":"{tags}","comment":"{comment}","snippet":"{snippet}"}},"autocomplete":"Desktop","icon":{{"type":"fileicon","path":"~/Desktop"}}}}"#,
|
||||
prefix = prefix,
|
||||
tags = tags,
|
||||
comment = comment,
|
||||
snippet = snippet
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
134
src/flows/alfred.rs
Normal file
134
src/flows/alfred.rs
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
use crate::parser;
|
||||
use crate::structures::cheat::Suggestion;
|
||||
|
||||
use crate::structures::{error::command::BashSpawnError, option::Config};
|
||||
use anyhow::Context;
|
||||
use anyhow::Error;
|
||||
use regex::Regex;
|
||||
|
||||
use std::env;
|
||||
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
lazy_static! {
|
||||
pub static ref VAR_REGEX: Regex = Regex::new(r"<(\w[\w\d\-_]*)>").expect("Invalid regex");
|
||||
}
|
||||
|
||||
pub fn main(config: Config) -> Result<(), Error> {
|
||||
let mut child = Command::new("cat").stdin(Stdio::piped()).spawn().unwrap();
|
||||
let stdin = child.stdin.as_mut().unwrap();
|
||||
|
||||
println!(r#"{{"items": ["#);
|
||||
|
||||
parser::read_all(&config, stdin).context("Failed to parse variables intended for finder")?;
|
||||
|
||||
let _ = child.wait_with_output().context("Failed to wait for fzf")?;
|
||||
|
||||
println!(r#"]}}"#);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prompt_with_suggestions(
|
||||
_variable_name: &str,
|
||||
_config: &Config,
|
||||
suggestion: &Suggestion,
|
||||
) -> Result<String, Error> {
|
||||
let (suggestion_command, _suggestion_opts) = suggestion;
|
||||
|
||||
let child = Command::new("bash")
|
||||
.stdout(Stdio::piped())
|
||||
.arg("-c")
|
||||
.arg(&suggestion_command)
|
||||
.spawn()
|
||||
.map_err(|e| BashSpawnError::new(suggestion_command, e))?;
|
||||
|
||||
let suggestions = String::from_utf8(
|
||||
child
|
||||
.wait_with_output()
|
||||
.context("Failed to wait and collect output from bash")?
|
||||
.stdout,
|
||||
)
|
||||
.context("Suggestions are invalid utf8")?;
|
||||
|
||||
Ok(suggestions)
|
||||
}
|
||||
|
||||
pub fn suggestions(config: Config) -> Result<(), Error> {
|
||||
let mut child = Command::new("cat")
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::null())
|
||||
.spawn()
|
||||
.unwrap();
|
||||
let stdin = child.stdin.as_mut().unwrap();
|
||||
|
||||
let variables = parser::read_all(&config, stdin)
|
||||
.context("Failed to parse variables intended for finder")?;
|
||||
|
||||
let tags = env::var("tags").unwrap();
|
||||
let _comment = env::var("comment").unwrap();
|
||||
let snippet = env::var("snippet").unwrap();
|
||||
|
||||
let varname = VAR_REGEX.captures_iter(&snippet).next();
|
||||
|
||||
if let Some(varname) = varname {
|
||||
let varname = &varname[0];
|
||||
let varname = &varname[1..varname.len() - 1];
|
||||
|
||||
println!(
|
||||
r#"{{"variables": {{"varname": "{varname}"}}, "items": ["#,
|
||||
varname = varname
|
||||
);
|
||||
|
||||
let lines = variables
|
||||
.get(&tags, &varname)
|
||||
.ok_or_else(|| anyhow!("No suggestions"))
|
||||
.and_then(|suggestion| {
|
||||
Ok(prompt_with_suggestions(&varname, &config, suggestion).unwrap())
|
||||
})?;
|
||||
|
||||
let mut is_first = true;
|
||||
for line in lines.split('\n') {
|
||||
if line.len() < 3 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let prefix = if is_first {
|
||||
is_first = false;
|
||||
""
|
||||
} else {
|
||||
","
|
||||
};
|
||||
|
||||
println!(
|
||||
r#"{prefix}{{
|
||||
"type": "file",
|
||||
"title": "{value}",
|
||||
"subtitle": "{snippet}",
|
||||
"autocomplete": "Desktop",
|
||||
"variables": {{
|
||||
"{varname}": "{value}"
|
||||
}},
|
||||
"icon": {{
|
||||
"type": "fileicon",
|
||||
"path": "~/Desktop"
|
||||
}}
|
||||
}}"#,
|
||||
prefix = prefix,
|
||||
snippet = snippet,
|
||||
varname = varname,
|
||||
value = line
|
||||
);
|
||||
}
|
||||
} else {
|
||||
println!(r#"{{"items": ["#);
|
||||
}
|
||||
|
||||
println!(r#"]}}"#);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn transform(_config: Config) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -213,8 +213,6 @@ fn with_new_lines(txt: String) -> String {
|
|||
}
|
||||
|
||||
pub fn main(variant: Variant, config: Config, contains_key: bool) -> Result<(), Error> {
|
||||
let _ = display::WIDTHS;
|
||||
|
||||
let opts =
|
||||
gen_core_finder_opts(variant, &config).context("Failed to generate finder options")?;
|
||||
let (raw_selection, variables) = config
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
pub mod alfred;
|
||||
pub mod aux;
|
||||
pub mod best;
|
||||
pub mod core;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::flows;
|
||||
use crate::flows::core::Variant;
|
||||
use crate::structures::option::Command::{Best, Fn, Preview, Query, Repo, Search, Widget};
|
||||
use crate::structures::option::{Config, RepoCommand};
|
||||
use crate::structures::option::Command::{Alfred, Best, Fn, Preview, Query, Repo, Search, Widget};
|
||||
use crate::structures::option::{AlfredCommand, Config, RepoCommand};
|
||||
use anyhow::Context;
|
||||
use anyhow::Error;
|
||||
|
||||
|
|
@ -39,6 +39,12 @@ pub fn handle_config(config: Config) -> Result<(), Error> {
|
|||
RepoCommand::Browse => flows::repo::browse(&config.finder)
|
||||
.context("Failed to browse featured cheatsheets"),
|
||||
},
|
||||
|
||||
Alfred { cmd } => match cmd {
|
||||
AlfredCommand::Start => flows::alfred::main(config),
|
||||
AlfredCommand::Suggestions => flows::alfred::suggestions(config),
|
||||
AlfredCommand::Transform => flows::alfred::transform(config),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
use crate::display;
|
||||
use crate::display::{self, Writer};
|
||||
use crate::filesystem;
|
||||
use crate::structures::cheat::VariableMap;
|
||||
use crate::structures::finder::{Opts as FinderOpts, SuggestionType};
|
||||
use crate::structures::fnv::HashLine;
|
||||
use crate::structures::{error::filesystem::InvalidPath, option::Config};
|
||||
use crate::structures::option::Command::Alfred;
|
||||
use crate::structures::{error::filesystem::InvalidPath, item::Item, option::Config};
|
||||
use crate::welcome;
|
||||
use anyhow::{Context, Error};
|
||||
use regex::Regex;
|
||||
|
|
@ -115,18 +116,19 @@ fn write_cmd(
|
|||
tags: &str,
|
||||
comment: &str,
|
||||
snippet: &str,
|
||||
tag_width: usize,
|
||||
comment_width: usize,
|
||||
writer: &mut Box<dyn Writer>,
|
||||
stdin: &mut std::process::ChildStdin,
|
||||
) -> Result<(), Error> {
|
||||
if snippet.len() <= 1 {
|
||||
Ok(())
|
||||
} else {
|
||||
let item = Item {
|
||||
tags: &tags,
|
||||
comment: &comment,
|
||||
snippet: &snippet,
|
||||
};
|
||||
stdin
|
||||
.write_all(
|
||||
display::format_line(&tags, &comment, &snippet, tag_width, comment_width)
|
||||
.as_bytes(),
|
||||
)
|
||||
.write_all(writer.write(item).as_bytes())
|
||||
.context("Failed to write command to finder's stdin")
|
||||
}
|
||||
}
|
||||
|
|
@ -135,6 +137,7 @@ fn read_file(
|
|||
path: &str,
|
||||
variables: &mut VariableMap,
|
||||
visited_lines: &mut HashSet<u64>,
|
||||
writer: &mut Box<dyn Writer>,
|
||||
stdin: &mut std::process::ChildStdin,
|
||||
) -> Result<(), Error> {
|
||||
let mut tags = String::from("");
|
||||
|
|
@ -142,8 +145,6 @@ fn read_file(
|
|||
let mut snippet = String::from("");
|
||||
let mut should_break = false;
|
||||
|
||||
let (tag_width, comment_width) = *display::WIDTHS;
|
||||
|
||||
for (line_nr, line_result) in filesystem::read_lines(path)?.enumerate() {
|
||||
let line = line_result
|
||||
.with_context(|| format!("Failed to read line nr.{} from `{}`", line_nr, path))?;
|
||||
|
|
@ -160,7 +161,7 @@ fn read_file(
|
|||
}
|
||||
// tag
|
||||
else if line.starts_with('%') {
|
||||
if write_cmd(&tags, &comment, &snippet, tag_width, comment_width, stdin).is_err() {
|
||||
if write_cmd(&tags, &comment, &snippet, writer, stdin).is_err() {
|
||||
should_break = true
|
||||
}
|
||||
snippet = String::from("");
|
||||
|
|
@ -175,7 +176,7 @@ fn read_file(
|
|||
}
|
||||
// comment
|
||||
else if line.starts_with('#') {
|
||||
if write_cmd(&tags, &comment, &snippet, tag_width, comment_width, stdin).is_err() {
|
||||
if write_cmd(&tags, &comment, &snippet, writer, stdin).is_err() {
|
||||
should_break = true
|
||||
}
|
||||
snippet = String::from("");
|
||||
|
|
@ -187,13 +188,13 @@ fn read_file(
|
|||
}
|
||||
// variable
|
||||
else if line.starts_with('$') {
|
||||
if write_cmd(&tags, &comment, &snippet, tag_width, comment_width, stdin).is_err() {
|
||||
if write_cmd(&tags, &comment, &snippet, writer, stdin).is_err() {
|
||||
should_break = true
|
||||
}
|
||||
snippet = String::from("");
|
||||
let (variable, command, opts) = parse_variable_line(&line).with_context(|| {
|
||||
format!(
|
||||
"Failed to parse variable line. See line nr.{} in cheatsheet `{}`",
|
||||
"Failed to parse variable line. See line number {} in cheatsheet `{}`",
|
||||
line_nr + 1,
|
||||
path
|
||||
)
|
||||
|
|
@ -216,7 +217,7 @@ fn read_file(
|
|||
}
|
||||
|
||||
if !should_break {
|
||||
let _ = write_cmd(&tags, &comment, &snippet, tag_width, comment_width, stdin);
|
||||
let _ = write_cmd(&tags, &comment, &snippet, writer, stdin);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
@ -233,10 +234,19 @@ pub fn read_all(
|
|||
let mut variables = VariableMap::new();
|
||||
let mut found_something = false;
|
||||
let mut visited_lines = HashSet::new();
|
||||
let mut writer: Box<dyn Writer> = if let Some(Alfred { .. }) = &config.cmd {
|
||||
Box::new(display::AlfredWriter { is_first: true })
|
||||
} else {
|
||||
let (tag_width, comment_width) = display::get_widths();
|
||||
Box::new(display::FinderWriter {
|
||||
tag_width,
|
||||
comment_width,
|
||||
})
|
||||
};
|
||||
let paths = filesystem::cheat_paths(config);
|
||||
|
||||
if paths.is_err() {
|
||||
welcome::cheatsheet(stdin);
|
||||
welcome::cheatsheet(&mut writer, stdin);
|
||||
return Ok(variables);
|
||||
}
|
||||
|
||||
|
|
@ -252,7 +262,14 @@ pub fn read_all(
|
|||
.to_str()
|
||||
.ok_or_else(|| InvalidPath(path.to_path_buf()))?;
|
||||
if path_str.ends_with(".cheat")
|
||||
&& read_file(path_str, &mut variables, &mut visited_lines, stdin).is_ok()
|
||||
&& read_file(
|
||||
path_str,
|
||||
&mut variables,
|
||||
&mut visited_lines,
|
||||
&mut writer,
|
||||
stdin,
|
||||
)
|
||||
.is_ok()
|
||||
&& !found_something
|
||||
{
|
||||
found_something = true;
|
||||
|
|
@ -263,7 +280,7 @@ pub fn read_all(
|
|||
}
|
||||
|
||||
if !found_something {
|
||||
welcome::cheatsheet(stdin);
|
||||
welcome::cheatsheet(&mut writer, stdin);
|
||||
}
|
||||
|
||||
Ok(variables)
|
||||
|
|
@ -300,7 +317,18 @@ mod tests {
|
|||
let mut child = Command::new("cat").stdin(Stdio::piped()).spawn().unwrap();
|
||||
let child_stdin = child.stdin.as_mut().unwrap();
|
||||
let mut visited_lines: HashSet<u64> = HashSet::new();
|
||||
read_file(path, &mut variables, &mut visited_lines, child_stdin).unwrap();
|
||||
let mut writer: Box<dyn Writer> = Box::new(display::FinderWriter {
|
||||
comment_width: 20,
|
||||
tag_width: 30,
|
||||
});
|
||||
read_file(
|
||||
path,
|
||||
&mut variables,
|
||||
&mut visited_lines,
|
||||
&mut writer,
|
||||
child_stdin,
|
||||
)
|
||||
.unwrap();
|
||||
let expected_suggestion = (
|
||||
r#" echo -e "$(whoami)\nroot" "#.to_string(),
|
||||
Some(FinderOpts {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use thiserror::Error;
|
|||
|
||||
#[derive(Error, Debug)]
|
||||
#[error(
|
||||
"\rHey listen! Navi encountered a problem.
|
||||
"\rHey, listen! Navi encountered a problem.
|
||||
Do you think this is a bug? File an issue at https://github.com/denisidoro/navi."
|
||||
)]
|
||||
pub struct FileAnIssue {
|
||||
|
|
|
|||
5
src/structures/item.rs
Normal file
5
src/structures/item.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
pub struct Item<'a> {
|
||||
pub tags: &'a str,
|
||||
pub comment: &'a str,
|
||||
pub snippet: &'a str,
|
||||
}
|
||||
|
|
@ -2,4 +2,5 @@ pub mod cheat;
|
|||
pub mod error;
|
||||
pub mod finder;
|
||||
pub mod fnv;
|
||||
pub mod item;
|
||||
pub mod option;
|
||||
|
|
|
|||
|
|
@ -106,6 +106,11 @@ pub enum Command {
|
|||
/// bash, zsh or fish
|
||||
shell: String,
|
||||
},
|
||||
/// Alfred
|
||||
Alfred {
|
||||
#[structopt(subcommand)]
|
||||
cmd: AlfredCommand,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
|
|
@ -119,6 +124,16 @@ pub enum RepoCommand {
|
|||
Browse,
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub enum AlfredCommand {
|
||||
/// Start
|
||||
Start,
|
||||
/// Suggestions
|
||||
Suggestions,
|
||||
/// Transform
|
||||
Transform,
|
||||
}
|
||||
|
||||
pub fn config_from_env() -> Config {
|
||||
Config::from_args()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,44 @@
|
|||
use crate::display;
|
||||
use crate::display::Writer;
|
||||
use crate::structures::item::Item;
|
||||
use std::io::Write;
|
||||
|
||||
fn add_msg(tag: &str, comment: &str, snippet: &str, stdin: &mut std::process::ChildStdin) {
|
||||
fn add_msg(
|
||||
tags: &str,
|
||||
comment: &str,
|
||||
snippet: &str,
|
||||
writer: &mut Box<dyn Writer>,
|
||||
stdin: &mut std::process::ChildStdin,
|
||||
) {
|
||||
let item = Item {
|
||||
tags: &tags,
|
||||
comment: &comment,
|
||||
snippet: &snippet,
|
||||
};
|
||||
stdin
|
||||
.write_all(display::format_line(tag, comment, snippet, 20, 60).as_bytes())
|
||||
.write_all(writer.write(item).as_bytes())
|
||||
.expect("Could not write to fzf's stdin");
|
||||
}
|
||||
|
||||
pub fn cheatsheet(stdin: &mut std::process::ChildStdin) {
|
||||
pub fn cheatsheet(writer: &mut Box<dyn Writer>, stdin: &mut std::process::ChildStdin) {
|
||||
add_msg(
|
||||
"cheatsheets",
|
||||
"Download default cheatsheets",
|
||||
"navi repo add denisidoro/cheats",
|
||||
writer,
|
||||
stdin,
|
||||
);
|
||||
add_msg(
|
||||
"cheatsheets",
|
||||
"Browse for cheatsheet repos",
|
||||
"navi repo browse",
|
||||
writer,
|
||||
stdin,
|
||||
);
|
||||
add_msg(
|
||||
"more info",
|
||||
"Read --help message",
|
||||
"navi --help",
|
||||
writer,
|
||||
stdin,
|
||||
);
|
||||
add_msg("more info", "Read --help message", "navi --help", stdin);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue