├── LICENSE
├── README.md
├── img
├── default_small.gif
├── detailed_small.gif
└── git_small.gif
├── layouts
├── default
├── git
├── system
└── tree
├── scripts
├── check_tmux_version.sh
├── delegate.sh
├── helpers.sh
├── toggler.sh
├── update_dir.sh
└── variables.sh
└── sidebar-plus.tmux
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # tmux-sidebar-plus
2 |
3 | *Proof of Concept - tested for tmux >= 2.1*
4 |
5 | ``tmux-sidebar-plus`` is a fast and customizable multi-window
6 | [tmux](https://github.com/tmux/tmux) sidebar.
7 |
8 | An enhanced fork of [tmux-sidebar](https://github.com/tmux-plugins/tmux-sidebar),
9 | the system information sidebar is available through the command ``prefix-b`` by default.
10 |
11 | 
12 |
13 | ## Features
14 |
15 |
16 | ``tmux-sidebar-plus`` adds the following features to ``tmux-sidebar``:
17 |
18 | - **multi-pane sidebar**
19 | Allows for multiple panes in a variety of layouts.
20 | - **practical default layouts**
21 | Including system monitoring, git tracking
22 | - **easily-customizable commands**
23 | Create simple configuration files to manually determine the layout of the
24 | sidebar
25 | - **layout selection**
26 | Select from a variety of default layouts, or a custom layout, using the
27 | layout selector key (default prefix+g)
28 |
29 |
30 |
31 | ## Installation
32 |
33 | ### Manual Installation
34 |
35 | Clone the repo:
36 |
37 | ```
38 | $ git clone https://github.com/addisonlynch/tmux-sidebar-plus ~/clone/path
39 | ```
40 |
41 | Add this line to the bottom of ``.tmux.conf``:
42 |
43 | ```
44 | $ run-shell ~/clone/path/sidebar-plus.tmux
45 | ```
46 |
47 | Reload tmux environment
48 |
49 | ```
50 | $ tmux source-file ~/.tmux.conf
51 | ```
52 |
53 | ## Configuration
54 |
55 |
56 | ### tmux Options
57 |
58 | | option | default | description |
59 | |-------|------|---|
60 | | ``@sidebar-plus-sidebar-key`` | b | Set sidebar toggle key |
61 | | ``@sidebar-plus-layout-key`` | g | Set layout selection key |
62 | | ``@sidebar-plus-layout-dir`` | none | Additional layouts directory |
63 | | ``@sidebar-plus-position`` | left | Desired sidebar position (left or right)|
64 |
65 |
66 | ## Basic Usage
67 |
68 | ### Opening/Closing
69 |
70 | Open and close the sidebar using prefix-b.
71 |
72 | ### Selecting a Layout
73 |
74 | Select your desired layout using prefix-g.
75 |
76 |
77 | ## Layouts
78 |
79 | A number of useful layouts are provided by ``tmux-sidebar-plus``, including:
80 |
81 | * ``default`` - system monitoring using [Glances](https://nicolargo.github.io/glances/),
82 | [htop](https://hisham.hm/htop/), or [top](https://linux.die.net/man/1/top)
83 | * ``git`` - git status & log monitoring
84 |
85 | ### Default
86 |
87 | 
88 |
89 | ### Git
90 |
91 | 
92 |
93 | ### Custom Layouts
94 |
95 | To add custom layouts, create a directory and set its absolute path to
96 | ``@sidebar-plus-layout-dir``.
97 |
98 | #### Formatting
99 |
100 | The layout file should be named as desired and follow the below format:
101 |
102 | ```bash
103 | LAYOUT_NAME="git"
104 | PANES=3
105 | MINIMUM_WIDTH="50"
106 |
107 | populate_sidebar(){
108 | local sidebar_id="$1"
109 | tmux select-pane -t "$sidebar_id"
110 | tmux resize-pane -t "$sidebar_id" -x "${MINIMUM_WIDTH}"
111 |
112 | # Build & register panes
113 | local pane_1_id="$(split "${sidebar_id}" "vertical")"
114 | register_pane $sidebar_id $pane_1_id
115 |
116 | # Run panes' commands
117 | watched_dir_command $sidebar_id "git status"
118 | watched_dir_command $pane_1_id "git log --all --decorate --oneline --graph"
119 | select_base_pane
120 | }
121 |
122 | ```
123 |
124 | Where ``MINIMUM_WIDTH`` is the minimum width of the parent pane required to
125 | open the sidebar.
126 |
127 | #### Overriding Default Layouts
128 |
129 | It is possible to override the default layouts by specifying a custom layout of
130 | the same name. ``tmux-sidebar-plus`` will then use the custom layout's
131 | configuration file.
132 |
133 | #### Using ``update_dir.sh``
134 |
135 | The ``update_dir.sh`` script runs a command every 3 seconds. This can be
136 | included in any custom layout.
137 |
138 | ## To Do
139 |
140 | * Enhanced layout caching (by pane, window, and session)
141 | * Expanded default layouts (pull requests welcome)
142 |
143 | ## Attribution
144 |
145 | tmux-sidebar-plus is an adapted implementation of [tmux-sidebar](https://github.com/tmux-plugins/tmux-sidebar/commits?author=bruno-) written by [Bruno Sutic](https://github.com/bruno-).
146 |
147 | ## Contact
148 |
149 | Email: ahlshop@gmail.com
150 | Twitter: alynchfc
151 |
152 | ## License
153 |
154 | Copyright © 2019 Addison Lynch
155 |
156 | See LICENSE for details
157 |
--------------------------------------------------------------------------------
/img/default_small.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/addisonlynch/tmux-sidebar-plus/bfa8713ad384450367901c88b3365dc71caa1a2f/img/default_small.gif
--------------------------------------------------------------------------------
/img/detailed_small.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/addisonlynch/tmux-sidebar-plus/bfa8713ad384450367901c88b3365dc71caa1a2f/img/detailed_small.gif
--------------------------------------------------------------------------------
/img/git_small.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/addisonlynch/tmux-sidebar-plus/bfa8713ad384450367901c88b3365dc71caa1a2f/img/git_small.gif
--------------------------------------------------------------------------------
/layouts/default:
--------------------------------------------------------------------------------
1 | LAYOUT_NAME="default"
2 | PANES=1
3 | MINIMUM_WIDTH="60"
4 |
5 | populate_sidebar(){
6 | local sidebar_id="$1"
7 | local command
8 | tmux select-pane -t "$sidebar_id"
9 | tmux resize-pane -t "$sidebar_id" -x "${MINIMUM_WIDTH}"
10 |
11 | tmux send-keys -t "${sidebar_id}" "ls | less" Enter
12 | select_base_pane
13 | }
14 |
--------------------------------------------------------------------------------
/layouts/git:
--------------------------------------------------------------------------------
1 | LAYOUT_NAME="git"
2 | PANES=3
3 | MINIMUM_WIDTH="50"
4 |
5 | populate_sidebar(){
6 | local sidebar_id="$1"
7 | tmux select-pane -t "$sidebar_id"
8 | tmux resize-pane -t "$sidebar_id" -x "${MINIMUM_WIDTH}"
9 |
10 | # Build & register panes
11 | local pane_1_id="$(split "${sidebar_id}" "vertical")"
12 | register_pane $sidebar_id $pane_1_id
13 |
14 | # Run panes' commands
15 | watched_dir_command $sidebar_id "git status"
16 | watched_dir_command $pane_1_id "git log --all --decorate --oneline --graph"
17 | select_base_pane
18 | }
19 |
--------------------------------------------------------------------------------
/layouts/system:
--------------------------------------------------------------------------------
1 | LAYOUT_NAME="system"
2 | PANES=1
3 | MINIMUM_WIDTH="60"
4 |
5 | populate_sidebar() {
6 | local sidebar_id="$1"
7 | local command
8 | tmux select-pane -t "$sidebar_id"
9 | tmux resize-pane -t "$sidebar_id" -x "${MINIMUM_WIDTH}"
10 | if type -p "glances" > /dev/null; then
11 | command='glances'
12 | elif type -p "htop" > /dev/null; then
13 | command='htop'
14 | else
15 | command='top'
16 | fi
17 | tmux send-keys -t "${sidebar_id}" "${command}" Enter
18 | select_base_pane
19 | }
20 |
--------------------------------------------------------------------------------
/layouts/tree:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/addisonlynch/tmux-sidebar-plus/bfa8713ad384450367901c88b3365dc71caa1a2f/layouts/tree
--------------------------------------------------------------------------------
/scripts/check_tmux_version.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Copied directly from tmux-sidebar
4 |
5 | VERSION="$1"
6 | UNSUPPORTED_MSG="$2"
7 |
8 | get_tmux_option() {
9 | local option=$1
10 | local default_value=$2
11 | local option_value=$(tmux show-option -gqv "$option")
12 | if [ -z "$option_value" ]; then
13 | echo "$default_value"
14 | else
15 | echo "$option_value"
16 | fi
17 | }
18 |
19 | # Ensures a message is displayed for 5 seconds in tmux prompt.
20 | # Does not override the 'display-time' tmux option.
21 | display_message() {
22 | local message="$1"
23 |
24 | # display_duration defaults to 5 seconds, if not passed as an argument
25 | if [ "$#" -eq 2 ]; then
26 | local display_duration="$2"
27 | else
28 | local display_duration="5000"
29 | fi
30 |
31 | # saves user-set 'display-time' option
32 | local saved_display_time=$(get_tmux_option "display-time" "750")
33 |
34 | # sets message display time to 5 seconds
35 | tmux set-option -gq display-time "$display_duration"
36 |
37 | # displays message
38 | tmux display-message "$message"
39 |
40 | # restores original 'display-time' value
41 | tmux set-option -gq display-time "$saved_display_time"
42 | }
43 |
44 | # this is used to get "clean" integer version number. Examples:
45 | # `tmux 1.9` => `19`
46 | # `1.9a` => `19`
47 | get_digits_from_string() {
48 | local string="$1"
49 | local only_digits="$(echo "$string" | tr -dC '[:digit:]')"
50 | echo "$only_digits"
51 | }
52 |
53 | tmux_version_int() {
54 | local tmux_version_string=$(tmux -V)
55 | echo "$(get_digits_from_string "$tmux_version_string")"
56 | }
57 |
58 | unsupported_version_message() {
59 | if [ -n "$UNSUPPORTED_MSG" ]; then
60 | echo "$UNSUPPORTED_MSG"
61 | else
62 | echo "Error, Tmux version unsupported! Please install Tmux version $VERSION or greater!"
63 | fi
64 | }
65 |
66 | exit_if_unsupported_version() {
67 | local current_version="$1"
68 | local supported_version="$2"
69 | if [ "$current_version" -lt "$supported_version" ]; then
70 | display_message "$(unsupported_version_message)"
71 | exit 1
72 | fi
73 | }
74 |
75 | main() {
76 | local supported_version_int="$(get_digits_from_string "$VERSION")"
77 | local current_version_int="$(tmux_version_int)"
78 | exit_if_unsupported_version "$current_version_int" "$supported_version_int"
79 | }
80 | main
81 |
--------------------------------------------------------------------------------
/scripts/delegate.sh:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env bash
2 |
3 | __dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
4 |
5 | PANE_ID="$1"
6 |
7 | source "$__dir/helpers.sh"
8 | source "$__dir/variables.sh"
9 |
10 | sidebar_registration() {
11 | get_tmux_option "${REGISTERED_PANE_PREFIX}-${PANE_ID}" ""
12 | }
13 |
14 | sidebar_exists() {
15 | local pane_id="$(sidebar_pane_id)"
16 | tmux list-panes -F "#{pane_id}" 2>/dev/null |
17 | \grep -q "^${pane_id}$"
18 | }
19 |
20 | sidebar_pane_id() {
21 | sidebar_registration |
22 | cut -d',' -f1
23 | }
24 |
25 | has_sidebar() {
26 | if [ -n "$(sidebar_registration)" ] && sidebar_exists; then
27 | return 0
28 | else
29 | return 1
30 | fi
31 | }
32 |
33 | main() {
34 | if has_sidebar; then
35 | tmux send-keys -t "${PANE_ID}" "$__dir/toggler.sh 'select_layout' '${PANE_ID}' 'default' 'default'" Enter
36 | fi
37 | return 0
38 | }
39 |
40 | main
41 |
--------------------------------------------------------------------------------
/scripts/helpers.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 |
4 | ######################
5 | # GET/SET tmux options
6 |
7 | get_tmux_option() {
8 | local option=$1
9 | local default_value=$2
10 | local option_value
11 | option_value=$(tmux show-option -gqv "$option")
12 | if [ -z "$option_value" ]; then
13 | echo "$default_value"
14 | else
15 | echo "$option_value"
16 | fi
17 | }
18 |
19 | set_tmux_option() {
20 | local option=$1
21 | local value=$2
22 | tmux set-option -gq "$option" "$value"
23 | }
24 |
25 | unset_tmux_option() {
26 | local option=$1
27 | tmux set-option -gu "$option"
28 | }
29 |
30 | sidebar_key(){
31 | get_tmux_option "${VAR_PREFIX}-${SIDEBAR_KEY_OPTION}" "$SIDEBAR_KEY"
32 | }
33 |
34 | layout_key(){
35 | get_tmux_option "${VAR_PREFIX}-${SIDEBAR_LAYOUT_OPTION}" "$LAYOUT_KEY"
36 | }
37 |
38 | custom_layouts_dir() {
39 | get_tmux_option "${VAR_PREFIX}-$CUSTOM_LAYOUTS_DIR_OPTION" ""
40 | }
41 |
42 | all_panes() {
43 | get_tmux_option "${ALL_PANES_OPTION}" ""
44 | }
45 |
46 | ##############
47 | # tmux version
48 |
49 | # function is used to get "clean" integer version number. Examples:
50 | # `tmux 1.9` => `19`
51 | # `1.9a` => `19`
52 | _get_digits_from_string() {
53 | local string="$1"
54 | local only_digits="$(echo "$string" | tr -dC '[:digit:]')"
55 | echo "$only_digits"
56 | }
57 |
58 | tmux_version_int() {
59 | local tmux_version_string=$(tmux -V)
60 | _get_digits_from_string "$tmux_version_string"
61 | }
62 |
63 | ###########
64 | # Utilities
65 |
66 | get_pane_info() {
67 | local pane_id="$1"
68 | local format_strings="#{pane_id},$2"
69 | tmux list-panes -t "$pane_id" -F "$format_strings" |
70 | \grep "$pane_id" |
71 | cut -d',' -f2-
72 | }
73 |
74 | watched_dir_command() {
75 | local target="$1"
76 | local command="$2"
77 |
78 | tmux send-keys -t "${target}" "$__dir/update_dir.sh '${PANE_ID}' '${target}' '${command}'" Enter
79 | }
80 |
81 | select_base_pane() {
82 | tmux select-pane -t "${PANE_ID}"
83 | }
84 |
85 | ##############
86 | # tree helpers
87 | ##############
88 |
89 | custom_tree_command="$__dir/custom_tree_command.sh"
90 |
91 |
92 | command_exists() {
93 | local command="$1"
94 | type "$command" >/dev/null 2>&1
95 | }
96 |
--------------------------------------------------------------------------------
/scripts/toggler.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | __dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
4 | __root_dir="$(dirname "$__dir")"
5 |
6 | ARG="$1"
7 | PANE_ID="$2"
8 | L="$3"
9 | L_TYPE="$4"
10 |
11 | source "${__dir}/helpers.sh"
12 | source "${__dir}/variables.sh"
13 |
14 | PANE_WIDTH="$(get_pane_info "$PANE_ID" "#{pane_width}")"
15 | readonly PANE_CURRENT_PATH="$(get_pane_info "$PANE_ID" "#{pane_current_path}")"
16 | readonly CUSTOM_LAYOUTS_DIR=$(custom_layouts_dir)
17 | readonly POSITION=$(get_tmux_option "${VAR_PREFIX}-${SIDEBAR_POSITION_OPTION}" "left")
18 |
19 | if [[ $L_TYPE == 'custom' ]]; then
20 | LAYOUT="${CUSTOM_LAYOUTS_DIR}/${L}"
21 | else
22 | LAYOUT="$__root_dir/layouts/${L}"
23 | fi
24 | # Default minimum width
25 | MINIMUM_WIDTH="${MINIMUM_WIDTH_FOR_SIDEBAR}"
26 |
27 | source "${LAYOUT}" # this can't be safe to do
28 |
29 | ###########
30 | # SIDEBAR #
31 | ###########
32 |
33 | # Derived from tmux-sidebar
34 | # source: https://github.com/tmux-plugins/tmux-sidebar
35 |
36 | sidebar_registration() {
37 | get_tmux_option "${REGISTERED_PANE_PREFIX}-${PANE_ID}" ""
38 | }
39 |
40 | sidebar_exists() {
41 | local pane_id="$(sidebar_pane_id)"
42 | tmux list-panes -F "#{pane_id}" 2>/dev/null |
43 | \grep -q "^${pane_id}$"
44 | }
45 |
46 | sidebar_pane_id() {
47 | sidebar_registration |
48 | cut -d',' -f1
49 | }
50 |
51 | current_pane_too_narrow() {
52 | [[ "${PANE_WIDTH}" -lt "${MINIMUM_WIDTH}" ]]
53 | }
54 |
55 | sidebar_left() {
56 | [[ "${POSITION}" =~ "left" ]]
57 | }
58 |
59 | has_sidebar() {
60 | if [ -n "$(sidebar_registration)" ] && sidebar_exists; then
61 | return 0
62 | else
63 | return 1
64 | fi
65 | }
66 |
67 | exit_if_pane_too_narrow() {
68 | if current_pane_too_narrow; then
69 | echo "Pane too narrow for the sidebar"
70 | exit
71 | fi
72 | }
73 |
74 | register_sidebar() {
75 | # Stores the sidebar data as tmux options
76 | #
77 | local sidebar_id="$1"
78 | # registers the main pane of the sidebar to the sidebar
79 | set_tmux_option "${REGISTERED_PANE_PREFIX}-${PANE_ID}" "${sidebar_id}"
80 | # initialize the sidebar panes list
81 | set_tmux_option "${SIDEBAR_PANES_LIST_PREFIX}-${sidebar_id}" ""
82 | # register pane to parent sidebar
83 | set_tmux_option "${PANE_PARENT_PREFIX}-${sidebar_id}" "${PANE_ID}"
84 |
85 | # register sidebar layout
86 | # only if layout is not select_layout
87 | if [ "${L}" != "select_layout" ]; then
88 | set_tmux_option "${REGISTERED_LAYOUT_PREFIX}-${PANE_ID}" "${L}"
89 | fi
90 | # add to list of all sidebar panes
91 | add_to_all_panes "${sidebar_id}"
92 | }
93 |
94 | add_to_all_panes() {
95 | # Adds sidebar panes to the list of all sidebar panes
96 | local pane_id="$1"
97 | local all_panes=$(all_panes)
98 | if [ -n "${all_panes}" ]; then
99 | all_panes+=" "
100 | fi
101 | all_panes+="${pane_id}"
102 | set_tmux_option "${ALL_PANES_OPTION}" "${all_panes}"
103 | }
104 |
105 | register_pane() {
106 | local sidebar_id="$1"
107 | local pane_id="$2"
108 | local option="${SIDEBAR_PANES_LIST_PREFIX}-${sidebar_id}"
109 | local panes_list="$(get_tmux_option "${option}" "")"
110 | if [ -n "${panes_list}" ]; then
111 | panes_list+=" "
112 | fi
113 | panes_list+="${pane_id}"
114 | set_tmux_option "${option}" "${panes_list}"
115 |
116 | # register pane to sidebar's parent
117 | set_tmux_option "${PANE_PARENT_PREFIX}-${pane_id}" "${PANE_ID}"
118 |
119 | # add pane to list of all panes
120 | add_to_all_panes "${pane_id}"
121 | }
122 |
123 | desired_sidebar_size() {
124 | local half_pane="$((PANE_WIDTH / 2))"
125 | if [ -n "${SIZE}" ] && [ "${SIZE}" -lt "${half_pane}" ]; then
126 | echo "${SIZE}"
127 | else
128 | echo "${half_pane}"
129 | fi
130 | }
131 |
132 | use_inverted_size() {
133 | local tmux_version_int="$(tmux_version_int)"
134 | [ "${tmux_version_int}" -le 20 ]
135 | }
136 |
137 | no_focus() {
138 | if [[ "${FOCUS}" =~ (^focus) ]]; then
139 | return 1
140 | else
141 | return 0
142 | fi
143 | }
144 |
145 | split_sidebar_left() {
146 | local sidebar_size=$(desired_sidebar_size)
147 | # if use_inverted_size; then
148 | # sidebar_size=$((PANE_WIDTH - $sidebar_size - 1))
149 | # fi
150 | local sidebar_id="$(tmux new-window -c "${PANE_CURRENT_PATH}" -P -F "#{pane_id}")"
151 | tmux join-pane -hb -l "${sidebar_size}" -t "${PANE_ID}" -s "${sidebar_id}"
152 | echo "${sidebar_id}"
153 | }
154 |
155 | split_sidebar_right() {
156 | local sidebar_size=$(desired_sidebar_size)
157 | tmux split-window -h -l "${sidebar_size}" -c "${PANE_CURRENT_PATH}" -P -F "#{pane_id}"
158 | }
159 |
160 | current_pane_width_mismatch() {
161 | if [ "${PANE_WIDTH}" -eq "$1" ]; then
162 | return 0
163 | else
164 | return 1
165 | fi
166 | }
167 |
168 | kill_sidebar() {
169 | # get data before killing the sidebar
170 | local sidebar_pane_id="$(sidebar_pane_id)"
171 | local old_pane_width=$(get_tmux_option "${BASE_PANE_WIDTH_OPTION}-${PANE_ID}")
172 | # kill the sidebar's nested panes
173 | kill_child_panes
174 | # kill the sidebar
175 | tmux kill-pane -t "${sidebar_pane_id}"
176 | # compensate 1 column
177 | tmux resize-pane -t "$PANE_ID" -x "$old_pane_width"
178 | # deregister the sidebar
179 | deregister_sidebar
180 | }
181 |
182 | deregister_sidebar() {
183 | local sidebar_pane_id="$(sidebar_pane_id)"
184 | local sidebar_panes=$(get_tmux_option "${SIDEBAR_PANES_LIST_PREFIX}-${sidebar_pane_id}")
185 | local all_panes=$(all_panes)
186 | # remove child panes from all-panes
187 | for pane in "${sidebar_panes[@]}"; do
188 | all_panes=$(echo ${all_panes//"$pane"/})
189 | set_tmux_option "${ALL_PANES_OPTION}" "$all_panes"
190 | done
191 |
192 | # remove main sidebar panes from all-panes
193 | all_panes=$(echo ${all_panes//${sidebar_pane_id}/})
194 | set_tmux_option "${ALL_PANES_OPTION}" "$all_panes"
195 |
196 | unset_tmux_option "${REGISTERED_PANE_PREFIX}-${PANE_ID}"
197 | unset_tmux_option "${SIDEBAR_PANES_LIST_PREFIX}-${sidebar_pane_id}"
198 | unset_tmux_option "${PANE_PARENT_PREFIX}-${sidebar_pane_id}"
199 | unset_tmux_option "${BASE_PANE_WIDTH_OPTION}-${PANE_ID}"
200 | # no need to remove from all-panes
201 | }
202 |
203 | deregister_pane() {
204 | local pane_id="$1"
205 | unset_tmux_option "${PANE_PARENT_PREFIX}-${pane_id}"
206 | }
207 |
208 | kill_child_panes() {
209 | local cp="$(get_child_panes)"
210 | local child_panes=($cp)
211 | for child_pane in "${child_panes[@]}"; do
212 | tmux kill-pane -t "${child_pane}"
213 | deregister_pane "${child_pane}"
214 | done
215 | }
216 |
217 | get_child_panes() {
218 | local sidebar_pane_id="$(sidebar_pane_id)"
219 | local child_panes="$(get_tmux_option "${SIDEBAR_PANES_LIST_PREFIX}-${sidebar_pane_id}" "")"
220 | echo "${child_panes}"
221 | }
222 |
223 |
224 | create_sidebar() {
225 | local position="$1" # left / right
226 |
227 | # check if has cached layout for select_layout
228 | if [ "${L}" = "select_layout" ]; then
229 | local cached_layout="$(get_cached_layout | tr -d ' ')"
230 | # if there is a cached layout
231 | if [ -n "${cached_layout// }" ] && [ "${cached_layout}" != "select_layout" ];
232 | then
233 | # open a sidebar with that layout
234 | $__dir/toggler.sh "sidebar" "${PANE_ID}" "${cached_layout}"
235 | else
236 | # open the default layout (directory tree)
237 | $__dir/toggler.sh "sidebar" "${PANE_ID}" "default"
238 | fi
239 | return
240 | fi
241 | # store the parent width for use later
242 | set_tmux_option "${BASE_PANE_WIDTH_OPTION}-${PANE_ID}" "${PANE_WIDTH}"
243 |
244 | local sidebar_id="$(split_sidebar_${position})"
245 | register_sidebar "${sidebar_id}"
246 | tmux last-pane
247 | populate_sidebar "${sidebar_id}"
248 | # if no_focus; then
249 | # tmux last-pane
250 | # fi
251 | }
252 |
253 | toggle_sidebar() {
254 | if has_sidebar; then
255 | kill_sidebar
256 | # if using different sidebar command automatically open a new sidebar
257 | # if registration_not_for_the_same_command; then
258 | # create_sidebar
259 | # fi
260 | else
261 | exit_if_pane_too_narrow
262 | if sidebar_left; then
263 | create_sidebar "left"
264 | else
265 | create_sidebar "right"
266 | fi
267 | fi
268 | }
269 |
270 | execute_command_from_main_pane() {
271 | # get pane_id for this sidebar
272 | local main_pane_id="$(get_tmux_option "${PANE_PARENT_PREFIX}-${PANE_ID}" "")"
273 | # execute the same command as if from the "main" pane
274 | $__dir/toggler.sh "${ARG}" "${main_pane_id}" "${L}"
275 | }
276 |
277 | current_pane_is_sidebar() {
278 | local all_panes=$(all_panes)
279 | local retval=$(echo "${all_panes}" | grep "${PANE_ID}" | tr -d ' ')
280 | [ -n "${retval}" ]
281 | }
282 |
283 | split() {
284 | # simple splitter for either horizontal or vertical
285 | local pane_id="$1"
286 | local direction="${DIRECTION["$2"]}"
287 |
288 | local new_pane_id="$(tmux new-window -P -F "#{pane_id}" )"
289 | tmux join-pane "${direction}" -p 50 -t "${pane_id}" -s "${new_pane_id}"
290 | echo "${new_pane_id}"
291 | }
292 |
293 |
294 | sidebar() {
295 | if current_pane_is_sidebar; then
296 | execute_command_from_main_pane
297 | else
298 | toggle_sidebar
299 | fi
300 | }
301 |
302 | ###################
303 | # LAYOUT SELECTOR #
304 | ###################
305 |
306 | get_cached_layout() {
307 | local pane_id="${PANE_ID}"
308 | local cached_layout="$(get_tmux_option "${REGISTERED_LAYOUT_PREFIX}-${pane_id}" "")"
309 | echo "${cached_layout}"
310 | }
311 |
312 | execute_main_and_switch() {
313 | local parent_id="$(get_tmux_option "${PANE_PARENT_PREFIX}-${PANE_ID}" "")"
314 | tmux send-keys -t "${parent_id}" "$__dir/toggler.sh 'select_layout' '${parent_id}' '${L}'" Enter
315 | tmux select-pane -t "${parent_id}"
316 | }
317 |
318 | select_layout() {
319 | if current_pane_is_sidebar; then
320 | execute_main_and_switch
321 | else
322 | select_menu
323 | fi
324 | }
325 |
326 | select_menu() {
327 | # TODO: refactor
328 |
329 | # Get all default layouts
330 | local DEFAULT_LAYOUTS=$(find -L "${__root_dir}/layouts" -type f -exec basename {} \;)
331 |
332 | local CUSTOM_LAYOUTS
333 | # If there is a custom layouts directory
334 | if [[ -n "${CUSTOM_LAYOUTS_DIR}" ]]; then
335 | # Add the layouts to the list
336 | CUSTOM_LAYOUTS=$(find "${CUSTOM_LAYOUTS_DIR}" -type f -exec basename {} \;)
337 | fi
338 |
339 | echo ""
340 | echo ""
341 | echo "Default layouts:"
342 | echo ""
343 | echo "${DEFAULT_LAYOUTS[@]}"
344 | if [[ -n $CUSTOM_LAYOUTS ]]; then
345 | echo ""
346 | echo "Custom Layouts:"
347 | echo ""
348 | echo "${CUSTOM_LAYOUTS[@]}"
349 | fi
350 | echo ""
351 | echo ""
352 | echo -n "Enter layout and press [ENTER]: "
353 | read -r layout
354 | local selected_layout="${layout}"
355 | local found_default_layout=0
356 | local found_custom_layout=0
357 |
358 | # check if selected layout is available in default layouts
359 | for l in $DEFAULT_LAYOUTS; do
360 | if [[ $l == $selected_layout ]]; then
361 | found_default_layout=1
362 | fi
363 | done
364 |
365 | # check if selected layout is available in custom layouts
366 | if [[ -n $CUSTOM_LAYOUTS ]]; then
367 | for l in $CUSTOM_LAYOUTS; do
368 | if [[ $l == $selected_layout ]]; then
369 | found_custom_layout=1
370 | found_default_layout=0
371 | fi
372 | done
373 | fi
374 |
375 | if [[ $found_custom_layout -eq 1 ]] || [[ $found_default_layout -eq 1 ]];
376 | then
377 | if has_sidebar; then
378 | kill_sidebar
379 | fi
380 | if [[ $found_custom_layout -eq 1 ]]; then
381 | $__dir/toggler.sh "sidebar" "${PANE_ID}" "${selected_layout}" "custom"
382 | else
383 | $__dir/toggler.sh "sidebar" "${PANE_ID}" "${selected_layout}" "d"
384 | fi
385 | else
386 | echo "ERROR: ${selected_layout} not found."
387 | exit 1
388 | fi
389 | }
390 |
391 |
392 | ##############
393 | # DELEGATION #
394 | ##############
395 |
396 | main(){
397 | case "${ARG}" in
398 | 'sidebar')
399 | sidebar
400 | return
401 | ;;
402 | 'select_layout')
403 | select_layout
404 | return
405 | ;;
406 | *)
407 | echo "enter valid command. ${ARG} not valid."
408 | return 1
409 | esac
410 | }
411 | main
412 |
--------------------------------------------------------------------------------
/scripts/update_dir.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Updates the directory of running watch commands to
4 | # current directory of the base pane
5 |
6 | __dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
7 | SCRIPTS_DIR="$(dirname "$__dir")"
8 |
9 | BASE_ID="$1" # ID of base pane
10 | CHILD_ID="$2"
11 | COMMAND="$3" # command to watch
12 |
13 | get_base_pane_dir() {
14 | tmux list-panes -F "#{pane_current_path} #{pane_id}" | grep $BASE_ID | cut -d' ' -f1
15 | }
16 |
17 | check_directory_match() {
18 | local base_pane_dir="$(get_base_pane_dir)"
19 | if [ "$base_pane_dir" == "$__dir" ]; then
20 | return
21 | else
22 | cd "$base_pane_dir"
23 | eval "${COMMAND}" 2>/dev/null || echo "Not a git repository"
24 | fi
25 | }
26 |
27 | main_loop() {
28 | while :
29 | do
30 | clear
31 | check_directory_match
32 | trap ' ' INT
33 | clear
34 | eval "${COMMAND}" 2>/dev/null || echo "Not a git repository"
35 | sleep 3
36 | done
37 | }
38 |
39 | main() {
40 | main_loop
41 | }
42 |
43 | main
44 |
--------------------------------------------------------------------------------
/scripts/variables.sh:
--------------------------------------------------------------------------------
1 | ##########
2 | # Common #
3 | ##########
4 | VAR_PREFIX="@sidebar-plus"
5 | KEY_SUFFIX="key"
6 | SUPPORTED_TMUX_VERSION="1.9"
7 |
8 | # used to save pane width before sidebar creation
9 | BASE_PANE_WIDTH_OPTION="@-sidebar-parent-width"
10 |
11 | ###########
12 | # Sidebar #
13 | ###########
14 | SIDEBAR_KEY="b"
15 | SIDEBAR_KEY_OPTION="sidebar-key"
16 |
17 | SIDEBAR_POSITION_OPTION="sidebar-position"
18 |
19 | SIDEBAR_ID="sidebar"
20 | SIDEBAR_ID_OPTION="sidebar-id"
21 |
22 | REGISTERED_PANE_PREFIX="@-sidebar-plus-registered-pane"
23 |
24 | ALL_PANES_OPTION="@-sidebar-plus-all-panes"
25 | SIDEBAR_PANES_LIST_PREFIX="@-sidebar-plus-panes-sidebar"
26 |
27 | PANE_PARENT_PREFIX="@-sidebar-plus-sidebar-parent"
28 |
29 | MINIMUM_WIDTH_FOR_SIDEBAR="40"
30 |
31 | ##########
32 | # CONFIG #
33 | ##########
34 |
35 | declare -A DIRECTION
36 |
37 | DIRECTION=(
38 | [horizontal]="-h"
39 | [vertical]="-v"
40 | )
41 | ###########
42 | # Layouts #
43 | ###########
44 |
45 | LAYOUT_KEY="g"
46 | SIDEBAR_LAYOUT_OPTION="layout-key"
47 |
48 | CUSTOM_LAYOUTS_DIR_OPTION="custom-layouts-dir"
49 |
50 | REGISTERED_LAYOUT_PREFIX="@-sidebar-layout-pane"
51 |
--------------------------------------------------------------------------------
/sidebar-plus.tmux:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
4 | SCRIPTS_DIR="$CURRENT_DIR/scripts"
5 |
6 | source "$SCRIPTS_DIR/helpers.sh"
7 | source "$SCRIPTS_DIR/variables.sh"
8 |
9 | set_default_key_options() {
10 | local sidebar_key="$(sidebar_key)"
11 | local layout_key="$(layout_key)"
12 |
13 | # Set sidebar key
14 | set_tmux_option "${VAR_PREFIX}-${SIDEBAR_KEY_OPTION}" "${sidebar_key}"
15 |
16 | # Set layout selection key
17 | set_tmux_option "${VAR_PREFIX}-${SIDEBAR_LAYOUT_OPTION}" "${layout_key}"
18 | }
19 |
20 | set_key_bindings() {
21 | local sidebar_key="$(sidebar_key)"
22 | local layout_key="$(layout_key)"
23 |
24 | tmux bind-key "${layout_key}" run-shell "$SCRIPTS_DIR/delegate.sh '#{pane_id}'"
25 | tmux bind-key "${sidebar_key}" run-shell "$SCRIPTS_DIR/toggler.sh 'sidebar' '#{pane_id}' 'select_layout' 'default'"
26 | }
27 |
28 | main() {
29 | set_default_key_options
30 | set_key_bindings
31 | }
32 |
33 | main
34 |
--------------------------------------------------------------------------------