├── .travis.yml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── desk ├── examples ├── desk.sh ├── hello.fish ├── hello.sh ├── python_project.sh └── terraform.sh ├── screencap.gif ├── shell_plugins ├── bash │ └── desk ├── fish │ └── desk.fish └── zsh │ ├── _desk │ └── desk.plugin.zsh └── test ├── bashrc ├── run_tests.fish ├── run_tests.sh └── zshrc /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | language: bash 4 | 5 | services: 6 | - docker 7 | 8 | before_install: 9 | - wget http://ftp.debian.org/debian/pool/main/s/shellcheck/shellcheck_0.4.4-4_amd64.deb 10 | - ar x shellcheck_0.4.4-4_amd64.deb 11 | - tar xvf data.tar.xz 12 | - cp ./usr/bin/shellcheck . 13 | 14 | env: 15 | - PATH=$PATH:$(pwd) 16 | 17 | script: 18 | - SHELL_CMD='-c ./run_tests.sh' make bash zsh 19 | - SHELL_CMD='-c ./run_tests.fish' make fish 20 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:14.04 2 | MAINTAINER desk 3 | 4 | ENV USERNAME desktester 5 | RUN adduser --disabled-password --gecos "" desktester 6 | RUN apt-get update && apt-get install -y vim expect zsh fish 7 | 8 | WORKDIR /home/$USERNAME/ 9 | ADD desk /usr/local/bin/desk 10 | ADD test/zshrc .zshrc 11 | ADD test/bashrc .bashrc 12 | ADD test/run_tests.sh run_tests.sh 13 | ADD test/run_tests.fish run_tests.fish 14 | ADD examples examples 15 | RUN mkdir -p .config/fish && touch .config/fish/config.fish 16 | 17 | # Set up test Deskfile 18 | RUN mkdir -p example-project && cp examples/hello.sh example-project/Deskfile 19 | 20 | RUN chown -R $USERNAME:$USERNAME .zshrc example-project examples run_tests.fish run_tests.sh .bashrc .config 21 | 22 | USER $USERNAME 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 James O'Beirne 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: install 2 | install: 3 | cp ./desk /usr/local/bin/desk 4 | 5 | .PHONY: uninstall 6 | uninstall: 7 | rm /usr/local/bin/desk 8 | 9 | .PHONY: oh-my-zsh 10 | oh-my-zsh: 11 | ln -s $(shell pwd)/shell_plugins/zsh $(HOME)/.oh-my-zsh/custom/plugins/desk 12 | 13 | 14 | # Test targets 15 | # ------------ 16 | 17 | .PHONY: dockerbuild 18 | dockerbuild: 19 | docker build -t desk/desk . 20 | 21 | SHELL_CMD?= 22 | 23 | .PHONY: bash 24 | bash: dockerbuild 25 | docker run -it desk/desk /bin/bash $(SHELL_CMD) 26 | 27 | .PHONY: zsh 28 | zsh: dockerbuild 29 | docker run -it desk/desk /usr/bin/zsh $(SHELL_CMD) 30 | 31 | .PHONY: fish 32 | fish: dockerbuild 33 | docker run -it desk/desk /usr/bin/fish $(SHELL_CMD) 34 | 35 | .PHONY: lint 36 | lint: 37 | shellcheck -e SC2155 desk 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # ◲ desk 3 | 4 | [![build](https://api.travis-ci.org/jamesob/desk.svg?branch=master)](https://travis-ci.org/jamesob/desk) [![Join the chat at https://gitter.im/jamesob/desk](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/jamesob/desk?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 5 | 6 | Lightweight workspace manager for the shell. 7 | 8 | Desk makes it easy to flip back and forth between different project contexts in 9 | your favorite shell. Change directory, activate a virtualenv or rvm, load 10 | in domain-specific aliases, environment variables, functions, arbitrary shell files, 11 | all in a single command. 12 | 13 | Instead of relying on `CTRL-R` to execute and recall ("that command's gotta 14 | be here somewhere..."), desk helps shorten and document those actions with 15 | shell aliases and functions, which are then namespaced under a particular 16 | desk. 17 | 18 | Because Deskfiles are just enriched shell scripts, the possibilities are 19 | endless. For example, when doing work on AWS I have desk 20 | securely load AWS API keys into environment variables via 21 | [`pass`](https://www.passwordstore.org/) -- no effort on my part, and no 22 | risk of accidentally persisting that sensitive information to a history file. 23 | 24 | 25 | 26 | I have a hard time calling this a "workspace manager" with a straight 27 | face -- it's basically just a shell script that sources another shell script in a new shell. 28 | But I often find myself working in multiple different code trees simultaneously: 29 | the quick context switches and namespaced commands that desk facilitates 30 | have proven useful. 31 | 32 | There are no dependencies other than `bash`. Desk is explicitly tested with `bash`, 33 | `zsh`, and `fish`. 34 | 35 | ```sh 36 | ◲ desk 0.6.0 37 | 38 | Usage: 39 | 40 | desk 41 | List the current desk and any associated aliases. If no desk 42 | is being used, display available desks. 43 | desk init 44 | Initialize desk configuration. 45 | desk (list|ls) 46 | List all desks along with a description. 47 | desk (.|go) [ [shell-args...]] 48 | Activate a desk. Extra arguments are passed onto shell. If called with 49 | no arguments, look for a Deskfile in the current directory. If not a 50 | recognized desk, try as a path to directory containing a Deskfile. 51 | desk run 52 | Run a command within a desk's environment then exit. Think '$SHELL -c'. 53 | desk edit [desk-name] 54 | Edit (or create) a deskfile with the name specified, otherwise 55 | edit the active deskfile. 56 | desk help 57 | Show this text. 58 | desk version 59 | Show version information. 60 | 61 | Since desk spawns a shell, to deactivate and "pop" out a desk, you 62 | simply need to exit or otherwise end the current shell process. 63 | ``` 64 | 65 | For example, given this deskfile (`~/.desk/desks/tf.sh`): 66 | ```sh 67 | # tf.sh 68 | # 69 | # Description: desk for doing work on a terraform-based repository 70 | # 71 | 72 | cd ~/terraform-repo 73 | 74 | # Set up AWS env variables: 75 | set_aws_env() { 76 | export AWS_ACCESS_KEY_ID="$1" 77 | export AWS_SECRET_ACCESS_KEY="$2" 78 | } 79 | 80 | # Run `terraform plan` with proper AWS var config 81 | plan() { 82 | terraform plan -module-depth=-1 \ 83 | -var "access_key=${AWS_ACCESS_KEY_ID}" \ 84 | -var "secret_key=${AWS_SECRET_ACCESS_KEY}" 85 | } 86 | 87 | # Run `terraform apply` with proper AWS var config 88 | alias apply='terraform apply' 89 | ``` 90 | 91 | we'd get 92 | 93 | ```sh 94 | $ desk . tf 95 | $ desk 96 | 97 | tf 98 | desk for doing work on a terraform repo 99 | 100 | set_aws_env Set up AWS env variables: 101 | plan Run `terraform plan` with proper AWS var config 102 | apply Run `terraform apply` with proper AWS var config 103 | ``` 104 | 105 | Basically, desk just associates a shell script (`name.sh`) with a name. When 106 | you call `desk . name`, desk drops you into a shell where `name.sh` has been 107 | executed, and then desk extracts out certain comments in `name.sh` for useful 108 | rendering. 109 | 110 | ### Installing 111 | 112 | #### With homebrew 113 | 114 | `brew install desk` 115 | 116 | #### With curl 117 | 118 | Assuming `~/bin` exists and is on the PATH... otherwise, substitute `/usr/local/bin` 119 | and add `sudo` as needed. 120 | 121 | 0. `curl https://raw.githubusercontent.com/jamesob/desk/master/desk > ~/bin/desk` 122 | 0. `chmod +x ~/bin/desk` 123 | 124 | #### With git 125 | 126 | 0. `git clone git@github.com:jamesob/desk.git && cd desk && sudo make install` 127 | 128 | After that, run `desk init` and start adding deskfiles with either `desk edit [deskfile name]` 129 | or by manually adding shell scripts into your deskfiles directory (by default `~/.desk/desks/`). 130 | 131 | ### Enabling shell extensions 132 | 133 | **NB**: Shell extensions are automatically enabled if Desk is installed via Homebrew. 134 | 135 | #### Using bash 136 | 137 | 0. Add `source /path/to/desk/repo/shell_plugins/bash/desk` to your `.bashrc`. 138 | 139 | #### Using fish 140 | 141 | ```fish 142 | mkdir -p ~/.config/fish/completions 143 | cp /path/to/desk/repo/shell_plugins/fish/desk.fish ~/.config/fish/completions 144 | ``` 145 | 146 | #### Using zsh 147 | 148 | 0. Add `fpath=(/path/to/desk/repo/shell_plugins/zsh $fpath)` to your `.zshrc`. 149 | 150 | 151 | Optionally, use one of the zsh plugin frameworks mentioned below. 152 | 153 | #### Using [oh-my-zsh](http://ohmyz.sh/) 154 | 155 | 0. `make oh-my-zsh` from within this repo. This sets up a symlink. 156 | 157 | or 158 | 159 | 0. `cd ~/.oh-my-zsh/custom/plugins` 160 | 0. `git clone git@github.com:jamesob/desk.git /tmp/desk && cp -r /tmp/desk/shell_plugins/zsh desk` 161 | 0. Add desk to your plugin list 162 | 163 | #### Using [Antigen](https://github.com/zsh-users/antigen) 164 | 165 | 0. Add `antigen bundle jamesob/desk shell_plugins/zsh` to your `.zshrc` 166 | 0. Open a new terminal window. Antigen will clone the desk repo and add it to your path. 167 | 168 | #### Using [zgen](https://github.com/tarjoilija/zgen) 169 | 170 | 0. Add `zgen load jamesob/desk shell_plugins/zsh` to your `.zshrc` with your other load commands 171 | 0. `rm ~/.zgen/init.zsh` 172 | 0. Start a new shell; zgen will generate a new `init.zsh` and automatically clone the desk repository for you and add it to your path. 173 | 174 | ### Deskfile rules 175 | 176 | Deskfiles are just shell scripts, nothing more, that live in the desk config directory. 177 | Desk does pay attention to certain kinds of comments, though. 178 | 179 | - *description*: you can describe a deskfile by including `# Description: ...` 180 | somewhere in the file. 181 | 182 | - *alias and function docs*: if the line above an alias or function is a 183 | comment, it will be used as documentation. 184 | 185 | ### Deskfiles 186 | 187 | Deskfiles are just shell scripts at the root of a project directory that 188 | adhere to the conventions above. These can be put into version control to 189 | formalize and ease common development tasks like running tests or doing 190 | local builds. 191 | 192 | For example, if we have some directory or repository called `myproject`, if 193 | we create `myproject/Deskfile`, we'll be able to do any of the following: 194 | ```sh 195 | $ desk go /path/to/myproject/ 196 | $ desk go /path/to/myproject/Deskfile 197 | myproject/ $ desk go . 198 | myproject/ $ desk go 199 | ``` 200 | 201 | ### Sharing deskfiles across computers 202 | 203 | Of course, the desk config directory (by default `~/.desks`) can be a symlink 204 | so that deskfiles can be stored in some centralized place, like Dropbox, 205 | and so shared across many computers. 206 | 207 | ### Using a non-default config location 208 | 209 | By default, desk configuration lives in `~/.desk` (`$DESK_DIR`) and deskfiles 210 | live in `~/.desk/desks` (`$DESK_DESKS_DIR`). If you want to use some other 211 | location, specify as much in `desk init` and then ensure you set `$DESK_DIR` 212 | and/or `$DESK_DESKS_DIR` to match in your shell's rc file. 213 | 214 | ### Shortening invocation 215 | 216 | Typing `desk .` frequently can get old; personally, I like to alias this with 217 | ```sh 218 | alias d.='desk .' 219 | ``` 220 | in my shell rc file. 221 | 222 | ### Usage with OS X 223 | 224 | Desk won't work when used strictly with `~/.bash_profile` on OS X's terminal, since 225 | the content of `~/.bash_profile` is only executed on *login*, not shell creation, as 226 | explained [here](http://www.joshstaiger.org/archives/2005/07/bash_profile_vs.html). 227 | 228 | My recommendation is to use `~/.bashrc` as your general-purpose config file, then simply 229 | have `~/.bash_profile` point to it: 230 | ```sh 231 | # ~/.bash_profile 232 | 233 | if [ -f ~/.bashrc ]; then 234 | source ~/.bashrc 235 | fi 236 | ``` 237 | 238 | ### Related projects 239 | 240 | - [godesk](https://github.com/hamin/godesk) by @hamin: a desk launcher with fuzzy filtering 241 | 242 | 243 | ### Tips accepted 244 | 245 | ![18ehgMUJBqKc2Eyi6WHiMwHFwA8kobYEhy](http://i.imgur.com/KAfUPA6.png) 246 | 247 | BTC: `18ehgMUJBqKc2Eyi6WHiMwHFwA8kobYEhy` 248 | 249 | Half of all tips will be donated to [an organization providing aid to Syrian refugees](http://www.moas.eu/). 250 | 251 | #### Donations made on behalf of tippers 252 | 253 | | date | amount | organization | 254 | | ---- | ------ | ------------ | 255 | | 2015-11-18 | $1.07 | http://moas.eu | 256 | | 2016-11-14 | $21.00 | http://moas.eu | 257 | -------------------------------------------------------------------------------- /desk: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # vim: set filetype=sh: 3 | 4 | PREFIX="${DESK_DIR:-$HOME/.desk}" 5 | DESKS="${DESK_DESKS_DIR:-$PREFIX/desks}" 6 | DESKFILE_NAME=Deskfile 7 | 8 | 9 | ## Commands 10 | 11 | cmd_version() { 12 | echo "◲ desk 0.6.0" 13 | } 14 | 15 | 16 | cmd_usage() { 17 | cmd_version 18 | echo 19 | cat <<_EOF 20 | Usage: 21 | 22 | $PROGRAM 23 | List the current desk and any associated aliases. If no desk 24 | is being used, display available desks. 25 | $PROGRAM init 26 | Initialize desk configuration. 27 | $PROGRAM (list|ls) 28 | List all desks along with a description. 29 | $PROGRAM (.|go) [ [shell-args...]] 30 | Activate a desk. Extra arguments are passed onto shell. If called with 31 | no arguments, look for a Deskfile in the current directory. If not a 32 | recognized desk, try as a path to directory containing a Deskfile. 33 | $PROGRAM run '' 34 | $PROGRAM run ... 35 | Run a command within a desk's environment then exit. In the first form 36 | shell expansion is performed. In the second form, the argument vector 37 | is executed as is. 38 | $PROGRAM edit [desk-name] 39 | Edit (or create) a deskfile with the name specified, otherwise 40 | edit the active deskfile. 41 | $PROGRAM help 42 | Show this text. 43 | $PROGRAM version 44 | Show version information. 45 | 46 | Since desk spawns a shell, to deactivate and "pop" out a desk, you 47 | simply need to exit or otherwise end the current shell process. 48 | _EOF 49 | } 50 | 51 | cmd_init() { 52 | if [ -d "$PREFIX" ]; then 53 | echo "Desk dir already exists at ${PREFIX}" 54 | exit 1 55 | fi 56 | read -p "Where do you want to store your deskfiles? (default: ${PREFIX}): " \ 57 | NEW_PREFIX 58 | [ -z "${NEW_PREFIX}" ] && NEW_PREFIX="$PREFIX" 59 | 60 | if [ ! -d "${NEW_PREFIX}" ]; then 61 | echo "${NEW_PREFIX} doesn't exist, attempting to create." 62 | mkdir -p "$NEW_PREFIX/desks" 63 | fi 64 | 65 | local SHELLTYPE=$(get_running_shell) 66 | 67 | case "${SHELLTYPE}" in 68 | bash) local SHELLRC="${HOME}/.bashrc" ;; 69 | fish) local SHELLRC="${HOME}/.config/fish/config.fish" ;; 70 | zsh) local SHELLRC="${HOME}/.zshrc" ;; 71 | esac 72 | 73 | read -p "Where's your shell rc file? (default: ${SHELLRC}): " \ 74 | USER_SHELLRC 75 | [ -z "${USER_SHELLRC}" ] && USER_SHELLRC="$SHELLRC" 76 | if [ ! -f "$USER_SHELLRC" ]; then 77 | echo "${USER_SHELLRC} doesn't exist" 78 | exit 1 79 | fi 80 | 81 | printf "\n# Hook for desk activation\n" >> "$USER_SHELLRC" 82 | 83 | # Since the hook is appended to the rc file, its exit status becomes 84 | # the exit status of `source $USER_SHELLRC` which typically 85 | # indicates if something went wrong. If $DESK_ENV is void, `test` 86 | # sets exit status to 1. That, however, is part of desk's normal 87 | # operation, so we clear exit status after that. 88 | if [ "$SHELLTYPE" == "fish" ]; then 89 | echo "test -n \"\$DESK_ENV\"; and . \"\$DESK_ENV\"; or true" >> "$USER_SHELLRC" 90 | else 91 | echo "[ -n \"\$DESK_ENV\" ] && source \"\$DESK_ENV\" || true" >> "$USER_SHELLRC" 92 | fi 93 | 94 | echo "Done. Start adding desks to ${NEW_PREFIX}/desks!" 95 | } 96 | 97 | 98 | cmd_go() { 99 | # TODESK ($1) may either be 100 | # 101 | # - the name of a desk in $DESKS/ 102 | # - a path to a Deskfile 103 | # - a directory containing a Deskfile 104 | # - empty -> `./Deskfile` 105 | # 106 | local TODESK="$1" 107 | local DESKEXT=$(get_deskfile_extension) 108 | local DESKPATH="$(find "${DESKS}/" -name "${TODESK}${DESKEXT}" 2>/dev/null)" 109 | 110 | local POSSIBLE_DESKFILE_DIR="${TODESK%$DESKFILE_NAME}" 111 | if [ -z "$POSSIBLE_DESKFILE_DIR" ]; then 112 | POSSIBLE_DESKFILE_DIR="." 113 | fi 114 | 115 | # If nothing could be found in $DESKS/, check to see if this is a path to 116 | # a Deskfile. 117 | if [[ -z "$DESKPATH" && -d "$POSSIBLE_DESKFILE_DIR" ]]; then 118 | if [ ! -f "${POSSIBLE_DESKFILE_DIR}/${DESKFILE_NAME}" ]; then 119 | echo "No Deskfile found in '${POSSIBLE_DESKFILE_DIR}'" 120 | exit 1 121 | fi 122 | 123 | local REALPATH=$( cd $POSSIBLE_DESKFILE_DIR && pwd ) 124 | DESKPATH="${REALPATH}/${DESKFILE_NAME}" 125 | TODESK=$(basename "$REALPATH") 126 | fi 127 | 128 | # Shift desk name so we can forward on all arguments to the shell. 129 | shift; 130 | 131 | if [ -z "$DESKPATH" ]; then 132 | echo "Desk $TODESK (${TODESK}${DESKEXT}) not found in $DESKS" 133 | exit 1 134 | else 135 | local SHELL_EXEC="$(get_running_shell)" 136 | DESK_NAME="${TODESK}" DESK_ENV="${DESKPATH}" exec "${SHELL_EXEC}" "$@" 137 | fi 138 | } 139 | 140 | 141 | cmd_run() { 142 | local TODESK="$1" 143 | shift; 144 | if [ $# -eq 1 ]; then 145 | cmd_go "$TODESK" -ic "$1" 146 | else 147 | cmd_go "$TODESK" -ic '"$@"' -- "$@" 148 | fi 149 | } 150 | 151 | 152 | # Usage: desk list [options] 153 | # Description: List all desks along with a description 154 | # --only-names: List only the names of the desks 155 | # --no-format: Use ' - ' to separate names from descriptions 156 | cmd_list() { 157 | if [ ! -d "${DESKS}/" ]; then 158 | echo "No desk dir! Run 'desk init'." 159 | exit 1 160 | fi 161 | local SHOW_DESCRIPTIONS DESKEXT AUTO_ALIGN name desc len longest out 162 | 163 | while [[ $# -gt 0 ]]; do 164 | case "$1" in 165 | --only-names) SHOW_DESCRIPTIONS=false && AUTO_ALIGN=false ;; 166 | --no-format) AUTO_ALIGN=false ;; 167 | esac 168 | shift 169 | done 170 | 171 | DESKEXT=$(get_deskfile_extension) 172 | out="" 173 | longest=0 174 | 175 | while read -d '' -r f; do 176 | name=$(basename "${f/${DESKEXT}//}") 177 | if [[ "$SHOW_DESCRIPTIONS" = false ]]; then 178 | out+="$name"$'\n' 179 | else 180 | desc=$(echo_description "$f") 181 | out+="$name - $desc"$'\n' 182 | len=${#name} 183 | (( len > longest )) && longest=$len 184 | fi 185 | done < <(find "${DESKS}/" -name "*${DESKEXT}" -print0) 186 | if [[ "$AUTO_ALIGN" != false ]]; then 187 | print_aligned "$out" "$longest" 188 | else 189 | printf "%s" "$out" 190 | fi 191 | } 192 | 193 | # Usage: desk [options] 194 | # Description: List the current desk and any associated aliases. If no desk is being used, display available desks 195 | # --no-format: Use ' - ' to separate alias/export/function names from their descriptions 196 | cmd_current() { 197 | if [ -z "$DESK_ENV" ]; then 198 | printf "No desk activated.\n\n%s" "$(cmd_list)" 199 | exit 1 200 | fi 201 | 202 | local DESKPATH=$DESK_ENV 203 | local CALLABLES=$(get_callables "$DESKPATH") 204 | local AUTO_ALIGN len longest out 205 | 206 | while [[ $# -gt 0 ]]; do 207 | case "$1" in 208 | --no-format) AUTO_ALIGN=false ;; 209 | esac 210 | shift 211 | done 212 | printf "%s - %s\n" "$DESK_NAME" "$(echo_description "$DESKPATH")" 213 | 214 | if [[ -n "$CALLABLES" ]]; then 215 | 216 | longest=0 217 | out=$'\n' 218 | for NAME in $CALLABLES; do 219 | # Last clause in the grep regexp accounts for fish functions. 220 | len=$((${#NAME} + 2)) 221 | (( len > longest )) && longest=$len 222 | local DOCLINE=$( 223 | grep -B 1 -E \ 224 | "^(alias ${NAME}=|export ${NAME}=|(function )?${NAME}( )?\()|function $NAME" "$DESKPATH" \ 225 | | grep "#") 226 | 227 | if [ -z "$DOCLINE" ]; then 228 | out+=" ${NAME}"$'\n' 229 | else 230 | out+=" ${NAME} - ${DOCLINE##\# }"$'\n' 231 | fi 232 | done 233 | 234 | if [[ "$AUTO_ALIGN" != false ]]; then 235 | print_aligned "$out" "$longest" 236 | else 237 | printf "%s" "$out" 238 | fi 239 | fi 240 | } 241 | 242 | cmd_edit() { 243 | if [ $# -eq 0 ]; then 244 | if [ "$DESK_NAME" == "" ]; then 245 | echo "No desk activated." 246 | exit 3 247 | fi 248 | local DESKNAME_TO_EDIT="$DESK_NAME" 249 | elif [ $# -eq 1 ]; then 250 | local DESKNAME_TO_EDIT="$1" 251 | else 252 | echo "Usage: ${PROGRAM} edit [desk-name]" 253 | exit 1 254 | fi 255 | 256 | local DESKEXT=$(get_deskfile_extension) 257 | local EDIT_PATH="${DESKS}/${DESKNAME_TO_EDIT}${DESKEXT}" 258 | 259 | ${EDITOR:-vi} "$EDIT_PATH" 260 | } 261 | 262 | ## Utilities 263 | 264 | FNAME_CHARS='[a-zA-Z0-9_-]' 265 | 266 | # Echo the description of a desk. $1 is the deskfile. 267 | echo_description() { 268 | local descline=$(grep -E "#\s+Description" "$1") 269 | echo "${descline##*Description: }" 270 | } 271 | 272 | # Echo a list of aliases, exports, and functions for a given desk 273 | get_callables() { 274 | local DESKPATH=$1 275 | grep -E "^(alias |export |(function )?${FNAME_CHARS}+ ?\()|function $NAME" "$DESKPATH" \ 276 | | sed 's/alias \([^= ]*\)=.*/\1/' \ 277 | | sed 's/export \([^= ]*\)=.*/\1/' \ 278 | | sed -E "s/(function )?(${FNAME_CHARS}+) ?\(\).*/\2/" \ 279 | | sed -E "s/function (${FNAME_CHARS}+).*/\1/" 280 | } 281 | 282 | get_running_shell() { 283 | # Echo the name of the parent shell via procfs, if we have it available. 284 | # Otherwise, try to use ps with bash's parent pid. 285 | if [ -e /proc ]; then 286 | # Get cmdline procfile of the process running this script. 287 | local CMDLINE_FILE="/proc/$(grep PPid /proc/$$/status | cut -f2)/cmdline" 288 | 289 | if [ -f "$CMDLINE_FILE" ]; then 290 | # Strip out any verion that may be attached to the shell executable. 291 | # Strip leading dash for login shells. 292 | local CMDLINE_SHELL=$(sed -r -e 's/\x0.*//' -e 's/^-//' "$CMDLINE_FILE") 293 | fi 294 | else 295 | # Strip leading dash for login shells. 296 | # If the parent process is a login shell, guess bash. 297 | local CMDLINE_SHELL=$(ps -o comm= -p $PPID | tail -1 | sed -e 's/login/bash/' -e 's/^-//') 298 | fi 299 | 300 | if [ ! -z "$CMDLINE_SHELL" ]; then 301 | basename "$CMDLINE_SHELL" 302 | exit 303 | fi 304 | 305 | # Fall back to $SHELL otherwise. 306 | basename "$SHELL" 307 | exit 308 | } 309 | 310 | # Echo the extension of recognized deskfiles (.fish for fish) 311 | get_deskfile_extension() { 312 | if [ "$(get_running_shell)" == "fish" ]; then 313 | echo '.fish' 314 | else 315 | echo '.sh' 316 | fi 317 | } 318 | 319 | # Echo first argument as key/value pairs aligned into two columns; second argument is the longest key 320 | print_aligned() { 321 | local out=$1 longest=$2 322 | printf "%s" "$out" | awk -v padding="$longest" -F' - ' '{ 323 | printf "%-*s %s\n", padding, $1, substr($0, index($0, " - ")+2, length($0)) 324 | }' 325 | } 326 | 327 | 328 | PROGRAM="${0##*/}" 329 | 330 | case "$1" in 331 | init) shift; cmd_init "$@" ;; 332 | help|--help) shift; cmd_usage "$@" ;; 333 | version|--version) shift; cmd_version "$@" ;; 334 | ls|list) shift; cmd_list "$@" ;; 335 | go|.) shift; cmd_go "$@" ;; 336 | run) shift; cmd_run "$@" ;; 337 | edit) shift; cmd_edit "$@" ;; 338 | *) cmd_current "$@" ;; 339 | esac 340 | exit 0 341 | -------------------------------------------------------------------------------- /examples/desk.sh: -------------------------------------------------------------------------------- 1 | # Description: the desk I use to work on desk :) 2 | 3 | cd ~/code/desk 4 | 5 | # Run `make lint` 6 | alias lint="make lint" 7 | 8 | # Args: . Checkout a Github PR's branch for local testing. 9 | checkout_pr () { 10 | git fetch origin pull/$1/head:pr-$1 && git checkout pr-$1; 11 | } 12 | 13 | test() { 14 | make lint 15 | SHELL_CMD='-c ./run_tests.sh' make bash zsh 16 | SHELL_CMD='-c ./run_tests.fish' make fish 17 | } 18 | -------------------------------------------------------------------------------- /examples/hello.fish: -------------------------------------------------------------------------------- 1 | # Description: a simple test for the fish shell. 2 | 3 | # Args: . Say hello to someone. 4 | function say_hello 5 | echo Hello $argv 6 | end 7 | -------------------------------------------------------------------------------- /examples/hello.sh: -------------------------------------------------------------------------------- 1 | # Description: simple desk that says hello 2 | 3 | 4 | # Say an informal hello. 5 | hi() { 6 | echo "hi, ${1}!" 7 | } 8 | 9 | # Use if you're from Texas. 10 | alias howdy="echo howdy y\'all" 11 | 12 | # Why should I always type my name 13 | export MyName="James" 14 | 15 | -------------------------------------------------------------------------------- /examples/python_project.sh: -------------------------------------------------------------------------------- 1 | # Description: desk for working on a Python project 2 | 3 | cd ~/python_project 4 | 5 | source venv/bin/activate 6 | 7 | PROJECT_NAME=python_project 8 | 9 | # Run unittests with nose 10 | alias t="nosetests ${PROJECT_NAME}/tests" 11 | 12 | # Install requirements 13 | alias req="pip install -r requirements.txt" 14 | 15 | # Push the package to PyPI 16 | alias pypipush="python setup.py sdist upload -r ${PROJECT_NAME}" 17 | -------------------------------------------------------------------------------- /examples/terraform.sh: -------------------------------------------------------------------------------- 1 | # Description: desk for doing work on a terraform-based repository 2 | # 3 | 4 | cd ~/terraform-repo 5 | 6 | # Set up AWS env variables: 7 | set_aws_env() { 8 | export AWS_DEFAULT_REGION=us-west-1 9 | export AWS_ACCESS_KEY_ID="$1" 10 | export AWS_SECRET_ACCESS_KEY="$2" 11 | } 12 | 13 | # Run `terraform plan` with proper AWS var config 14 | plan() { 15 | terraform plan -module-depth=-1 \ 16 | -var "access_key=${AWS_ACCESS_KEY_ID}" \ 17 | -var "secret_key=${AWS_SECRET_ACCESS_KEY}" 18 | } 19 | 20 | # Run `terraform apply` with proper AWS var config 21 | apply () { 22 | terraform apply \ 23 | -var "access_key=${AWS_ACCESS_KEY_ID}" \ 24 | -var "secret_key=${AWS_SECRET_ACCESS_KEY}" 25 | } 26 | 27 | # Set up terraform config: 28 | config () { 29 | local KEY=$1 30 | terraform remote config -backend=s3 \ 31 | -backend-config="bucket=some.bucket.secrets.terraform" \ 32 | -backend-config="key=${KEY}" 33 | } 34 | -------------------------------------------------------------------------------- /screencap.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamesob/desk/252e6834b4a4a535fe905c6087e7eecfda70e040/screencap.gif -------------------------------------------------------------------------------- /shell_plugins/bash/desk: -------------------------------------------------------------------------------- 1 | #Bash completion for ◲ desk 2 | 3 | _desk() { 4 | PREFIX="${DESK_DIR:-$HOME/.desk}" 5 | DESKS="${DESK_DESKS_DIR:-$PREFIX/desks}" 6 | 7 | cur=${COMP_WORDS[COMP_CWORD]} 8 | prev=${COMP_WORDS[COMP_CWORD-1]} 9 | 10 | case ${COMP_CWORD} in 11 | 1) 12 | COMPREPLY=($(compgen -W "edit . go run help init list ls version" ${cur})) 13 | ;; 14 | 2) 15 | case ${prev} in 16 | edit|go|.|run) 17 | if [[ -d $DESKS ]]; then 18 | local desks=$(desk list --only-names) 19 | else 20 | local desks="" 21 | fi 22 | COMPREPLY=( $(compgen -W "${desks}" -- ${cur}) ) 23 | ;; 24 | esac 25 | ;; 26 | *) 27 | COMPREPLY=() 28 | ;; 29 | esac 30 | } 31 | 32 | 33 | complete -F _desk desk 34 | -------------------------------------------------------------------------------- /shell_plugins/fish/desk.fish: -------------------------------------------------------------------------------- 1 | # desk.fish - desk completions for fish shell 2 | # 3 | # To install the completions: 4 | # mkdir -p ~/.config/fish/completions 5 | # cp desk.fish ~/.config/fish/completions 6 | 7 | function __fish_desk_no_subcommand --description 'Test if desk has yet to be given a subcommand' 8 | for i in (commandline -opc) 9 | if contains -- $i init list ls . go run edit help version 10 | return 1 11 | end 12 | end 13 | return 0 14 | end 15 | 16 | # desk 17 | complete -c desk -f -n '__fish_desk_no_subcommand' -a init -d 'Initialize desk configuration' 18 | 19 | # desk list|ls 20 | complete -c desk -f -n '__fish_desk_no_subcommand' -a "ls list" -d 'List all desks along with a description' 21 | complete -c desk -A -f -n '__fish_seen_subcommand_from ls list' -l only-names -d 'List only the names of the desks' 22 | complete -c desk -A -f -n '__fish_seen_subcommand_from ls list' -l no-format -d "Use ' - ' to separate names from descriptions" 23 | 24 | # desk go|. 25 | complete -c desk -f -n '__fish_desk_no_subcommand' -a "go ." -d 'Activate a desk. Extra arguments are passed onto shell' 26 | complete -c desk -A -f -n '__fish_seen_subcommand_from . go' -a "(desk ls --only-names)" -d "Desk" 27 | 28 | # desk run 29 | complete -c desk -f -n '__fish_desk_no_subcommand' -a run -d 'Run a command within a desk\'s environment then exit' 30 | complete -c desk -A -f -n '__fish_seen_subcommand_from run' -a '(desk ls --only-names)' -d "Desk" 31 | 32 | # desk edit 33 | complete -c desk -f -n '__fish_desk_no_subcommand' -a edit -d 'Edit (or create) a deskfile with the name specified, otherwise edit the active deskfile' 34 | complete -c desk -A -f -n '__fish_seen_subcommand_from edit' -a '(desk ls --only-names)' -d "Desk" 35 | 36 | # desk help 37 | complete -c desk -f -n '__fish_desk_no_subcommand' -a help -d 'Show help text' 38 | 39 | # desk version 40 | complete -c desk -f -n '__fish_desk_no_subcommand' -a version -d 'Show version information' 41 | -------------------------------------------------------------------------------- /shell_plugins/zsh/_desk: -------------------------------------------------------------------------------- 1 | #compdef desk 2 | #autoload 3 | 4 | _all_desks() { 5 | desks=($(desk list --only-names)) 6 | } 7 | 8 | local expl 9 | local -a desks 10 | 11 | local -a _subcommands 12 | _subcommands=( 13 | 'help:Print a help message.' 14 | 'init:Initialize your desk configuration.' 15 | 'list:List available desks' 16 | 'ls:List available desks' 17 | 'edit:Edit or create a desk, defaults to current desk' 18 | 'go:Activate a desk' 19 | '.:Activate a desk' 20 | 'run:Run a command within a desk environment' 21 | 'version:Show the desk version.' 22 | ) 23 | 24 | if (( CURRENT == 2 )); then 25 | _describe -t commands 'desk subcommand' _subcommands 26 | return 27 | fi 28 | 29 | case "$words[2]" in 30 | go|.|edit|run) 31 | _all_desks 32 | _wanted desks expl 'desks' compadd -a desks ;; 33 | esac 34 | -------------------------------------------------------------------------------- /shell_plugins/zsh/desk.plugin.zsh: -------------------------------------------------------------------------------- 1 | # Add our plugin's bin diretory to user's path 2 | PLUGIN_D="$(dirname $0)" 3 | export PATH=${PATH}:${PLUGIN_D} 4 | -------------------------------------------------------------------------------- /test/bashrc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamesob/desk/252e6834b4a4a535fe905c6087e7eecfda70e040/test/bashrc -------------------------------------------------------------------------------- /test/run_tests.fish: -------------------------------------------------------------------------------- 1 | #!/usr/bin/fish 2 | printf "\ 3 | 4 | 5 | " | desk init 6 | 7 | cp examples/* ~/.desk/desks/ 8 | 9 | set LIST (desk ls) 10 | echo $LIST | grep "hello" >/dev/null 11 | test $status -ne 0; and echo "hello desk not found"; and exit 1 12 | 13 | set CURRENT (env DESK_ENV=$HOME/.desk/desks/hello.fish desk) 14 | echo $CURRENT | grep "say_hello Args: . Say hello to someone." >/dev/null 15 | test $status -ne 0; and echo "say_hello command not found"; and exit 1 16 | 17 | set RAN (desk run hello 'say_hello mrfish') 18 | echo $RAN | grep "Hello mrfish" >/dev/null 19 | test $status -ne 0; and echo "Desk run with 'hello' failed"; and exit 1 20 | 21 | exit 0 22 | -------------------------------------------------------------------------------- /test/run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Run automated tests to ensure desk is behaving as expected 4 | 5 | ensure() { 6 | if [ $1 -ne 0 ]; then 7 | echo "Failed: $2" 8 | exit 1 9 | fi 10 | } 11 | 12 | ensure_not(){ 13 | if [ $1 -eq 0 ]; then 14 | echo "Failed: $2" 15 | exit 1 16 | fi 17 | } 18 | 19 | desk | grep "No desk activated." >/dev/null 20 | ensure $? "Desk without desk activated fails." 21 | desk --version | grep "◲ desk " >/dev/null 22 | ensure $? "Desk version fails." 23 | 24 | HELP=$(desk help) 25 | ensure $? "Desk help fails." 26 | echo "$HELP" | grep 'desk init' >/dev/null 27 | ensure $? "Desk help doesn't contain init" 28 | echo "$HELP" | grep 'desk (list|ls)' >/dev/null 29 | ensure $? "Desk help doesn't contain list" 30 | echo "$HELP" | grep 'desk (.|go)' >/dev/null 31 | ensure $? "Desk help doesn't contain go" 32 | echo "$HELP" | grep 'desk run' >/dev/null 33 | ensure $? "Desk help doesn't contain run" 34 | echo "$HELP" | grep 'desk help' >/dev/null 35 | ensure $? "Desk help doesn't contain help" 36 | echo "$HELP" | grep 'desk version' >/dev/null 37 | ensure $? "Desk help doesn't contain version" 38 | 39 | desk init </dev/null 46 | ensure $? "Desk list missing desk (with DESK_DESKS_DIR)" 47 | echo "$LIST" | grep "python_project desk for working on a Python project" >/dev/null 48 | ensure $? "Desk list missing python_project (with DESK_DESKS_DIR)" 49 | echo "$LIST" | grep "terraform desk for doing work on a terraform-based repository" >/dev/null 50 | ensure $? "Desk list missing terraform (with DESK_DESKS_DIR)" 51 | echo "$LIST" | grep "hello simple desk that says hello" >/dev/null 52 | ensure $? "Desk list missing hello (with DESK_DESKS_DIR)" 53 | 54 | rm -rf "$HOME/.desk/desks" 55 | ln -s "$HOME/examples" "$HOME/.desk/desks" 56 | 57 | ## `desk list` 58 | 59 | # --no-format 60 | LIST=$(desk list --no-format) 61 | echo "$LIST" | grep "desk - the desk I use to work on desk :)" >/dev/null 62 | ensure $? "Desk list missing desk with --no-format option (with symlink)" 63 | echo "$LIST" | grep "python_project - desk for working on a Python project" >/dev/null 64 | ensure $? "Desk list missing python_project with --no-format option (with symlink)" 65 | echo "$LIST" | grep "terraform - desk for doing work on a terraform-based repository" >/dev/null 66 | ensure $? "Desk list missing terraform with --no-format option (with symlink)" 67 | echo "$LIST" | grep "hello - simple desk that says hello" >/dev/null 68 | ensure $? "Desk list missing hello (with symlink)" 69 | 70 | # --only-names 71 | LIST=$(desk list --only-names) 72 | echo "$LIST" | grep "the desk I use to work on desk :)" >/dev/null 73 | ensure_not $? "Desk list --only-names contains 'desk' description (with symlink)" 74 | echo "$LIST" | grep -e '^desk$' >/dev/null 75 | ensure $? "Desk list --only-names missing 'desk' (with symlink)" 76 | 77 | # without options 78 | LIST=$(desk list) 79 | echo "$LIST" | grep "desk the desk I use to work on desk :)" >/dev/null 80 | ensure $? "Desk list did not align 'desk' (with symlink)" 81 | echo "$LIST" | grep "python_project desk for working on a Python project" >/dev/null 82 | ensure $? "Desk list did not align 'python_project' (with symlink)" 83 | echo "$LIST" | grep "terraform desk for doing work on a terraform-based repository" >/dev/null 84 | ensure $? "Desk list did not align 'terraform' (with symlink)" 85 | 86 | # DESK_DESKS_DIR=... 87 | rm -rf "$HOME/.desk/desks" 88 | LIST=$(DESK_DESKS_DIR=$HOME/examples desk list) 89 | echo "$LIST" | grep "desk the desk I use to work on desk :)" >/dev/null 90 | ensure $? "Desk list missing desk (with DESK_DESKS_DIR)" 91 | echo "$LIST" | grep "python_project desk for working on a Python project" >/dev/null 92 | ensure $? "Desk list missing python_project (with DESK_DESKS_DIR)" 93 | echo "$LIST" | grep "terraform desk for doing work on a terraform-based repository" >/dev/null 94 | ensure $? "Desk list missing terraform (with DESK_DESKS_DIR)" 95 | 96 | ln -s "$HOME/examples" "$HOME/.desk/desks" 97 | 98 | mkdir ~/terraform-repo 99 | 100 | ## `desk` 101 | 102 | # without options 103 | CURRENT=$(DESK_ENV=$HOME/.desk/desks/terraform.sh desk) 104 | echo "$CURRENT" | grep 'set_aws_env Set up AWS env variables: ' >/dev/null 105 | ensure $? "Desk current terraform missing set_aws_env" 106 | echo "$CURRENT" | grep 'plan Run `terraform plan` with proper AWS var config' >/dev/null 107 | ensure $? "Desk current terraform missing plan" 108 | echo "$CURRENT" | grep 'apply Run `terraform apply` with proper AWS var config' >/dev/null 109 | ensure $? "Desk current terraform missing apply" 110 | echo "$CURRENT" | grep 'config Set up terraform config: ' >/dev/null 111 | ensure $? "Desk current terraform missing config" 112 | 113 | # --no-format 114 | CURRENT=$(DESK_ENV=$HOME/.desk/desks/terraform.sh desk --no-format) 115 | echo "$CURRENT" | grep 'set_aws_env - Set up AWS env variables: ' >/dev/null 116 | ensure $? "Desk current terraform missing set_aws_env" 117 | echo "$CURRENT" | grep 'plan - Run `terraform plan` with proper AWS var config' >/dev/null 118 | ensure $? "Desk current terraform missing plan" 119 | echo "$CURRENT" | grep 'apply - Run `terraform apply` with proper AWS var config' >/dev/null 120 | ensure $? "Desk current terraform missing apply" 121 | echo "$CURRENT" | grep 'config - Set up terraform config: ' >/dev/null 122 | ensure $? "Desk current terraform missing config" 123 | 124 | # testing for exported variables 125 | CURRENT=$(DESK_ENV=$HOME/.desk/desks/hello.sh desk) 126 | echo "$CURRENT" | grep 'MyName Why should I always type my name' >/dev/null 127 | ensure $? "Desk current hello missing exported environment variable" 128 | 129 | 130 | RAN=$(desk run hello 'howdy james!') 131 | echo "$RAN" | grep 'howdy y'"'"'all james!' >/dev/null 132 | ensure $? "Run in desk 'hello' didn't work with howdy alias" 133 | 134 | RAN=$(desk run hello 'hi j') 135 | echo "$RAN" | grep 'hi, j!' >/dev/null 136 | ensure $? "Run in desk 'hello' didn't work with hi function" 137 | 138 | RAN=$(desk run hello 'echo $MyName') 139 | echo "$RAN" | grep 'James' >/dev/null 140 | ensure $? "Run in desk 'hello' didn't work with MyName exported variable" 141 | 142 | RAN=$(desk run hello echo ahoy matey) 143 | echo "$RAN" | grep 'ahoy matey' >/dev/null 144 | ensure $? "Run in desk 'hello' didn't work with argument vector" 145 | 146 | ## `desk go` 147 | 148 | RAN=$(desk go example-project/Deskfile -c 'desk ; exit') 149 | echo "$RAN" | grep "example-project - simple desk that says hello" >/dev/null 150 | ensure $? "Deskfile invocation didn't work (example-project/)" 151 | echo "$RAN" | grep -E "hi\s+" >/dev/null 152 | ensure $? "Deskfile invocation didn't work (example-project/)" 153 | echo "$RAN" | grep -E "howdy\s+" >/dev/null 154 | ensure $? "Deskfile invocation didn't work (example-project/)" 155 | 156 | RAN=$(desk go example-project/ -c 'desk ; exit') 157 | echo "$RAN" | grep "example-project - simple desk that says hello" >/dev/null 158 | ensure $? "Deskfile invocation didn't work (example-project/)" 159 | echo "$RAN" | grep -E "hi\s+" >/dev/null 160 | ensure $? "Deskfile invocation didn't work (example-project/)" 161 | echo "$RAN" | grep -E "howdy\s+" >/dev/null 162 | ensure $? "Deskfile invocation didn't work (example-project/)" 163 | 164 | pushd example-project 165 | 166 | RAN=$(desk go . -c 'desk ; exit') 167 | echo "$RAN" | grep "example-project - simple desk that says hello" >/dev/null 168 | ensure $? "Deskfile invocation didn't work (./)" 169 | echo "$RAN" | grep -E "hi\s+" >/dev/null 170 | ensure $? "Deskfile invocation didn't work (example-project/)" 171 | echo "$RAN" | grep -E "howdy\s+" >/dev/null 172 | ensure $? "Deskfile invocation didn't work (example-project/)" 173 | 174 | popd 175 | 176 | echo "tests pass." 177 | -------------------------------------------------------------------------------- /test/zshrc: -------------------------------------------------------------------------------- 1 | # Set up the prompt 2 | 3 | autoload -Uz promptinit 4 | promptinit 5 | prompt adam1 6 | 7 | setopt histignorealldups sharehistory 8 | 9 | # Use emacs keybindings even if our EDITOR is set to vi 10 | bindkey -e 11 | 12 | # Keep 1000 lines of history within the shell and save it to ~/.zsh_history: 13 | HISTSIZE=1000 14 | SAVEHIST=1000 15 | HISTFILE=~/.zsh_history 16 | 17 | # Use modern completion system 18 | autoload -Uz compinit 19 | compinit 20 | 21 | zstyle ':completion:*' auto-description 'specify: %d' 22 | zstyle ':completion:*' completer _expand _complete _correct _approximate 23 | zstyle ':completion:*' format 'Completing %d' 24 | zstyle ':completion:*' group-name '' 25 | zstyle ':completion:*' menu select=2 26 | eval "$(dircolors -b)" 27 | zstyle ':completion:*:default' list-colors ${(s.:.)LS_COLORS} 28 | zstyle ':completion:*' list-colors '' 29 | zstyle ':completion:*' list-prompt %SAt %p: Hit TAB for more, or the character to insert%s 30 | zstyle ':completion:*' matcher-list '' 'm:{a-z}={A-Z}' 'm:{a-zA-Z}={A-Za-z}' 'r:|[._-]=* r:|=* l:|=*' 31 | zstyle ':completion:*' menu select=long 32 | zstyle ':completion:*' select-prompt %SScrolling active: current selection at %p%s 33 | zstyle ':completion:*' use-compctl false 34 | zstyle ':completion:*' verbose true 35 | 36 | zstyle ':completion:*:*:kill:*:processes' list-colors '=(#b) #([0-9]#)*=0=01;31' 37 | zstyle ':completion:*:kill:*' command 'ps -u $USER -o pid,%cpu,tty,cputime,cmd' 38 | --------------------------------------------------------------------------------