Skip to content

Added support for Zsh #1

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

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 110 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# 👇local👇bashrc
# 👇local👇(bash|zsh)rc

`.local.bashrc` files that are sourced in a clean bash process when entering
`.local.bashrc` or `.local.zshrc` files that are sourced in a clean process when entering
the directory.

*Replaces the [projector](https://github.com/bas080/projector) project.*
Expand All @@ -9,18 +9,20 @@ the directory.

- Makes a simple to define env variables, aliases and functions for a specific
project directory.
- Creates a bash history that is a separate from the root bash history.
- Creates a shell history that is a separate from the root shell history.
- Makes it easier to keep and find commands that are related to that project.
- Doesn't fill up the root bash history.
- Does not inherit env variables of other `.local.bashrc` directories when
- Doesn't fill up the root shell history.
- Does not inherit env variables of other `.local.bashrc`/`.local.zshrc` directories when
jumping directories.

## Setup

A quick setup script.
### Bash:

A quick setup script for bash.

```bash
curl 'https://raw.githubusercontent.com/bas080/dotlocaldotbashrc/master/dotlocaldotbashrc' > "$HOME/.dotlocaldotbashrc" &&
curl 'https://raw.githubusercontent.com/brianmatzelle/dotlocaldotrc/refs/heads/master/bash/dotlocaldotbashrc' > "$HOME/.dotlocaldotbashrc" &&
echo 'source "$HOME/.dotlocaldotbashrc"' >> ~/.bashrc
```

Expand All @@ -37,10 +39,36 @@ PS1="${BASHRC_HOME#$HOME/}:\W "
projects/dotlocaldotbashrc:dotlocaldotbashrc cd ../█
```

When using in repositores consider adding `.local.*` to your `.gitignore`.
When using in repositories consider adding `.local.*` to your `.gitignore`.

### Zsh:

A quick setup script for Z shell.

```zsh
curl 'https://raw.githubusercontent.com/brianmatzelle/dotlocaldotrc/refs/heads/master/zsh/dotlocaldotzshrc' > "$HOME/.dotlocaldotzshrc" &&
echo 'source "$HOME/.dotlocaldotzshrc"' >> ~/.zshrc
```

After re-sourcing your .zshrc you can create a `.local.zshrc` in a directory where you would like
a local zshrc by running `dotlocaldotzshrc init`.

Optionally you can show which local zshrc is currently active in your prompt.

```zsh
# What I use:
PS1="${ZSHRC_HOME#$HOME/}:\W "

# Looks like this:
projects/dotlocaldotzshrc:dotlocaldotzshrc cd ../█
```

When using in repositories consider adding `.local.*` to your `.gitignore`.

## Usage

### Bash

Quick start:

```text
Expand Down Expand Up @@ -104,15 +132,88 @@ $ cd ..
dotlocaldotbashrc: error: will not exit with jobs in the background.
```

### Zsh

Quick start:

```text
dotlocaldotzshrc [<subcommand>]
Create and configure directory specific zsh sessions.

Will source .local.zshrc when no subcommand is defined.

[subcommand]
init - Create a .local.zshrc in current directory.
edit - Opens the zshrc in $EDITOR and re-sources on exit.
```

More information:

```
$ cd projects/zshrc/
dotlocaldotzshrc: open /home/user/projects/zshrc
$ cd nested/
dotlocaldotzshrc: exit /home/user/projects/zshrc
dotlocaldotzshrc: open /home/user/projects/zshrc/nested
$ cd
dotlocaldotzshrc: exit /home/user/projects/zshrc/nested
$
```

> Notice that navigating to the home directory resulted in only a zshrc zsh
> session being exited. We just use the initial zsh process that was started
> when zshrc and the rest of the interactive shell was bootstrapped.

You can source the local .local.zshrc at anytime with `dotlocaldotzshrc` function.

```zsh
$ dotlocaldotzshrc
zshrc: source '/home/user/.local.zshrc'
$
```

You can quickly edit and automatically source after editor quit with
`dotlocaldotzshrc edit`.

One notable feature of `dotlocaldotzshrc` is its behavior when background jobs
are active. When attempting to exit the `dotlocaldotzshrc` session using the
`cd` command or when explicitly running the `dotlocaldotzshrc` command with the
`exit` option, the script checks for any background jobs in the current session.

If there are background jobs running, the script will display an error message
and refrain from exiting the session. This behavior ensures that you don't
unintentionally lose work by exiting a session with active background jobs.

```zsh
$ cd projects/myproject/
dotlocaldotzshrc: open /home/user/projects/myproject

# Start a background job
$ sleep 300 & # ctrl-z
[1] 12345

# Attempt to exit the dotlocaldotzshrc session
$ cd ..
dotlocaldotzshrc: error: will not exit with jobs in the background.
```

## Test

How to run the tests:

### Bash

```bash
SPAT_SHELL='bash' spat ./dotlocaldotbashrc.t
```

> Uses [spat](https://github.com/bas080/spat) to run expect tests.
### Zsh

```bash
SPAT_SHELL='zsh' spat ./dotlocaldotzshrc.t
```

Uses [spat](https://github.com/bas080/spat) to run expect tests.

## License

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
108 changes: 108 additions & 0 deletions zsh/dotlocaldotzshrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#!/usr/bin/env zsh

_zshrc_dir() {
local dir="$1"

test -f "$dir/.local.zshrc" && echo "$dir" && return 0
test '/' = "$dir" && return 1

_zshrc_dir "$(dirname "$1")"
}

ZSHRC_HOME="$(_zshrc_dir "$PWD")"

dotlocaldotzshrc () {
local subcommand;
if [ "$1" = "init" ]; then
subcommand="init"

if [ -f .local.zshrc ]; then
echo "dotlocaldotzshrc: error: $PWD already has a .local.zshrc"
return 1
fi

printf '#!/usr/bin/env zsh\n' >> .local.zshrc
"${EDITOR:-vi}" .local.zshrc
fi

if [ "$1" = "edit" ]; then
subcommand="edit"
if [ -z "$ZSHRC_HOME" ]; then
echo "dotlocaldotzshrc: error: cannot find a .local.zshrc"
return 1
fi

"${EDITOR:-vi}" "$ZSHRC_HOME/.local.zshrc"

# And re-source
dotlocaldotzshrc
fi

if [ -n "$1" ] && [ -z "$subcommand" ]; then
echo "dotlocaldotzshrc: error: $1 is not a valid command"
return 1
fi

_zshrc_dir "$PWD" || {
echo "dotlocaldotzshrc: error: no parent directory with .local.zshrc found"
return
}

ZSHRC_SOURCED="1"
echo "dotlocaldotzshrc: source '$ZSHRC_HOME/.local.zshrc'"
source "$ZSHRC_HOME/.local.zshrc"
}

lcd() {
cd "$ZSHRC_HOME/$1"
}

_zshrc_init() {
local zshrc_dir
local zshrc_temp

# Return early if no .local.zshrc is found.
zshrc_dir="$(_zshrc_dir "$PWD")"

test -n "$ZSHRC_TEMP" && test -z "$ZSHRC_SOURCED" && {
dotlocaldotzshrc
}

# The local zshrc path changed.
test "$zshrc_dir" != "$ZSHRC_HOME" && {

wait

# Do a fg to make sure you don't lose work
if [[ -n $(jobs -p) ]]; then
jobs
echo "dotlocaldotzshrc: error: will not exit with jobs in background."
return
fi

# Current zsh process is a local zshrc session.
test -n "$ZSHRC_TEMP" && {
echo "$PWD" > "$ZSHRC_TEMP"
echo "dotlocaldotzshrc: exit $ZSHRC_HOME"
exit
}

# Current zsh process is the original (root) session.
zshrc_temp="$(mktemp)"

echo "dotlocaldotzshrc: open $zshrc_dir"
HISTFILE="$zshrc_dir/.local.zsh_history" \
ZSHRC_TEMP="$zshrc_temp" \
zsh

test -s "$ZSHRC_TEMP" || exit
cd "$(cat "$ZSHRC_TEMP")" || true
_zshrc_init

}

}

autoload -Uz add-zsh-hook
add-zsh-hook precmd _zshrc_init

37 changes: 37 additions & 0 deletions zsh/dotlocaldotzshrc.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env zsh

EDITOR="cat"

{
source ./dotlocaldotzshrc

rm -r /tmp/dotlocaldotzshrc/test || true
mkdir -p /tmp/dotlocaldotzshrc/test/subdirectory
cd /tmp/dotlocaldotzshrc/test || exit

# Test Case 1: Initialize a new .local.zshrc
dotlocaldotzshrc init

# Validate that the .local.zshrc file has been created
test -f .local.zshrc

# Test Case 2: Attempt to initialize .local.zshrc again (expecting an error)
dotlocaldotzshrc init

echo 'echo hello .local.zshrc' >> .local.zshrc

# Test Case 3: Source the .local.zshrc file
dotlocaldotzshrc

# Test Case 4: Navigate to a subdirectory and source .local.zshrc
cd subdirectory

dotlocaldotzshrc

# Test Case 5: Exit the .local.zshrc session
cd ../../

_zshrc_init

dotlocaldotzshrc
} 2>&1 | sed "s#$PWD#/#g"
14 changes: 14 additions & 0 deletions zsh/dotlocaldotzshrc.t.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
rm: /tmp/dotlocaldotzshrc/test: No such file or directory
#!/usr/bin/env zsh
/tmp/dotlocaldotzshrc/test
dotlocaldotzshrc: source '/.local.zshrc'
dotlocaldotzshrc:source:39: no such file or directory: /.local.zshrc
dotlocaldotzshrc: error: /tmp/dotlocaldotzshrc/test already has a .local.zshrc
/tmp/dotlocaldotzshrc/test
dotlocaldotzshrc: source '/.local.zshrc'
dotlocaldotzshrc:source:39: no such file or directory: /.local.zshrc
/tmp/dotlocaldotzshrc/test
dotlocaldotzshrc: source '/.local.zshrc'
dotlocaldotzshrc:source:39: no such file or directory: /.local.zshrc
dotlocaldotzshrc: error: no parent directory with .local.zshrc found
(Exit Code: 0)