Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Enable zsh integration #107

Merged
merged 11 commits into from
Jun 4, 2018
61 changes: 0 additions & 61 deletions pkg/integration/bash.go
Original file line number Diff line number Diff line change
@@ -1,68 +1,7 @@
package integration

var bashSource = `
# Be careful! This runs in the user shell.

# Mask the command dad with this shell function
# This let us mutate the current shell
dad() {
# Prepare a file to pass the finalize actions
local finalizer_file
finalizer_file="$(mktemp /tmp/dad-finalize-XXXXXX)"

# Run the actual command
env DAD_FINALIZER_FILE=$finalizer_file dad $@
return_code=$?

# Perform finalizers
local fin
while read -r fin; do
[ -n "${DAD_DEBUG:-}" ] && echo "DAD_DEBUG: finalizer: ${fin}"

case "${fin}" in
cd:*)
cd "${fin//cd:/}"
;;
setenv:*)
export "${fin//setenv:/}"
;;
*)
;;
esac
done < "${finalizer_file}"
rm -f "${finalizer_file}"

return ${return_code}
}

__dad_prompt_command() {
# In shell hook mode, the command will use stderr to print in the console
# and stdout to mutate the shell (like activating a Python virtualenv)

# Fail fast if no dad executable is reachable
which dad > /dev/null || return

local hook_eval
hook_eval="$(command dad --shell-hook)"
[ -n "${DAD_DEBUG:-}" ] && echo -e "DAD_DEBUG: Hook eval:\n${hook_eval}\n---"
eval "${hook_eval}"
}

if [[ ! "${PROMPT_COMMAND:-}" == *__dad_prompt_command* ]]; then
PROMPT_COMMAND="__dad_prompt_command; ${PROMPT_COMMAND:-}"
fi

dad-enable-debug() {
export DAD_DEBUG=1
echo "DAD_DEBUG: enabled"
}

dad-disable-debug() {
unset DAD_DEBUG
echo "DAD_DEBUG: disable"
}

if [[ -n "${DAD_DEBUG:-}" ]]; then
echo "DAD_DEBUG: Dad is now enabled..."
fi
`
64 changes: 64 additions & 0 deletions pkg/integration/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package integration

var shellSource = `
# Be careful! This runs in the user shell.

# Mask the command dad with this shell function
# This let us mutate the current shell
dad() {
# Prepare a file to pass the finalize actions
local finalizer_file
finalizer_file="$(mktemp /tmp/dad-finalize-XXXXXX)"

# Run the actual command
env DAD_FINALIZER_FILE=$finalizer_file dad $@
return_code=$?

# Perform finalizers
local fin
while read -r fin; do
[ -n "${DAD_DEBUG:-}" ] && echo "DAD_DEBUG: finalizer: ${fin}"

case "${fin}" in
cd:*)
cd "${fin//cd:/}"
;;
setenv:*)
export "${fin//setenv:/}"
;;
*)
;;
esac
done < "${finalizer_file}"
rm -f "${finalizer_file}"

return ${return_code}
}

__dad_prompt_command() {
# In shell hook mode, the command will use stderr to print in the console
# and stdout to mutate the shell (like activating a Python virtualenv)

# Fail fast if no dad executable is reachable
which dad > /dev/null || return

local hook_eval
hook_eval="$(command dad --shell-hook)"
[ -n "${DAD_DEBUG:-}" ] && echo -e "DAD_DEBUG: Hook eval:\n${hook_eval}\n---"
eval "${hook_eval}"
}

dad-enable-debug() {
export DAD_DEBUG=1
echo "DAD_DEBUG: enabled"
}

dad-disable-debug() {
unset DAD_DEBUG
echo "DAD_DEBUG: disable"
}

if [[ -n "${DAD_DEBUG:-}" ]]; then
echo "DAD_DEBUG: Dad is now enabled..."
fi
`
13 changes: 12 additions & 1 deletion pkg/integration/integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,23 @@ package integration
import (
"fmt"
"os"
"strings"

color "github.com/logrusorgru/aurora"
)

func Print() {
fmt.Println(bashSource)
var currentShell = os.Getenv("SHELL")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just an idea, we could warn in case the value is empty, since it would be a very special case of "unknown shell".
That may help someone debug their environment someday.


fmt.Println(shellSource)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we print most of the source already if we have a major failure condition right after?

If we are running an unsupported shell, I think we should not print anything to stdout, because it will be eval'ed... by this unsupported shell.

I know I mentioned the general idea of preferring warnings rather than stopping execution...(because it could partially work and that could be enough for some people) but in this case, it looks like it could actually be very dangerous...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah you're right it should work without shell integration but we should not have a half one


if strings.HasSuffix(currentShell, "bash") {
fmt.Println(bashSource)
} else if strings.HasSuffix(currentShell, "zsh") {
fmt.Println(zshSource)
} else {
fmt.Println(color.Brown("Your shell is not supported"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The execution context is an eval:

eval "$(dad --shell-init --with-completion)"

So anything printed on the stdout will be eval'ed.
To display a warning, it must be printed on stderr.

}
}

func AddFinalizerCd(path string) error {
Expand Down
6 changes: 6 additions & 0 deletions pkg/integration/zsh.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package integration

var zshSource = `
prmptcmd() { $("__dad_prompt_command") }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to call the prompt command in a subshell with $(...) ?
Could we just call it directly? prmptcmd() { __dad_prompt_command }

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

promptcmd? (with a o)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes sadly it seems it needs to be called in a subshell cause if not, it would run as soon as you type prmptcmd() { __dad_prompt_command } and not everytime a prompt is being displayed

precmd_functions=(prmptcmd)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

precmd_functions+=(prmptcmd)
To preserve the other pre-command hooks.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could also be wrapped in a if statement in case dad is already activated

if [[ ! "${precmd_functions[@]}" == *__dad_prompt_command* ]]; then
  precmd_functions+=(prmptcmd)
fi

`