diff --git a/.dfminstall b/.dfminstall index e798f80..4057227 100644 --- a/.dfminstall +++ b/.dfminstall @@ -3,6 +3,15 @@ bin recurse bin/dfm chmod 0755 .tmux/newpanes 0755 +.tmux/prefix_highlight.tmux 0755 +.tmux/scripts/check_tmux_version.sh 0755 +.tmux/scripts/custom_tree.sh 0755 +.tmux/scripts/helpers.sh 0755 +.tmux/scripts/save_sidebar_width.sh 0755 +.tmux/scripts/toggle.sh 0755 +.tmux/scripts/tree_helpers.sh 0755 +.tmux/scripts/variables.sh 0755 +.tmux/sidebar.tmux 0755 .tmux/zoom 0755 .dotfiles/.iterm2 skip diff --git a/.gitignore b/.gitignore index 2b93ed7..a15276b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ -tmux.start.sh +*.sublime-workspace .config/mc/hotlist .vim/.netrwhist -*.sublime-workspace +.tmux/sidebar/directory_widths.txt +tmux.start.sh diff --git a/.tmux.conf b/.tmux.conf index ad7e99b..42322de 100644 --- a/.tmux.conf +++ b/.tmux.conf @@ -147,4 +147,9 @@ run-shell ~/.tmux/prefix_highlight.tmux set -g @prefix_highlight_fg 'white' # default is 'colour231' set -g @prefix_highlight_bg 'blue' # default is 'colour04' set -g @prefix_highlight_show_copy_mode 'on' -set -g @prefix_highlight_copy_mode_attr 'fg=black,bg=yellow,bold' # default is 'fg=default,bg=yellow' \ No newline at end of file +set -g @prefix_highlight_copy_mode_attr 'fg=black,bg=yellow,bold' # default is 'fg=default,bg=yellow' + +# tmux-sidebar (https://github.com/tmux-plugins/tmux-sidebar) +run-shell ~/.tmux/sidebar.tmux +set -g @sidebar-tree-position 'left' +set -g @sidebar-tree-width '40' \ No newline at end of file diff --git a/.tmux/scripts/check_tmux_version.sh b/.tmux/scripts/check_tmux_version.sh new file mode 100755 index 0000000..21a543e --- /dev/null +++ b/.tmux/scripts/check_tmux_version.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash + +VERSION="$1" +UNSUPPORTED_MSG="$2" + +get_tmux_option() { + local option=$1 + local default_value=$2 + local option_value=$(tmux show-option -gqv "$option") + if [ -z "$option_value" ]; then + echo "$default_value" + else + echo "$option_value" + fi +} + +# Ensures a message is displayed for 5 seconds in tmux prompt. +# Does not override the 'display-time' tmux option. +display_message() { + local message="$1" + + # display_duration defaults to 5 seconds, if not passed as an argument + if [ "$#" -eq 2 ]; then + local display_duration="$2" + else + local display_duration="5000" + fi + + # saves user-set 'display-time' option + local saved_display_time=$(get_tmux_option "display-time" "750") + + # sets message display time to 5 seconds + tmux set-option -gq display-time "$display_duration" + + # displays message + tmux display-message "$message" + + # restores original 'display-time' value + tmux set-option -gq display-time "$saved_display_time" +} + +# this is used to get "clean" integer version number. Examples: +# `tmux 1.9` => `19` +# `1.9a` => `19` +get_digits_from_string() { + local string="$1" + local only_digits="$(echo "$string" | tr -dC '[:digit:]')" + echo "$only_digits" +} + +tmux_version_int() { + local tmux_version_string=$(tmux -V) + echo "$(get_digits_from_string "$tmux_version_string")" +} + +unsupported_version_message() { + if [ -n "$UNSUPPORTED_MSG" ]; then + echo "$UNSUPPORTED_MSG" + else + echo "Error, Tmux version unsupported! Please install Tmux version $VERSION or greater!" + fi +} + +exit_if_unsupported_version() { + local current_version="$1" + local supported_version="$2" + if [ "$current_version" -lt "$supported_version" ]; then + display_message "$(unsupported_version_message)" + exit 1 + fi +} + +main() { + local supported_version_int="$(get_digits_from_string "$VERSION")" + local current_version_int="$(tmux_version_int)" + exit_if_unsupported_version "$current_version_int" "$supported_version_int" +} +main \ No newline at end of file diff --git a/.tmux/scripts/custom_tree.sh b/.tmux/scripts/custom_tree.sh new file mode 100755 index 0000000..e19de2c --- /dev/null +++ b/.tmux/scripts/custom_tree.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +find . -path '*/.git*' -prune -o -print | + sed -e 's;[^/]*/;|___;g;s;___|; |;g' \ No newline at end of file diff --git a/.tmux/scripts/helpers.sh b/.tmux/scripts/helpers.sh new file mode 100755 index 0000000..c7d85cc --- /dev/null +++ b/.tmux/scripts/helpers.sh @@ -0,0 +1,101 @@ +get_tmux_option() { + local option=$1 + local default_value=$2 + local option_value=$(tmux show-option -gqv "$option") + if [ -z "$option_value" ]; then + echo "$default_value" + else + echo "$option_value" + fi +} + +set_tmux_option() { + local option=$1 + local value=$2 + tmux set-option -gq "$option" "$value" +} + +# Ensures a message is displayed for 5 seconds in tmux prompt. +# Does not override the 'display-time' tmux option. +display_message() { + local message="$1" + + # display_duration defaults to 5 seconds, if not passed as an argument + if [ "$#" -eq 2 ]; then + local display_duration="$2" + else + local display_duration="5000" + fi + + # saves user-set 'display-time' option + local saved_display_time=$(get_tmux_option "display-time" "750") + + # sets message display time to 5 seconds + tmux set-option -gq display-time "$display_duration" + + # displays message + tmux display-message "$message" + + # restores original 'display-time' value + tmux set-option -gq display-time "$saved_display_time" +} + +stored_key_vars() { + tmux show-options -g | + \grep -i "^${VAR_KEY_PREFIX}-" | + cut -d ' ' -f1 | # cut just the variable names + xargs # splat var names in one line +} + +# get the key from the variable name +get_key_from_option_name() { + local option="$1" + echo "$option" | + sed "s/^${VAR_KEY_PREFIX}-//" +} + +get_value_from_option_name() { + local option="$1" + echo "$(get_tmux_option "$option" "")" +} + +get_pane_info() { + local pane_id="$1" + local format_strings="#{pane_id},$2" + tmux list-panes -t "$pane_id" -F "$format_strings" | + \grep "$pane_id" | + cut -d',' -f2- +} + +sidebar_dir() { + echo "$SIDEBAR_DIR" +} + +sidebar_file() { + echo "$(sidebar_dir)/directory_widths.txt" +} + +directory_in_sidebar_file() { + local pane_current_path="$1" + grep -q "^${pane_current_path}\t" $(sidebar_file) 2>/dev/null +} + +width_from_sidebar_file() { + local pane_current_path="$1" + grep "^${pane_current_path}\t" $(sidebar_file) | + cut -f2 +} + +# function is used to get "clean" integer version number. Examples: +# `tmux 1.9` => `19` +# `1.9a` => `19` +_get_digits_from_string() { + local string="$1" + local only_digits="$(echo "$string" | tr -dC '[:digit:]')" + echo "$only_digits" +} + +tmux_version_int() { + local tmux_version_string=$(tmux -V) + echo "$(_get_digits_from_string "$tmux_version_string")" +} \ No newline at end of file diff --git a/.tmux/scripts/save_sidebar_width.sh b/.tmux/scripts/save_sidebar_width.sh new file mode 100755 index 0000000..8b396c0 --- /dev/null +++ b/.tmux/scripts/save_sidebar_width.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +source "$CURRENT_DIR/helpers.sh" +source "$CURRENT_DIR/variables.sh" + +DIR_PATH="$(echo "$1" | tail -1)" # fixes a bug with invalid param +WIDTH="$2" +delimiter=$'\t' + +replace_directory_width() { + sed "s|^${DIR_PATH}${delimiter}.*|${DIR_PATH}${delimiter}${WIDTH}|g" $(sidebar_file) > $(sidebar_file).bak + mv $(sidebar_file).bak $(sidebar_file) +} + +add_directory_width() { + mkdir -p "$(sidebar_dir)" + echo "${DIR_PATH}${delimiter}${WIDTH}" >> $(sidebar_file) +} + +save_sidebar_width() { + if directory_in_sidebar_file "$DIR_PATH"; then + replace_directory_width + else + add_directory_width + fi +} + +main() { + save_sidebar_width +} +main \ No newline at end of file diff --git a/.tmux/scripts/toggle.sh b/.tmux/scripts/toggle.sh new file mode 100755 index 0000000..0a05760 --- /dev/null +++ b/.tmux/scripts/toggle.sh @@ -0,0 +1,204 @@ +#!/usr/bin/env bash + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +source "$CURRENT_DIR/helpers.sh" +source "$CURRENT_DIR/variables.sh" + +# script global vars +ARGS="$1" # example args format: "tree | less,right,20,focus" +PANE_ID="$2" +COMMAND="$(echo "$ARGS" | cut -d',' -f1)" # "tree | less" +POSITION="$(echo "$ARGS" | cut -d',' -f2)" # "right" +SIZE="$(echo "$ARGS" | cut -d',' -f3)" # "20" +FOCUS="$(echo "$ARGS" | cut -d',' -f4)" # "focus" + +PANE_WIDTH="$(get_pane_info "$PANE_ID" "#{pane_width}")" +PANE_CURRENT_PATH="$(get_pane_info "$PANE_ID" "#{pane_current_path}")" + +supported_tmux_version_ok() { + $CURRENT_DIR/check_tmux_version.sh "$SUPPORTED_TMUX_VERSION" +} + +sidebar_registration() { + get_tmux_option "${REGISTERED_PANE_PREFIX}-${PANE_ID}" "" +} + +sidebar_pane_id() { + sidebar_registration | + cut -d',' -f1 +} + +sidebar_pane_args() { + echo "$(sidebar_registration)" | + cut -d',' -f2- +} + +register_sidebar() { + local sidebar_id="$1" + set_tmux_option "${REGISTERED_SIDEBAR_PREFIX}-${sidebar_id}" "$PANE_ID" + set_tmux_option "${REGISTERED_PANE_PREFIX}-${PANE_ID}" "${sidebar_id},${ARGS}" +} + +registration_not_for_the_same_command() { + local registered_args="$(sidebar_registration | cut -d',' -f2-)" + [[ $ARGS != $registered_args ]] +} + +sidebar_exists() { + local pane_id="$(sidebar_pane_id)" + tmux list-panes -F "#{pane_id}" 2>/dev/null | + \grep -q "^${pane_id}$" +} + +has_sidebar() { + if [ -n "$(sidebar_registration)" ] && sidebar_exists; then + return 0 + else + return 1 + fi +} + +current_pane_width_not_changed() { + if [ $PANE_WIDTH -eq $1 ]; then + return 0 + else + return 1 + fi +} + +kill_sidebar() { + # get data before killing the sidebar + local sidebar_pane_id="$(sidebar_pane_id)" + local sidebar_args="$(sidebar_pane_args)" + local sidebar_position="$(echo "$sidebar_args" | cut -d',' -f2)" # left or defults to right + local sidebar_width="$(get_pane_info "$sidebar_pane_id" "#{pane_width}")" + + $CURRENT_DIR/save_sidebar_width.sh "$PANE_CURRENT_PATH" "$sidebar_width" + + # kill the sidebar + tmux kill-pane -t "$sidebar_pane_id" + + # check current pane "expanded" properly + local new_current_pane_width="$(get_pane_info "$PANE_ID" "#{pane_width}")" + if current_pane_width_not_changed "$new_current_pane_width"; then + # need to expand current pane manually + local direction_flag + if [[ "$sidebar_position" =~ "left" ]]; then + direction_flag="-L" + else + direction_flag="-R" + fi + # compensate 1 column + tmux resize-pane "$direction_flag" "$((sidebar_width + 1))" + fi + PANE_WIDTH="$new_current_pane_width" +} + +sidebar_left() { + [[ $POSITION =~ "left" ]] +} + +no_focus() { + if [[ $FOCUS =~ (^focus) ]]; then + return 1 + else + return 0 + fi +} + +size_defined() { + [ -n $SIZE ] +} + +desired_sidebar_size() { + local half_pane="$((PANE_WIDTH / 2))" + if directory_in_sidebar_file "$PANE_CURRENT_PATH"; then + # use stored sidebar width for the directory + echo "$(width_from_sidebar_file "$PANE_CURRENT_PATH")" + elif size_defined && [ $SIZE -lt $half_pane ]; then + echo "$SIZE" + else + echo "$half_pane" + fi +} + +# tmux version 2.0 and below requires different argument for `join-pane` +use_inverted_size() { + [ tmux_version_int -le 20 ] +} + +split_sidebar_left() { + local sidebar_size=$(desired_sidebar_size) + if use_inverted_size; then + sidebar_size=$((PANE_WIDTH - $sidebar_size - 1)) + fi + local sidebar_id="$(tmux new-window -c "$PANE_CURRENT_PATH" -P -F "#{pane_id}" "$COMMAND")" + tmux join-pane -hb -l "$sidebar_size" -t "$PANE_ID" -s "$sidebar_id" + echo "$sidebar_id" +} + +split_sidebar_right() { + local sidebar_size=$(desired_sidebar_size) + tmux split-window -h -l "$sidebar_size" -c "$PANE_CURRENT_PATH" -P -F "#{pane_id}" "$COMMAND" +} + +create_sidebar() { + local position="$1" # left / right + local sidebar_id="$(split_sidebar_${position})" + register_sidebar "$sidebar_id" + if no_focus; then + tmux last-pane + fi +} + +current_pane_is_sidebar() { + local var="$(get_tmux_option "${REGISTERED_SIDEBAR_PREFIX}-${PANE_ID}" "")" + [ -n "$var" ] +} + +current_pane_too_narrow() { + [ $PANE_WIDTH -lt $MINIMUM_WIDTH_FOR_SIDEBAR ] +} + +execute_command_from_main_pane() { + # get pane_id for this sidebar + local main_pane_id="$(get_tmux_option "${REGISTERED_SIDEBAR_PREFIX}-${PANE_ID}" "")" + # execute the same command as if from the "main" pane + $CURRENT_DIR/toggle.sh "$ARGS" "$main_pane_id" +} + +exit_if_pane_too_narrow() { + if current_pane_too_narrow; then + display_message "Pane too narrow for the sidebar" + exit + fi +} + +toggle_sidebar() { + if has_sidebar; then + kill_sidebar + # if using different sidebar command automatically open a new sidebar + # if registration_not_for_the_same_command; then + # create_sidebar + # fi + else + exit_if_pane_too_narrow + if sidebar_left; then + create_sidebar "left" + else + create_sidebar "right" + fi + fi +} + +main() { + if supported_tmux_version_ok; then + if current_pane_is_sidebar; then + execute_command_from_main_pane + else + toggle_sidebar + fi + fi +} +main \ No newline at end of file diff --git a/.tmux/scripts/tree_helpers.sh b/.tmux/scripts/tree_helpers.sh new file mode 100755 index 0000000..6119107 --- /dev/null +++ b/.tmux/scripts/tree_helpers.sh @@ -0,0 +1,42 @@ +# file sourced from ./sidebar.tmux +custom_tree_command="$CURRENT_DIR/scripts/custom_tree.sh" + +command_exists() { + local command="$1" + type "$command" >/dev/null 2>&1 +} + +tree_command() { + local user_command="$(tree_user_command)" + if [ -n "$user_command" ]; then + echo "$user_command" + elif command_exists "tree"; then + echo "$TREE_COMMAND" + else + echo "$custom_tree_command" + fi +} + +tree_user_command() { + get_tmux_option "$TREE_COMMAND_OPTION" "" +} + +tree_key() { + get_tmux_option "$TREE_OPTION" "$TREE_KEY" +} + +tree_focus_key() { + get_tmux_option "$TREE_FOCUS_OPTION" "$TREE_FOCUS_KEY" +} + +tree_pager() { + get_tmux_option "$TREE_PAGER_OPTION" "$TREE_PAGER" +} + +tree_position() { + get_tmux_option "$TREE_POSITION_OPTION" "$TREE_POSITION" +} + +tree_width() { + get_tmux_option "$TREE_WIDTH_OPTION" "$TREE_WIDTH" +} \ No newline at end of file diff --git a/.tmux/scripts/variables.sh b/.tmux/scripts/variables.sh new file mode 100755 index 0000000..4ebe5c3 --- /dev/null +++ b/.tmux/scripts/variables.sh @@ -0,0 +1,26 @@ +VAR_KEY_PREFIX="@sidebar-key" +REGISTERED_PANE_PREFIX="@-sidebar-registered-pane" +REGISTERED_SIDEBAR_PREFIX="@-sidebar-is-sidebar" +MINIMUM_WIDTH_FOR_SIDEBAR="71" + +TREE_KEY="Tab" +TREE_OPTION="@sidebar-tree" + +TREE_FOCUS_KEY="Bspace" +TREE_FOCUS_OPTION="@sidebar-tree-focus" + +TREE_COMMAND="tree" +TREE_COMMAND_OPTION="@sidebar-tree-command" + +TREE_PAGER='sh -c "LESS= less --dumb --chop-long-lines --tilde --IGNORE-CASE --RAW-CONTROL-CHARS"' +TREE_PAGER_OPTION="@sidebar-tree-pager" + +TREE_POSITION="left" +TREE_POSITION_OPTION="@sidebar-tree-position" + +TREE_WIDTH="40" +TREE_WIDTH_OPTION="@sidebar-tree-width" + +SUPPORTED_TMUX_VERSION="1.9" + +SIDEBAR_DIR="$HOME/.tmux/sidebar" \ No newline at end of file diff --git a/.tmux/sidebar.tmux b/.tmux/sidebar.tmux new file mode 100755 index 0000000..be24390 --- /dev/null +++ b/.tmux/sidebar.tmux @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +SCRIPTS_DIR="$CURRENT_DIR/scripts" + +source "$SCRIPTS_DIR/helpers.sh" +source "$SCRIPTS_DIR/variables.sh" +source "$SCRIPTS_DIR/tree_helpers.sh" + +set_default_key_binding_options() { + local tree_command="$(tree_command)" + local tree_key="$(tree_key)" + local tree_focus_key="$(tree_focus_key)" + local tree_pager="$(tree_pager)" + local tree_position="$(tree_position)" + local tree_width="$(tree_width)" + + set_tmux_option "${VAR_KEY_PREFIX}-${tree_key}" "$tree_command | ${tree_pager},${tree_position},${tree_width}" + set_tmux_option "${VAR_KEY_PREFIX}-${tree_focus_key}" "$tree_command | ${tree_pager},${tree_position},${tree_width},focus" +} + +set_key_bindings() { + local stored_key_vars="$(stored_key_vars)" + local search_var + local key + local pattern + for option in $stored_key_vars; do + key="$(get_key_from_option_name "$option")" + value="$(get_value_from_option_name "$option")" + tmux bind-key "$key" run-shell "$SCRIPTS_DIR/toggle.sh '$value' '#{pane_id}'" + done +} + +main() { + set_default_key_binding_options + set_key_bindings +} +main \ No newline at end of file diff --git a/.tmux/sidebar/directory_widths.txt b/.tmux/sidebar/directory_widths.txt new file mode 100644 index 0000000..cc95d40 --- /dev/null +++ b/.tmux/sidebar/directory_widths.txt @@ -0,0 +1 @@ +/Users/eragos/.dotfiles/.tmux/scripts 115