Добавлены конфиги tmux и zsh
This commit is contained in:
19
tmux/plugins/tmux-copycat/LICENSE.md
Normal file
19
tmux/plugins/tmux-copycat/LICENSE.md
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (C) 2014 Bruno Sutic
|
||||
|
||||
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:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
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.
|
142
tmux/plugins/tmux-copycat/README.md
Normal file
142
tmux/plugins/tmux-copycat/README.md
Normal file
@@ -0,0 +1,142 @@
|
||||
# Tmux copycat
|
||||
|
||||
[](https://travis-ci.org/tmux-plugins/tmux-copycat)
|
||||
|
||||
This plugin enables:
|
||||
|
||||
- regex searches
|
||||
- search result highlighting
|
||||
- predefined searches
|
||||
|
||||
Predefined searches are plugin killer feature. It speeds the workflow and
|
||||
reduces mouse usage with Tmux.
|
||||
|
||||
It works even better when paired with
|
||||
[tmux yank](https://github.com/tmux-plugins/tmux-yank). Tested and working on
|
||||
Linux, OSX and Cygwin.
|
||||
|
||||
### Screencast
|
||||
|
||||
[](https://vimeo.com/101867689)
|
||||
|
||||
#### Search
|
||||
|
||||
- `prefix + /` - regex search (strings work too)
|
||||
|
||||
Example search entries:
|
||||
|
||||
- `foo` - searches for string `foo`
|
||||
- `[0-9]+` - regex search for numbers
|
||||
|
||||
Grep is used for searching.<br/>
|
||||
Searches are case insensitive.<br/>
|
||||
|
||||
#### Predefined searches
|
||||
|
||||
- `prefix + ctrl-f` - simple *f*ile search
|
||||
- `prefix + ctrl-g` - jumping over *g*it status files (best used after `git status` command)
|
||||
- `prefix + alt-h` - jumping over SHA-1/SHA-256 hashes (best used after `git log` command)
|
||||
- `prefix + ctrl-u` - *u*rl search (http, ftp and git urls)
|
||||
- `prefix + ctrl-d` - number search (mnemonic d, as digit)
|
||||
- `prefix + alt-i` - *i*p address search
|
||||
|
||||
These start "copycat mode" and jump to first match.
|
||||
|
||||
#### "Copycat mode" bindings
|
||||
|
||||
These are enabled when you search with copycat:
|
||||
|
||||
- `n` - jumps to the next match
|
||||
- `N` - jumps to the previous match
|
||||
|
||||
To copy a highlighted match:
|
||||
|
||||
- `Enter` - if you're using Tmux `vi` mode
|
||||
- `ctrl-w` or `alt-w` - if you're using Tmux `emacs` mode
|
||||
|
||||
Copying a highlighted match will take you "out" of copycat mode. Paste with
|
||||
`prefix + ]` (this is Tmux default paste).
|
||||
|
||||
Copying highlighted matches can be enhanced with
|
||||
[tmux yank](https://github.com/tmux-plugins/tmux-yank).
|
||||
|
||||
### Installation with [Tmux Plugin Manager](https://github.com/tmux-plugins/tpm) (recommended)
|
||||
|
||||
Add plugin to the list of TPM plugins in `.tmux.conf`:
|
||||
|
||||
set -g @plugin 'tmux-plugins/tmux-copycat'
|
||||
|
||||
Hit `prefix + I` to fetch the plugin and source it. You should now be able to
|
||||
use the plugin.
|
||||
|
||||
Optional (but recommended) install `gawk` via your package manager of choice
|
||||
for better UTF-8 character support.
|
||||
|
||||
### Manual Installation
|
||||
|
||||
Clone the repo:
|
||||
|
||||
$ git clone https://github.com/tmux-plugins/tmux-copycat ~/clone/path
|
||||
|
||||
Add this line to the bottom of `.tmux.conf`:
|
||||
|
||||
run-shell ~/clone/path/copycat.tmux
|
||||
|
||||
Reload TMUX environment with: `$ tmux source-file ~/.tmux.conf`. You should now
|
||||
be able to use the plugin.
|
||||
|
||||
Optional (but recommended) install `gawk` via your package manager of choice
|
||||
for better UTF-8 character support.
|
||||
|
||||
### Installation for Tmux 2.3 and earlier
|
||||
|
||||
Due to the changes in tmux, the latest version of this plugin doesn't support
|
||||
tmux 2.3 and earlier. It is recommended you upgrade to tmux version 2.4 or
|
||||
later. If you must continue using older version, please follow
|
||||
[these steps for installation](docs/installation_for_tmux_2.3.md).
|
||||
|
||||
### Limitations
|
||||
|
||||
This plugin has some known limitations. Please read about it
|
||||
[here](docs/limitations.md).
|
||||
|
||||
### Docs
|
||||
|
||||
- Most of the behavior of tmux-copycat can be customized via tmux options.
|
||||
[Check out the full options list](docs/customizations.md).
|
||||
- To speed up the workflow you can define new bindings in `.tmux.conf` for
|
||||
searches you use often, more info [here](docs/defining_new_stored_searches.md)
|
||||
|
||||
### Other goodies
|
||||
|
||||
`tmux-copycat` works great with:
|
||||
|
||||
- [tmux-yank](https://github.com/tmux-plugins/tmux-yank) - enables copying
|
||||
highlighted text to system clipboard
|
||||
- [tmux-open](https://github.com/tmux-plugins/tmux-open) - a plugin for quickly
|
||||
opening a highlighted file or a url
|
||||
- [tmux-continuum](https://github.com/tmux-plugins/tmux-continuum) - automatic
|
||||
restoring and continuous saving of tmux env
|
||||
|
||||
### Test suite
|
||||
|
||||
This plugin has a pretty extensive integration test suite that runs on
|
||||
[travis](https://travis-ci.org/tmux-plugins/tmux-copycat).
|
||||
|
||||
When run locally, it depends on `vagrant`. Run it with:
|
||||
|
||||
# within project top directory
|
||||
$ ./run-tests
|
||||
|
||||
### Contributions and new features
|
||||
|
||||
Bug fixes and contributions are welcome.
|
||||
|
||||
Feel free to suggest new features, via github issues.
|
||||
|
||||
If you have a bigger idea you'd like to work on, please get in touch, also via
|
||||
github issues.
|
||||
|
||||
### License
|
||||
|
||||
[MIT](LICENSE.md)
|
70
tmux/plugins/tmux-copycat/copycat.tmux
Executable file
70
tmux/plugins/tmux-copycat/copycat.tmux
Executable file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
source "$CURRENT_DIR/scripts/variables.sh"
|
||||
source "$CURRENT_DIR/scripts/helpers.sh"
|
||||
source "$CURRENT_DIR/scripts/stored_search_helpers.sh"
|
||||
|
||||
# this function defines default stored searches
|
||||
set_default_stored_searches() {
|
||||
local file_search="$(get_tmux_option "$copycat_file_search_option" "$default_file_search_key")"
|
||||
local url_search="$(get_tmux_option "$copycat_url_search_option" "$default_url_search_key")"
|
||||
local digit_search="$(get_tmux_option "$copycat_digit_search_option" "$default_digit_search_key")"
|
||||
local hash_search="$(get_tmux_option "$copycat_hash_search_option" "$default_hash_search_key")"
|
||||
local ip_search="$(get_tmux_option "$copycat_ip_search_option" "$default_ip_search_key")"
|
||||
|
||||
if stored_search_not_defined "$url_search"; then
|
||||
tmux set-option -g "${COPYCAT_VAR_PREFIX}_${url_search}" "(https?://|git@|git://|ssh://|ftp://|file:///)[[:alnum:]?=%/_.:,;~@!#$&()*+-]*"
|
||||
fi
|
||||
if stored_search_not_defined "$file_search"; then
|
||||
tmux set-option -g "${COPYCAT_VAR_PREFIX}_${file_search}" "(^|^\.|[[:space:]]|[[:space:]]\.|[[:space:]]\.\.|^\.\.)[[:alnum:]~_-]*/[][[:alnum:]_.#$%&+=/@-]*"
|
||||
fi
|
||||
if stored_search_not_defined "$digit_search"; then
|
||||
tmux set-option -g "${COPYCAT_VAR_PREFIX}_${digit_search}" "[[:digit:]]+"
|
||||
fi
|
||||
if stored_search_not_defined "$hash_search"; then
|
||||
tmux set-option -g "${COPYCAT_VAR_PREFIX}_${hash_search}" "\b([0-9a-f]{7,40}|[[:alnum:]]{52}|[0-9a-f]{62})\b"
|
||||
fi
|
||||
if stored_search_not_defined "$ip_search"; then
|
||||
tmux set-option -g "${COPYCAT_VAR_PREFIX}_${ip_search}" "[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}"
|
||||
fi
|
||||
}
|
||||
|
||||
set_start_bindings() {
|
||||
set_default_stored_searches
|
||||
local stored_search_vars="$(stored_search_vars)"
|
||||
local search_var
|
||||
local key
|
||||
local pattern
|
||||
for search_var in $stored_search_vars; do
|
||||
key="$(get_stored_search_key "$search_var")"
|
||||
pattern="$(get_stored_search_pattern "$search_var")"
|
||||
tmux bind-key "$key" run-shell "$CURRENT_DIR/scripts/copycat_mode_start.sh '$pattern'"
|
||||
done
|
||||
}
|
||||
|
||||
set_copycat_search_binding() {
|
||||
local key_bindings
|
||||
read -r -d '' -a key_bindings <<<"$(get_tmux_option "$copycat_search_option" "$default_copycat_search_key")"
|
||||
local key
|
||||
for key in "${key_bindings[@]}"; do
|
||||
tmux bind-key "$key" run-shell "$CURRENT_DIR/scripts/copycat_search.sh"
|
||||
done
|
||||
}
|
||||
|
||||
set_copycat_git_special_binding() {
|
||||
local key_bindings
|
||||
read -r -d '' -a key_bindings <<<"$(get_tmux_option "$copycat_git_search_option" "$default_git_search_key")"
|
||||
local key
|
||||
for key in "${key_bindings[@]}"; do
|
||||
tmux bind-key "$key" run-shell "$CURRENT_DIR/scripts/copycat_git_special.sh #{pane_current_path}"
|
||||
done
|
||||
}
|
||||
|
||||
main() {
|
||||
set_start_bindings
|
||||
set_copycat_search_binding
|
||||
set_copycat_git_special_binding
|
||||
}
|
||||
main
|
78
tmux/plugins/tmux-copycat/scripts/check_tmux_version.sh
Executable file
78
tmux/plugins/tmux-copycat/scripts/check_tmux_version.sh
Executable file
@@ -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
|
58
tmux/plugins/tmux-copycat/scripts/copycat_generate_results.sh
Executable file
58
tmux/plugins/tmux-copycat/scripts/copycat_generate_results.sh
Executable file
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
source "$CURRENT_DIR/helpers.sh"
|
||||
|
||||
search_pattern="$1"
|
||||
|
||||
capture_pane() {
|
||||
local file=$1
|
||||
# copying 9M lines back will hopefully fetch the whole scrollback
|
||||
tmux capture-pane -S -9000000 -p > "$file"
|
||||
}
|
||||
|
||||
# doing 2 things in 1 step so that we don't write to disk too much
|
||||
reverse_and_create_copycat_file() {
|
||||
local file=$1
|
||||
local copycat_file=$2
|
||||
local grep_pattern=$3
|
||||
(tac 2>/dev/null || tail -r) < "$file" | grep -oniE "$grep_pattern" > "$copycat_file"
|
||||
}
|
||||
|
||||
delete_old_files() {
|
||||
local scrollback_filename="$(get_scrollback_filename)"
|
||||
local copycat_filename="$(get_copycat_filename)"
|
||||
rm -f "$scrollback_filename" "$copycat_filename"
|
||||
}
|
||||
|
||||
generate_copycat_file() {
|
||||
local grep_pattern="$1"
|
||||
local scrollback_filename="$(get_scrollback_filename)"
|
||||
local copycat_filename="$(get_copycat_filename)"
|
||||
mkdir -p "$(_get_tmp_dir)"
|
||||
chmod 0700 "$(_get_tmp_dir)"
|
||||
capture_pane "$scrollback_filename"
|
||||
reverse_and_create_copycat_file "$scrollback_filename" "$copycat_filename" "$grep_pattern"
|
||||
}
|
||||
|
||||
if_no_results_exit_with_message() {
|
||||
local copycat_filename="$(get_copycat_filename)"
|
||||
# check for empty filename
|
||||
if ! [ -s "$copycat_filename" ]; then
|
||||
display_message "No results!"
|
||||
exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
local grep_pattern="$1"
|
||||
if not_in_copycat_mode; then
|
||||
delete_old_files
|
||||
generate_copycat_file "$grep_pattern"
|
||||
if_no_results_exit_with_message
|
||||
set_copycat_mode
|
||||
copycat_increase_counter
|
||||
fi
|
||||
}
|
||||
main "$search_pattern"
|
58
tmux/plugins/tmux-copycat/scripts/copycat_git_special.sh
Executable file
58
tmux/plugins/tmux-copycat/scripts/copycat_git_special.sh
Executable file
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
PANE_CURRENT_PATH="$1"
|
||||
|
||||
source "$CURRENT_DIR/helpers.sh"
|
||||
|
||||
git_status_files() {
|
||||
local git_working_dir="$PANE_CURRENT_PATH"
|
||||
local git_dir="$PANE_CURRENT_PATH/.git"
|
||||
echo "$(git --git-dir="$git_dir" --work-tree="$git_working_dir" status --porcelain)"
|
||||
}
|
||||
|
||||
formatted_git_status() {
|
||||
local raw_gist_status="$(git_status_files)"
|
||||
echo "$(echo "$raw_gist_status" | cut -c 4-)"
|
||||
}
|
||||
|
||||
exit_if_no_results() {
|
||||
local results="$1"
|
||||
if [ -z "$results" ]; then
|
||||
display_message "No results!"
|
||||
exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
concatenate_files() {
|
||||
local git_status_files="$(formatted_git_status)"
|
||||
exit_if_no_results "$git_status_files"
|
||||
|
||||
local result=""
|
||||
# Undefined until later within a while loop.
|
||||
local file_separator
|
||||
while read -r line; do
|
||||
result="${result}${file_separator}${line}"
|
||||
file_separator="|"
|
||||
done <<< "$git_status_files"
|
||||
echo "$result"
|
||||
}
|
||||
|
||||
# Creates one, big regex out of git status files.
|
||||
# Example:
|
||||
# `git status` shows files `foo.txt` and `bar.txt`
|
||||
# output regex will be:
|
||||
# `(foo.txt|bar.txt)
|
||||
git_status_files_regex() {
|
||||
local concatenated_files="$(concatenate_files)"
|
||||
local regex_result="(${concatenated_files})"
|
||||
echo "$regex_result"
|
||||
}
|
||||
|
||||
main() {
|
||||
local search_regex="$(git_status_files_regex)"
|
||||
# starts copycat mode
|
||||
$CURRENT_DIR/copycat_mode_start.sh "$search_regex"
|
||||
}
|
||||
main
|
289
tmux/plugins/tmux-copycat/scripts/copycat_jump.sh
Executable file
289
tmux/plugins/tmux-copycat/scripts/copycat_jump.sh
Executable file
@@ -0,0 +1,289 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
source "$CURRENT_DIR/helpers.sh"
|
||||
|
||||
MAXIMUM_PADDING="25" # maximum padding below the result when it can't be centered
|
||||
|
||||
# jump to 'next' or 'prev' match
|
||||
# global var for this file
|
||||
NEXT_PREV="$1"
|
||||
|
||||
# 'vi' or 'emacs', this variable used as a global file constant
|
||||
TMUX_COPY_MODE="$(tmux_copy_mode)"
|
||||
|
||||
_file_number_of_lines() {
|
||||
local file="$1"
|
||||
echo "$(wc -l $file | $AWK_CMD '{print $1}')"
|
||||
}
|
||||
|
||||
_get_result_line() {
|
||||
local file="$1"
|
||||
local number="$2"
|
||||
echo "$(head -"$number" "$file" | tail -1)"
|
||||
}
|
||||
|
||||
_string_starts_with_digit() {
|
||||
local string="$1"
|
||||
echo "$string" |
|
||||
\grep -q '^[[:digit:]]\+:'
|
||||
}
|
||||
|
||||
_get_line_number() {
|
||||
local string="$1"
|
||||
local copycat_file="$2" # args 2 & 3 used to handle bug in OSX grep
|
||||
local position_number="$3"
|
||||
if _string_starts_with_digit "$string"; then
|
||||
# we have a number!
|
||||
local grep_line_number="$(echo "$string" | cut -f1 -d:)"
|
||||
# grep line number index starts from 1, tmux line number index starts from 0
|
||||
local tmux_line_number="$((grep_line_number - 1))"
|
||||
else
|
||||
# no number in the results line This is a bug in OSX grep.
|
||||
# Fetching a number from a previous line.
|
||||
local previous_line_num="$((position_number - 1))"
|
||||
local result_line="$(_get_result_line "$copycat_file" "$previous_line_num")"
|
||||
# recursively invoke this same function
|
||||
tmux_line_number="$(_get_line_number "$result_line" "$copycat_file" "$previous_line_num")"
|
||||
fi
|
||||
echo "$tmux_line_number"
|
||||
}
|
||||
|
||||
_get_match() {
|
||||
local string="$1"
|
||||
local full_match
|
||||
if _string_starts_with_digit "$string"; then
|
||||
full_match="$(echo "$string" | cut -f2- -d:)"
|
||||
else
|
||||
# This scenario handles OS X grep bug "no number in the results line".
|
||||
# When there's no number at the beginning of the line, we're taking the
|
||||
# whole line as a match. This handles the result line like this:
|
||||
# `http://www.example.com` (the `http` would otherwise get cut off)
|
||||
full_match="$string"
|
||||
fi
|
||||
echo -n "$full_match"
|
||||
}
|
||||
|
||||
_escape_backslash() {
|
||||
local string="$1"
|
||||
echo "$(echo "$string" | sed 's/\\/\\\\/g')"
|
||||
}
|
||||
|
||||
_get_match_line_position() {
|
||||
local file="$1"
|
||||
local line_number="$2"
|
||||
local match="$3"
|
||||
local adjusted_line_num=$((line_number + 1))
|
||||
local result_line=$(tail -"$adjusted_line_num" "$file" | head -1)
|
||||
|
||||
# OS X awk cannot have `=` as the first char in the variable (bug in awk).
|
||||
# If exists, changing the `=` character with `.` to avoid error.
|
||||
local platform="$(uname)"
|
||||
if [ "$platform" == "Darwin" ]; then
|
||||
result_line="$(echo "$result_line" | sed 's/^=/./')"
|
||||
match="$(echo "$match" | sed 's/^=/./')"
|
||||
fi
|
||||
|
||||
# awk treats \r, \n, \t etc as single characters and that messes up match
|
||||
# highlighting. For that reason, we're escaping backslashes so above chars
|
||||
# are treated literally.
|
||||
result_line="$(_escape_backslash "$result_line")"
|
||||
match="$(_escape_backslash "$match")"
|
||||
|
||||
local index=$($AWK_CMD -v a="$result_line" -v b="$match" 'BEGIN{print index(a,b)}')
|
||||
local zero_index=$((index - 1))
|
||||
echo "$zero_index"
|
||||
}
|
||||
|
||||
_copycat_jump() {
|
||||
local line_number="$1"
|
||||
local match_line_position="$2"
|
||||
local match="$3"
|
||||
local scrollback_line_number="$4"
|
||||
_copycat_enter_mode
|
||||
_copycat_exit_select_mode
|
||||
_copycat_jump_to_line "$line_number" "$scrollback_line_number"
|
||||
_copycat_position_to_match_start "$match_line_position"
|
||||
_copycat_select "$match"
|
||||
}
|
||||
|
||||
_copycat_enter_mode() {
|
||||
tmux copy-mode
|
||||
}
|
||||
|
||||
# clears selection from a previous match
|
||||
_copycat_exit_select_mode() {
|
||||
tmux send-keys -X clear-selection
|
||||
}
|
||||
|
||||
# "manually" go up in the scrollback for a number of lines
|
||||
_copycat_manually_go_up() {
|
||||
local line_number="$1"
|
||||
tmux send-keys -X -N "$line_number" cursor-up
|
||||
tmux send-keys -X start-of-line
|
||||
}
|
||||
|
||||
_copycat_create_padding_below_result() {
|
||||
local number_of_lines="$1"
|
||||
local maximum_padding="$2"
|
||||
local padding
|
||||
|
||||
# Padding should not be greater than half pane height
|
||||
# (it wouldn't be centered then).
|
||||
if [ "$number_of_lines" -gt "$maximum_padding" ]; then
|
||||
padding="$maximum_padding"
|
||||
else
|
||||
padding="$number_of_lines"
|
||||
fi
|
||||
|
||||
# cannot create padding, exit function
|
||||
if [ "$padding" -eq "0" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
tmux send-keys -X -N "$padding" cursor-down
|
||||
tmux send-keys -X -N "$padding" cursor-up
|
||||
}
|
||||
|
||||
# performs a jump to go to line
|
||||
_copycat_go_to_line_with_jump() {
|
||||
local line_number="$1"
|
||||
# first jumps to the "bottom" in copy mode so that jumps are consistent
|
||||
tmux send-keys -X history-bottom
|
||||
tmux send-keys -X start-of-line
|
||||
tmux send-keys -X goto-line $line_number
|
||||
}
|
||||
|
||||
# maximum line number that can be reached via tmux 'jump'
|
||||
_get_max_jump() {
|
||||
local scrollback_line_number="$1"
|
||||
local window_height="$2"
|
||||
local max_jump=$((scrollback_line_number - $window_height))
|
||||
# max jump can't be lower than zero
|
||||
if [ "$max_jump" -lt "0" ]; then
|
||||
max_jump="0"
|
||||
fi
|
||||
echo "$max_jump"
|
||||
}
|
||||
|
||||
_copycat_jump_to_line() {
|
||||
local line_number="$1"
|
||||
local scrollback_line_number="$2"
|
||||
local window_height="$(tmux display-message -p '#{pane_height}')"
|
||||
local correct_line_number
|
||||
|
||||
local max_jump=$(_get_max_jump "$scrollback_line_number" "$window_height")
|
||||
local correction="0"
|
||||
|
||||
if [ "$line_number" -gt "$max_jump" ]; then
|
||||
# We need to 'reach' a line number that is not accessible via 'jump'.
|
||||
# Introducing 'correction'
|
||||
correct_line_number="$max_jump"
|
||||
correction=$((line_number - $correct_line_number))
|
||||
else
|
||||
# we can reach the desired line number via 'jump'. Correction not needed.
|
||||
correct_line_number="$line_number"
|
||||
fi
|
||||
|
||||
_copycat_go_to_line_with_jump "$correct_line_number"
|
||||
|
||||
if [ "$correction" -gt "0" ]; then
|
||||
_copycat_manually_go_up "$correction"
|
||||
fi
|
||||
|
||||
# If no corrections (meaning result is not at the top of scrollback)
|
||||
# we can then 'center' the result within a pane.
|
||||
if [ "$correction" -eq "0" ]; then
|
||||
local half_window_height="$((window_height / 2))"
|
||||
# creating as much padding as possible, up to half pane height
|
||||
_copycat_create_padding_below_result "$line_number" "$half_window_height"
|
||||
fi
|
||||
}
|
||||
|
||||
_copycat_position_to_match_start() {
|
||||
local match_line_position="$1"
|
||||
[ "$match_line_position" -eq "0" ] && return 0
|
||||
|
||||
tmux send-keys -X -N "$match_line_position" cursor-right
|
||||
}
|
||||
|
||||
_copycat_select() {
|
||||
local match="$1"
|
||||
local length="${#match}"
|
||||
tmux send-keys -X begin-selection
|
||||
tmux send-keys -X -N "$length" cursor-right
|
||||
if [ "$TMUX_COPY_MODE" == "vi" ]; then
|
||||
tmux send-keys -X cursor-left # selection correction for 1 char
|
||||
fi
|
||||
}
|
||||
|
||||
# all functions above are "private", called from `do_next_jump` function
|
||||
|
||||
get_new_position_number() {
|
||||
local copycat_file="$1"
|
||||
local current_position="$2"
|
||||
local new_position
|
||||
|
||||
# doing a forward/up jump
|
||||
if [ "$NEXT_PREV" == "next" ]; then
|
||||
local number_of_results=$(wc -l "$copycat_file" | $AWK_CMD '{ print $1 }')
|
||||
if [ "$current_position" -eq "$number_of_results" ]; then
|
||||
# position can't go beyond the last result
|
||||
new_position="$current_position"
|
||||
else
|
||||
new_position="$((current_position + 1))"
|
||||
fi
|
||||
|
||||
# doing a backward/down jump
|
||||
elif [ "$NEXT_PREV" == "prev" ]; then
|
||||
if [ "$current_position" -eq "1" ]; then
|
||||
# position can't go below 1
|
||||
new_position="1"
|
||||
else
|
||||
new_position="$((current_position - 1))"
|
||||
fi
|
||||
fi
|
||||
echo "$new_position"
|
||||
}
|
||||
|
||||
do_next_jump() {
|
||||
local position_number="$1"
|
||||
local copycat_file="$2"
|
||||
local scrollback_file="$3"
|
||||
|
||||
local scrollback_line_number=$(_file_number_of_lines "$scrollback_file")
|
||||
local result_line="$(_get_result_line "$copycat_file" "$position_number")"
|
||||
local line_number=$(_get_line_number "$result_line" "$copycat_file" "$position_number")
|
||||
local match=$(_get_match "$result_line")
|
||||
local match_line_position=$(_get_match_line_position "$scrollback_file" "$line_number" "$match")
|
||||
_copycat_jump "$line_number" "$match_line_position" "$match" "$scrollback_line_number"
|
||||
}
|
||||
|
||||
notify_about_first_last_match() {
|
||||
local current_position="$1"
|
||||
local next_position="$2"
|
||||
local message_duration="1500"
|
||||
|
||||
# if position didn't change, we are either on a 'first' or 'last' match
|
||||
if [ "$current_position" -eq "$next_position" ]; then
|
||||
if [ "$NEXT_PREV" == "next" ]; then
|
||||
display_message "Last match!" "$message_duration"
|
||||
elif [ "$NEXT_PREV" == "prev" ]; then
|
||||
display_message "First match!" "$message_duration"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
if in_copycat_mode; then
|
||||
local copycat_file="$(get_copycat_filename)"
|
||||
local scrollback_file="$(get_scrollback_filename)"
|
||||
local current_position="$(get_copycat_position)"
|
||||
local next_position="$(get_new_position_number "$copycat_file" "$current_position")"
|
||||
do_next_jump "$next_position" "$copycat_file" "$scrollback_file"
|
||||
notify_about_first_last_match "$current_position" "$next_position"
|
||||
set_copycat_position "$next_position"
|
||||
fi
|
||||
}
|
||||
main
|
55
tmux/plugins/tmux-copycat/scripts/copycat_mode_bindings.sh
Executable file
55
tmux/plugins/tmux-copycat/scripts/copycat_mode_bindings.sh
Executable file
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
source "$CURRENT_DIR/helpers.sh"
|
||||
AWK_CMD='awk'
|
||||
if command_exists gawk; then
|
||||
AWK_CMD='gawk'
|
||||
fi
|
||||
|
||||
# Extends a keyboard key.
|
||||
# Benefits: tmux won't report errors and everything will work fine even if the
|
||||
# script is deleted.
|
||||
extend_key() {
|
||||
local key="$1"
|
||||
local script="$2"
|
||||
local cmd
|
||||
|
||||
# 1. 'cmd' or 'key' is sent to tmux. This ensures the default key action is done.
|
||||
# 2. Script is executed.
|
||||
# 3. `true` command ensures an exit status 0 is returned. This ensures a
|
||||
# user never gets an error msg - even if the script file from step 2 is
|
||||
# deleted.
|
||||
# We fetch the current behavior of the 'key' mapping in
|
||||
# variable 'cmd'
|
||||
cmd=$(tmux list-keys -T $(tmux_copy_mode_string) | $AWK_CMD '$4 == "'$key'"' | $AWK_CMD '{ $1=""; $2=""; $3=""; $4=""; sub(" ", " "); print }')
|
||||
# If 'cmd' is already a copycat command, we do nothing
|
||||
if echo "$cmd" | grep -q copycat; then
|
||||
return
|
||||
fi
|
||||
# We save the previous mapping to a file in order to be able to recover
|
||||
# the previous mapping when we unbind
|
||||
tmux list-keys -T $(tmux_copy_mode_string) | $AWK_CMD '$4 == "'$key'"' >> "${TMPDIR:-/tmp}/copycat_$(whoami)_recover_keys"
|
||||
tmux bind-key -T $(tmux_copy_mode_string) "$key" run-shell "tmux $cmd; $script; true"
|
||||
}
|
||||
|
||||
copycat_cancel_bindings() {
|
||||
# keys that quit copy mode are enhanced to quit copycat mode as well.
|
||||
local cancel_mode_bindings=$(copycat_quit_copy_mode_keys)
|
||||
local key
|
||||
for key in $cancel_mode_bindings; do
|
||||
extend_key "$key" "$CURRENT_DIR/copycat_mode_quit.sh"
|
||||
done
|
||||
}
|
||||
|
||||
copycat_mode_bindings() {
|
||||
extend_key "$(copycat_next_key)" "$CURRENT_DIR/copycat_jump.sh 'next'"
|
||||
extend_key "$(copycat_prev_key)" "$CURRENT_DIR/copycat_jump.sh 'prev'"
|
||||
}
|
||||
|
||||
main() {
|
||||
copycat_mode_bindings
|
||||
copycat_cancel_bindings
|
||||
}
|
||||
main
|
38
tmux/plugins/tmux-copycat/scripts/copycat_mode_quit.sh
Executable file
38
tmux/plugins/tmux-copycat/scripts/copycat_mode_quit.sh
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
source "$CURRENT_DIR/helpers.sh"
|
||||
|
||||
unbind_cancel_bindings() {
|
||||
local cancel_mode_bindings=$(copycat_quit_copy_mode_keys)
|
||||
local key
|
||||
for key in $cancel_mode_bindings; do
|
||||
tmux unbind-key -n "$key"
|
||||
done
|
||||
}
|
||||
|
||||
unbind_prev_next_bindings() {
|
||||
tmux unbind-key -n "$(copycat_next_key)"
|
||||
tmux unbind-key -n "$(copycat_prev_key)"
|
||||
}
|
||||
|
||||
unbind_all_bindings() {
|
||||
grep -v copycat <"${TMPDIR:-/tmp}/copycat_$(whoami)_recover_keys" | while read -r key_cmd; do
|
||||
sh -c "tmux $key_cmd"
|
||||
done < /dev/stdin
|
||||
rm "${TMPDIR:-/tmp}/copycat_$(whoami)_recover_keys"
|
||||
}
|
||||
|
||||
main() {
|
||||
if in_copycat_mode; then
|
||||
reset_copycat_position
|
||||
unset_copycat_mode
|
||||
copycat_decrease_counter
|
||||
# removing all bindings only if no panes are in copycat mode
|
||||
if copycat_counter_zero; then
|
||||
unbind_all_bindings
|
||||
fi
|
||||
fi
|
||||
}
|
||||
main
|
21
tmux/plugins/tmux-copycat/scripts/copycat_mode_start.sh
Executable file
21
tmux/plugins/tmux-copycat/scripts/copycat_mode_start.sh
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
SUPPORTED_VERSION="1.9"
|
||||
|
||||
PATTERN="$1"
|
||||
|
||||
supported_tmux_version_ok() {
|
||||
$CURRENT_DIR/check_tmux_version.sh "$SUPPORTED_VERSION"
|
||||
}
|
||||
|
||||
main() {
|
||||
local pattern="$1"
|
||||
if supported_tmux_version_ok; then
|
||||
$CURRENT_DIR/copycat_generate_results.sh "$pattern" # will `exit 0` if no results
|
||||
$CURRENT_DIR/copycat_mode_bindings.sh
|
||||
$CURRENT_DIR/copycat_jump.sh 'next'
|
||||
fi
|
||||
}
|
||||
main "$PATTERN"
|
8
tmux/plugins/tmux-copycat/scripts/copycat_search.sh
Executable file
8
tmux/plugins/tmux-copycat/scripts/copycat_search.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
main() {
|
||||
tmux command-prompt -p "copycat search:" "run-shell \"$CURRENT_DIR/copycat_mode_start.sh '%1'\""
|
||||
}
|
||||
main
|
192
tmux/plugins/tmux-copycat/scripts/helpers.sh
Normal file
192
tmux/plugins/tmux-copycat/scripts/helpers.sh
Normal file
@@ -0,0 +1,192 @@
|
||||
# config options
|
||||
|
||||
default_next_key="n"
|
||||
tmux_option_next="@copycat_next"
|
||||
|
||||
default_prev_key="N"
|
||||
tmux_option_prev="@copycat_prev"
|
||||
|
||||
# keeps track of number of panes in copycat mode
|
||||
tmux_option_counter="@copycat_counter"
|
||||
|
||||
# === awk vs gawk ===
|
||||
command_exists() {
|
||||
command -v "$@" > /dev/null 2>&1
|
||||
}
|
||||
AWK_CMD='awk'
|
||||
if command_exists gawk; then
|
||||
AWK_CMD='gawk'
|
||||
fi
|
||||
|
||||
# === general helpers ===
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
tmux_copy_mode() {
|
||||
tmux show-option -gwv mode-keys
|
||||
}
|
||||
|
||||
tmux_copy_mode_string() {
|
||||
if [ $(tmux_copy_mode) == 'vi' ]; then
|
||||
echo copy-mode-vi
|
||||
else
|
||||
echo copy-mode
|
||||
fi
|
||||
}
|
||||
|
||||
# === copycat mode specific helpers ===
|
||||
|
||||
set_copycat_mode() {
|
||||
set_tmux_option "$(_copycat_mode_var)" "true"
|
||||
}
|
||||
|
||||
unset_copycat_mode() {
|
||||
set_tmux_option "$(_copycat_mode_var)" "false"
|
||||
}
|
||||
|
||||
in_copycat_mode() {
|
||||
local copycat_mode=$(get_tmux_option "$(_copycat_mode_var)" "false")
|
||||
[ "$copycat_mode" == "true" ]
|
||||
}
|
||||
|
||||
not_in_copycat_mode() {
|
||||
if in_copycat_mode; then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
# === copycat mode position ===
|
||||
|
||||
get_copycat_position() {
|
||||
local copycat_position_variable=$(_copycat_position_var)
|
||||
echo $(get_tmux_option "$copycat_position_variable" "0")
|
||||
}
|
||||
|
||||
set_copycat_position() {
|
||||
local position="$1"
|
||||
local copycat_position_variable=$(_copycat_position_var)
|
||||
set_tmux_option "$copycat_position_variable" "$position"
|
||||
}
|
||||
|
||||
reset_copycat_position() {
|
||||
set_copycat_position "0"
|
||||
}
|
||||
|
||||
# === scrollback and results position ===
|
||||
|
||||
get_scrollback_filename() {
|
||||
echo "$(_get_tmp_dir)/scrollback-$(_pane_unique_id)"
|
||||
}
|
||||
|
||||
get_copycat_filename() {
|
||||
echo "$(_get_tmp_dir)/results-$(_pane_unique_id)"
|
||||
}
|
||||
|
||||
# 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"
|
||||
}
|
||||
|
||||
# === counter functions ===
|
||||
|
||||
copycat_increase_counter() {
|
||||
local count=$(get_tmux_option "$tmux_option_counter" "0")
|
||||
local new_count="$((count + 1))"
|
||||
set_tmux_option "$tmux_option_counter" "$new_count"
|
||||
}
|
||||
|
||||
copycat_decrease_counter() {
|
||||
local count="$(get_tmux_option "$tmux_option_counter" "0")"
|
||||
if [ "$count" -gt "0" ]; then
|
||||
# decreasing the counter only if it won't go below 0
|
||||
local new_count="$((count - 1))"
|
||||
set_tmux_option "$tmux_option_counter" "$new_count"
|
||||
fi
|
||||
}
|
||||
|
||||
copycat_counter_zero() {
|
||||
local count="$(get_tmux_option "$tmux_option_counter" "0")"
|
||||
[ "$count" -eq "0" ]
|
||||
}
|
||||
|
||||
# === key binding functions ===
|
||||
|
||||
copycat_next_key() {
|
||||
echo "$(get_tmux_option "$tmux_option_next" "$default_next_key")"
|
||||
}
|
||||
|
||||
copycat_prev_key() {
|
||||
echo "$(get_tmux_option "$tmux_option_prev" "$default_prev_key")"
|
||||
}
|
||||
|
||||
# function expected output: 'C-c Enter q'
|
||||
copycat_quit_copy_mode_keys() {
|
||||
local commands_that_quit_copy_mode="cancel"
|
||||
local copy_mode="$(tmux_copy_mode_string)"
|
||||
tmux list-keys -T "$copy_mode" |
|
||||
\grep "$commands_that_quit_copy_mode" |
|
||||
$AWK_CMD '{ print $4 }' |
|
||||
sort -u |
|
||||
sed 's/C-j//g' |
|
||||
xargs echo
|
||||
}
|
||||
|
||||
# === 'private' functions ===
|
||||
|
||||
_copycat_mode_var() {
|
||||
local pane_id="$(_pane_unique_id)"
|
||||
echo "@copycat_mode_$pane_id"
|
||||
}
|
||||
|
||||
_copycat_position_var() {
|
||||
local pane_id="$(_pane_unique_id)"
|
||||
echo "@copycat_position_$pane_id"
|
||||
}
|
||||
|
||||
_get_tmp_dir() {
|
||||
echo "${TMPDIR:-/tmp}/tmux-$EUID-copycat"
|
||||
}
|
||||
|
||||
# returns a string unique to current pane
|
||||
# sed removes `$` sign because `session_id` contains is
|
||||
_pane_unique_id() {
|
||||
tmux display-message -p "#{session_id}-#{window_index}-#{pane_index}" |
|
||||
sed 's/\$//'
|
||||
}
|
23
tmux/plugins/tmux-copycat/scripts/stored_search_helpers.sh
Normal file
23
tmux/plugins/tmux-copycat/scripts/stored_search_helpers.sh
Normal file
@@ -0,0 +1,23 @@
|
||||
stored_search_not_defined() {
|
||||
local key="$1"
|
||||
local search_value="$(tmux show-option -gqv "${COPYCAT_VAR_PREFIX}_${key}")"
|
||||
[ -z $search_value ]
|
||||
}
|
||||
|
||||
stored_search_vars() {
|
||||
tmux show-options -g |
|
||||
\grep -i "^${COPYCAT_VAR_PREFIX}_" |
|
||||
cut -d ' ' -f1 | # cut just variable names
|
||||
xargs # splat var names in one line
|
||||
}
|
||||
|
||||
# get the search key from the variable name
|
||||
get_stored_search_key() {
|
||||
local search_var="$1"
|
||||
echo "$(echo "$search_var" | sed "s/^${COPYCAT_VAR_PREFIX}_//")"
|
||||
}
|
||||
|
||||
get_stored_search_pattern() {
|
||||
local search_var="$1"
|
||||
echo "$(get_tmux_option "$search_var" "")"
|
||||
}
|
26
tmux/plugins/tmux-copycat/scripts/variables.sh
Normal file
26
tmux/plugins/tmux-copycat/scripts/variables.sh
Normal file
@@ -0,0 +1,26 @@
|
||||
# stored search variable prefix
|
||||
COPYCAT_VAR_PREFIX="@copycat_search"
|
||||
|
||||
# basic search
|
||||
default_copycat_search_key="/"
|
||||
copycat_search_option="@copycat_search"
|
||||
|
||||
# git special search
|
||||
default_git_search_key="C-g"
|
||||
copycat_git_search_option="@copycat_git_special"
|
||||
|
||||
# regular searches
|
||||
default_file_search_key="C-f"
|
||||
copycat_file_search_option="@copycat_file_search"
|
||||
|
||||
default_url_search_key="C-u"
|
||||
copycat_url_search_option="@copycat_url_search"
|
||||
|
||||
default_digit_search_key="C-d"
|
||||
copycat_digit_search_option="@copycat_digit_search"
|
||||
|
||||
default_hash_search_key="M-h"
|
||||
copycat_hash_search_option="@copycat_hash_search"
|
||||
|
||||
default_ip_search_key="M-i"
|
||||
copycat_ip_search_option="@copycat_ip_search"
|
Reference in New Issue
Block a user