Compare commits

...

8 commits
v1.0 ... main

Author SHA1 Message Date
Chris Breneman c80c2c0680 Fix config typo 2021-12-20 02:25:21 -05:00
Chris Breneman 5faa1c4833 Fix README TOC 2021-12-19 02:04:29 -05:00
Chris Breneman 89fec42fe8 Add line copy action.
Closes #10
2021-12-19 01:44:26 -05:00
Chris Breneman 79b7cb64bb Add config for colors
Closes #9
2021-12-12 23:55:31 -05:00
CrispyConductor 0705681ee4
Merge pull request #8 from tomerha/markdown-paths
added ` as a path delimit character for quickcopy and updated regexes
2021-12-06 14:09:52 -05:00
Tomer Harpaz 8f24223ca5 added ` as a path delimit character for quickcopy and updated regexes 2021-12-06 11:53:49 +02:00
CrispyConductor 289e1864ca
Merge pull request #6 from jaehyung-ca/main
fixed the issue that Popen.communicate hangs waiting stdout of a command
2021-11-12 20:42:44 -05:00
Jaehyung Choi 626c408c7d fixed the issue that Popen.communicate hangs waiting stdout of a command 2021-11-12 03:19:32 -08:00
4 changed files with 205 additions and 34 deletions

View file

@ -14,6 +14,7 @@ lines, panes, zooming, etc.
* [easycopy](#easycopy)
* [quickcopy](#quickcopy)
* [quickopen](#quickopen)
* [linecopy](#linecopy)
* [Requirements](#requirements)
* [Installation](#installation)
* [Manual Install](#manual-install)
@ -25,7 +26,7 @@ lines, panes, zooming, etc.
## Features
There are 4 major modes/features:
There are 5 major modes/features:
### easymotion
@ -82,6 +83,25 @@ except that only openable things are highlighted. Selecting one invokes
to import X environment variables from external source to ensure the command
can reach X.
### linecopy
This mode allows copying blocks of full lines, or a single line; essentially a
faster version of easycopy when the start and end points are both line boundaries.
To use, hit `Ctrl-b` `W` to activate linecopy mode. The beginning of each terminal
line is replaced by the label string. Key in a label string to select the first line
to copy, then the label string for the last line to copy (inclusive). The block of
lines will then be copied.
To copy a single line, key in that line's label using uppercase letters. In this
case, do not enter a second label. This will not work if there are any capital
letters defined in the label chars. Single-line copies can also be performed
using quickcopy.
Line beginnings are assigned for all terminal lines, including wrapped lines; but
the end of a line always goes to the end of a "logical" line (a hard line break).
This allows both easily copying wrapped lines and copying some partial lines.
## Requirements
- python3
@ -134,6 +154,7 @@ copy | easymotion lines action (all lines) | `S` `n`
any | easycopy | `<Prefix>` `S`
any | quickcopy | `<Prefix>` `Q`
any | quickopen | `<Prefix>` `P`
any | linecopy | `<Prefix>` `W`
## Options
@ -162,6 +183,10 @@ Option | Default | Description
`@copytk-flash-only-one` | `on` | In quickcopy mode, if there is more than one instance of the copied text on-screen, this is whether to flash all occurrences of the text or just one.
`@copytk-quickopen-env-file` | `~/.tmux-copytk-env` | Path to a file containing newlike-separated `KEY=VALUE` environment variables. These are added to the environment for running the open command. Generation of this file can be automated in your shellrc.
`@copytk-quickopen-open-command` | `xdg-open` on Linux, `open` on Mac | Command to run to open selected blocks in quickopen. The selected text is passed as an argument.
`@copytk-color-highlight` | `green:yellow` | The color to use for highlighted matches, in the form `foreground`:`background`. Valid names are: `none`, `black`, `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, `white`
`@copytk-color-labelchar` | `red:none` | The color to use for the first/active label character.
`@copytk-color-labelchar2` | `yellow:none` | The color to use for the second and subsequent label characters.
`@copytk-color-message` | `red:none` | The color to use for the status message.
### quickcopy/quickopen matches

View file

@ -11,8 +11,8 @@ def make_path_regexes():
# paths with at least 3 elements, and never in the first or last element. There cannot
# be more than one consecutive space, and it cannot be at the beginning or end of the
# element.
edge_delimiters = r'[][\s:=,#$"{}<>()' + "'" + ']'
edge_delimiters_w_slash = r'[][\s:=,#$"{}<>()/' + "'" + ']'
edge_delimiters = r'[][\s:=,#$"{}<>()`' + "'" + ']'
edge_delimiters_w_slash = r'[][\s:=,#$"{}<>()`/' + "'" + ']'
leader = r'(?:^|' + edge_delimiters + ')'
leader_w_slash = r'(?:^|' + edge_delimiters_w_slash + ')'
follower = r'(?:$|' + edge_delimiters + ')'

204
copytk.py
View file

@ -39,11 +39,11 @@ match_expr_presets = {
# matches common types of urls and things that look like urls
'urls': r'(?:^|[][\s:=,#"{}()'+"'"+r'])([a-zA-Z][a-zA-Z0-9]{1,5}://(?:[a-zA-Z0-9_]+(?::[a-zA-Z0-9_-]+)?@)?(?:(?:[a-zA-Z0-9][\w-]*\.)*[a-zA-Z][\w-]*|(?:[0-2]?[0-9]{1,2}\.){3}[0-2]?[0-9]{1,2})(?::[0-9]{1,5})?(?:/(?:[\w.~%/&-]+|(?:[\w.~%/&-]*\([\w.~%/&-]*\)[\w.~%/&-]*)+)?/?)?(?:\?(?:(?:[\w.~%/&-]+|(?:[\w.~%/&-]*\([\w.~%/&-]*\)[\w.~%/&-]*)+)+(?:=(?:[\w.~%/&-]+|(?:[\w.~%/&-]*\([\w.~%/&-]*\)[\w.~%/&-]*)+)?)?&)*(?:(?:[\w.~%/&-]+|(?:[\w.~%/&-]*\([\w.~%/&-]*\)[\w.~%/&-]*)+)+(?:=(?:[\w.~%/&-]+|(?:[\w.~%/&-]*\([\w.~%/&-]*\)[\w.~%/&-]*)+)?)?)?)?(?:#(?:(?:[\w.~%/&-]+|(?:[\w.~%/&-]*\([\w.~%/&-]*\)[\w.~%/&-]*)+)+(?:=(?:[\w.~%/&-]+|(?:[\w.~%/&-]*\([\w.~%/&-]*\)[\w.~%/&-]*)+)?)?&)*(?:(?:[\w.~%/&-]+|(?:[\w.~%/&-]*\([\w.~%/&-]*\)[\w.~%/&-]*)+)+(?:=(?:[\w.~%/&-]+|(?:[\w.~%/&-]*\([\w.~%/&-]*\)[\w.~%/&-]*)+)?)?)?)?)(?:$|[][\s:=,#"{}()'+"'"+r'])',
# Unix and window style absolute paths
'abspaths': r'(?:^|[][\s:=,#$"{}<>()'+"'"+r'])((?:/|~/|[A-Z]:[\\/])(?:(?:(?:[a-zA-Z0-9_-]{1,80}|\.|\.\.)|[a-zA-Z0-9_-]{1,60}\\? [a-zA-Z0-9_-]{1,60})[\\/])*(?:(?:(?:[a-zA-Z0-9_-]{1,80}|\.|\.\.)|[a-zA-Z0-9_-]{1,60}\\? [a-zA-Z0-9_-]{1,60})\.[a-zA-Z0-9]{1,6}|(?:[a-zA-Z0-9_-]{1,80}|\.|\.\.)[\\/]?))(?:$|[][\s:=,#$"{}<>()'+"'"+r'])',
'abspaths': r'(?:^|[][\s:=,#$"{}<>()`'+"'"+r'])((?:/|~/|[A-Z]:[\\/])(?:(?:(?:[a-zA-Z0-9_-]{1,80}|\.|\.\.)|[a-zA-Z0-9_-]{1,60}\\? [a-zA-Z0-9_-]{1,60})[\\/])*(?:(?:(?:[a-zA-Z0-9_-]{1,80}|\.|\.\.)|[a-zA-Z0-9_-]{1,60}\\? [a-zA-Z0-9_-]{1,60})\.[a-zA-Z0-9]{1,6}|(?:[a-zA-Z0-9_-]{1,80}|\.|\.\.)[\\/]?))(?:$|[][\s:=,#$"{}<>()`'+"'"+r'])',
# Absolute or relative paths
'paths': r'(?:^|[][\s:=,#$"{}<>()'+"'"+r'])((?:(?:/|~/|[A-Z]:[\\/])(?:(?:(?:[a-zA-Z0-9_-]{1,80}|\.|\.\.)|[a-zA-Z0-9_-]{1,60}\\? [a-zA-Z0-9_-]{1,60})[\\/])*(?:(?:(?:[a-zA-Z0-9_-]{1,80}|\.|\.\.)|[a-zA-Z0-9_-]{1,60}\\? [a-zA-Z0-9_-]{1,60})\.[a-zA-Z0-9]{1,6}|(?:[a-zA-Z0-9_-]{1,80}|\.|\.\.)[\\/]?)|(?:[a-zA-Z0-9_-]{1,80}|\.|\.\.)[\\/](?:(?:[a-zA-Z0-9_-]{1,80}|\.|\.\.)[\\/]?|(?:(?:[a-zA-Z0-9_-]{1,80}|\.|\.\.)|[a-zA-Z0-9_-]{1,60}\\? [a-zA-Z0-9_-]{1,60})\.[a-zA-Z0-9]{1,6})|(?:[a-zA-Z0-9_-]{1,80}|\.|\.\.)[\\/](?:(?:(?:[a-zA-Z0-9_-]{1,80}|\.|\.\.)|[a-zA-Z0-9_-]{1,60}\\? [a-zA-Z0-9_-]{1,60})[\\/])+(?:(?:[a-zA-Z0-9_-]{1,80}|\.|\.\.)[\\/]?|(?:(?:[a-zA-Z0-9_-]{1,80}|\.|\.\.)|[a-zA-Z0-9_-]{1,60}\\? [a-zA-Z0-9_-]{1,60})\.[a-zA-Z0-9]{1,6})))(?:$|[][\s:=,#$"{}<>()'+"'"+r'])',
'paths': r'(?:^|[][\s:=,#$"{}<>()`'+"'"+r'])((?:(?:/|~/|[A-Z]:[\\/])(?:(?:(?:[a-zA-Z0-9_-]{1,80}|\.|\.\.)|[a-zA-Z0-9_-]{1,60}\\? [a-zA-Z0-9_-]{1,60})[\\/])*(?:(?:(?:[a-zA-Z0-9_-]{1,80}|\.|\.\.)|[a-zA-Z0-9_-]{1,60}\\? [a-zA-Z0-9_-]{1,60})\.[a-zA-Z0-9]{1,6}|(?:[a-zA-Z0-9_-]{1,80}|\.|\.\.)[\\/]?)|(?:[a-zA-Z0-9_-]{1,80}|\.|\.\.)[\\/](?:(?:[a-zA-Z0-9_-]{1,80}|\.|\.\.)[\\/]?|(?:(?:[a-zA-Z0-9_-]{1,80}|\.|\.\.)|[a-zA-Z0-9_-]{1,60}\\? [a-zA-Z0-9_-]{1,60})\.[a-zA-Z0-9]{1,6})|(?:[a-zA-Z0-9_-]{1,80}|\.|\.\.)[\\/](?:(?:(?:[a-zA-Z0-9_-]{1,80}|\.|\.\.)|[a-zA-Z0-9_-]{1,60}\\? [a-zA-Z0-9_-]{1,60})[\\/])+(?:(?:[a-zA-Z0-9_-]{1,80}|\.|\.\.)[\\/]?|(?:(?:[a-zA-Z0-9_-]{1,80}|\.|\.\.)|[a-zA-Z0-9_-]{1,60}\\? [a-zA-Z0-9_-]{1,60})\.[a-zA-Z0-9]{1,6})))(?:$|[][\s:=,#$"{}<>()`'+"'"+r'])',
# Isolated filenames without paths
'filenames': r'(?:^|[][\s:=,#$"{}<>()/'+"'"+r'])([a-zA-Z0-9_-]{1,80}\.[a-zA-Z][a-zA-Z0-9]{0,5})(?:$|[][\s:=,#$"{}<>()'+"'"+r'])'
'filenames': r'(?:^|[][\s:=,#$"{}<>()`/'+"'"+r'])([a-zA-Z0-9_-]{1,80}\.[a-zA-Z][a-zA-Z0-9]{0,5})(?:$|[][\s:=,#$"{}<>()`'+"'"+r'])',
}
@ -106,28 +106,20 @@ def runtmux(args, one=False, lines=False, noblanklines=False, sendstdin=None):
return dlines[0] if len(dlines) > 0 else ''
return dlines if lines else data
def runshellcommand(command, one=False, lines=False, noblanklines=False, sendstdin=None, raisenonzero=True):
def runshellcommand(command, sendstdin=None, raisenonzero=True):
log('run shell command: ' + command, time=True)
with subprocess.Popen(
command,
shell=True,
executable='/bin/bash',
stdin=subprocess.PIPE if sendstdin != None else subprocess.DEVNULL,
stdout=subprocess.PIPE
stdout=subprocess.DEVNULL
) as proc:
if sendstdin != None and isinstance(sendstdin, str):
sendstdin = bytearray(sendstdin, 'utf8')
recvstdout, _ = proc.communicate(input=sendstdin)
proc.communicate(input=sendstdin)
if proc.returncode != 0 and raisenonzero:
raise Exception(f'Command {command} returned exit code {proc.returncode}')
data = recvstdout.decode('utf8')
if one or lines: # return list of lines
dlines = data.split('\n')
if not one and noblanklines:
dlines = [ l for l in dlines if len(l) > 0 ]
if one: # single-line
return dlines[0] if len(dlines) > 0 else ''
return dlines if lines else data
def runtmuxmulti(argsets):
@ -218,6 +210,33 @@ def get_tmux_option_key_curses(name, default=None, optmode='g', aslist=False):
else:
return remap.get(v, v)
def get_tmux_option_color_pair_curses(name, default_fg=-1, default_bg=-1):
strmap = {
'none': -1,
'black': curses.COLOR_BLACK,
'red': curses.COLOR_RED,
'green': curses.COLOR_GREEN,
'yellow': curses.COLOR_YELLOW,
'blue': curses.COLOR_BLUE,
'magenta': curses.COLOR_MAGENTA,
'cyan': curses.COLOR_CYAN,
'white': curses.COLOR_WHITE
}
v = get_tmux_option(name)
if v == None:
return (default_fg, default_bg)
parts = v.lower().split(':')
if parts[0] not in strmap:
raise Exception(f'Invalid color {parts[0]}')
fg = strmap[parts[0]]
if len(parts) > 1:
if parts[1] not in strmap:
raise Exception(f'Invalid color {parts[1]}')
bg = strmap[parts[1]]
else:
bg = default_bg
return (fg, bg)
def capture_pane_contents(target=None, opts=None):
args = [ 'capture-pane', '-p' ]
if opts:
@ -247,7 +266,7 @@ def get_pane_info(target=None, capture=False, capturej=False):
'window_id_full': r[0] + ':' + r[1],
'pane_id': r[2],
'pane_id_full': r[0] + ':' + r[1] + '.' + r[2],
'pane_size': (int(r[3]), int(r[4])),
'pane_size': (int(r[3]), int(r[4])), # (width, height)
'zoomed': bool(int(r[5])),
'cursor': copycursorpos if mode == 'copy-mode' else cursorpos,
'scroll_position': int(r[11]) if r[11] != '' else None,
@ -257,8 +276,10 @@ def get_pane_info(target=None, capture=False, capturej=False):
if mode == 'copy-mode' and rdict['scroll_position'] != None and rdict['scroll_position'] > 0:
capture_opts += [ '-S', str(-rdict['scroll_position']), '-E', str(-rdict['scroll_position'] + rdict['pane_size'][1] - 1) ]
if capture:
# The "normal" pane capture includes "hard" newlines at line wraps and truncates trailing spaces
rdict['contents'] = capture_pane_contents(rdict['pane_id_full'], capture_opts)
if capturej:
# The "-J" pane capture includes trailing spaces and does not have newlines for wrapping
rdict['contentsj'] = capture_pane_contents(rdict['pane_id_full'], [ '-J' ] + capture_opts)
return rdict
@ -359,6 +380,17 @@ def gen_em_labels(n, chars=None, min_nchars=1, max_nchars=None):
yield ''.join(label)
def process_pane_capture_lines(data, nlines=None):
"""Given the string blob of data from `tmux capture-pane`, returns an array of line strings.
Arguments:
data -- String blob of data from `tmux capture-pane`
nlines -- Maximum number of lines to return
Returns:
An array of line strings. Each line string corresponds to a single visible line such that
a wrapped line is returned as multiple visible lines. Nonprintable characters are removed
and tabs are converted to spaces.
"""
# processes pane capture data into an array of lines
# also handles nonprintables
lines = [
@ -521,6 +553,7 @@ class PaneJumpAction:
# Fetch options
self.em_label_chars = get_tmux_option('@copytk-label-chars', 'asdghklqwertyuiopzxcvbnmfj;')
self.has_capital_label_chars = bool(re.search(r'[A-Z]', self.em_label_chars))
# Sanitize the J capture data by removing trailing spaces on each line
self.copy_data = '\n'.join(( line.rstrip() for line in self.orig_pane['contentsj'].split('\n') ))
@ -546,10 +579,13 @@ class PaneJumpAction:
curses.curs_set(False)
curses.start_color()
curses.use_default_colors()
curses.init_pair(1, curses.COLOR_RED, -1) # color for label first char
curses.init_pair(2, curses.COLOR_YELLOW, -1) # color for label second+ char
curses.init_pair(3, curses.COLOR_GREEN, curses.COLOR_YELLOW) # color for highlight
curses.init_pair(4, curses.COLOR_RED, -1)
def init_color(index, optname, default_fg, default_bg):
pair = get_tmux_option_color_pair_curses(optname, default_fg, default_bg)
curses.init_pair(index, pair[0], pair[1])
init_color(1, '@copytk-color-labelchar', curses.COLOR_RED, -1) # first label char
init_color(2, '@copytk-color-labelchar2', curses.COLOR_YELLOW, -1) # second+ label char
init_color(3, '@copytk-color-highlight', curses.COLOR_GREEN, curses.COLOR_YELLOW) # highlight
init_color(4, '@copytk-color-message', curses.COLOR_RED, -1) # status message
self.stdscr.clear()
# Track the size as known by curses
@ -706,6 +742,7 @@ class EasyMotionAction(PaneJumpAction):
self.search_len = search_len
self.case_sensitive_search = get_tmux_option('@copytk-case-sensitive-search', 'upper') # value values: on, off, upper
self.min_match_spacing = int(get_tmux_option('@copytk-min-match-spacing', '2'))
self.loc_label_mapping = {} # override mapping from match loc tuples to labels
def _em_filter_locs(self, locs):
d = args.search_direction
@ -755,6 +792,15 @@ class EasyMotionAction(PaneJumpAction):
return search_str
def get_locations(self, action):
"""Returns a list of (x, y) locations of potential match locations.
Arguments:
action -- Either 'search' (to input a search char) or 'lines' (to use each line as a location)
Returns:
A list of (x, y) tuples where y is the line number and x is the character in the line. The lines
represent "physical" lines (eg. a single wrapped line is treated as multiple physical lines here.)
"""
pane_search_lines = self.display_content_lines
log('\n'.join(pane_search_lines), 'pane_search_lines')
@ -771,7 +817,21 @@ class EasyMotionAction(PaneJumpAction):
else:
raise Exception('Invalid copytk easymotion action')
def do_easymotion(self, action, filter_locs=None, sort_close_to=None):
def _input_easymotion_keys(self):
"""Waits for easymotion keypresses to select match; filters possible matches as is executed."""
# Wait for label presses
keyed_label = ''
while True: # loop over each key/char in the label
keyed_label += self.getkey()
self.cur_label_pos += 1
self.match_locations = [ m for m in self.match_locations if m[2].startswith(keyed_label) ]
if len(self.match_locations) < 2:
break
self.redraw()
log('keyed label: ' + keyed_label, time=True)
def do_easymotion(self, action, filter_locs=None, sort_close_to=None, save_labels=False):
# Get possible jump locations sorted by proximity to cursor
locs = self.get_locations(action)
locs = self._em_filter_locs(locs)
@ -783,21 +843,28 @@ class EasyMotionAction(PaneJumpAction):
# Assign each match a label
label_it = gen_em_labels(len(locs), self.em_label_chars)
self.match_locations = [ (ml[0], ml[1], next(label_it) ) for ml in locs ]
self.match_locations = []
used_labels = { label : loc for loc, label in self.loc_label_mapping.items() }
for ml in locs:
if ml in self.loc_label_mapping:
label = self.loc_label_mapping[ml]
else:
while True:
label = next(label_it)
if label not in used_labels:
break
used_labels[label] = ml
self.match_locations.append(( ml[0], ml[1], label ))
# If save_labels is true, preserve labels for locations across batches
if save_labels:
self.loc_label_mapping.update({ loc : label for label, loc in used_labels.items() })
# Draw labels
self.redraw()
# Wait for label presses
keyed_label = ''
while True: # loop over each key/char in the label
keyed_label += self.getkey()
self.cur_label_pos += 1
self.match_locations = [ m for m in self.match_locations if m[2].startswith(keyed_label) ]
if len(self.match_locations) < 2:
break
self.redraw()
log('keyed label: ' + keyed_label, time=True)
# Wait for keypresses
self._input_easymotion_keys()
if len(self.match_locations) == 0:
return None
@ -850,6 +917,76 @@ class EasyCopyAction(EasyMotionAction):
self.flash_highlight_range((pos1, pos2))
class LineCopyAction(EasyMotionAction):
def __init__(self, stdscr):
super().__init__(stdscr)
# override from EasyMotionAction to support single-char line selection
def _input_easymotion_keys(self):
"""Waits for easymotion keypresses to select match; filters possible matches as is executed."""
keyed_label = ''
while True: # loop over each key/char in the label
k = self.getkey()
if not self.has_capital_label_chars and re.fullmatch('[A-Z]', k) and self.easymotion_phase == 0:
# Enable single-line-copy mode
k = k.lower()
self.single_line_copy = True
keyed_label += k
self.cur_label_pos += 1
self.match_locations = [ m for m in self.match_locations if m[2].startswith(keyed_label) ]
if len(self.match_locations) < 2:
break
self.redraw()
log('keyed label: ' + keyed_label, time=True)
def run(self):
log('easycopy linecopy swapping in hidden pane', time=True)
swap_hidden_pane(True)
# Input searches to get bounds
self.easymotion_phase = 0
self.single_line_copy = False
pos1 = self.do_easymotion('lines', save_labels=True)
if not pos1: return
if self.single_line_copy:
# Copy single logical line starting at pos1.
# Find end of line as a copy data index
startidx = self.disp_copy_map[pos1]
endidx = self.copy_data.find('\n', startidx)
if endidx == -1:
endidx = len(self.copy_data)
pos2 = self.copy_disp_map[len(self.copy_data) - 1]
selected_data = self.copy_data[startidx:]
else:
pos2 = self.copy_disp_map[endidx-1]
selected_data = self.copy_data[startidx:endidx]
else:
self.highlight_ranges = [ (pos1, pos1) ]
self.reset(keep_highlight=True)
# restrict second search to after first position
self.easymotion_phase = 1
pos2 = self.do_easymotion(
'lines',
filter_locs=lambda loc: loc[1] > pos1[1] or (loc[1] == pos1[1] and loc[0] >= pos1[0]),
sort_close_to=pos1
)
if not pos2: return
# since typing last n letters of word, advance end position by n-1 (-1 because range is inclusive)
pos2 = (self.orig_pane['pane_size'][0] - 1, pos2[1])
# Find the data associated with this range and run the copy command
selected_data = self.copy_data[self.disp_copy_map[pos1] : self.disp_copy_map[pos2] + 1]
log('Copied: ' + selected_data)
execute_copy(selected_data)
# Flash selected range as confirmation
self.flash_highlight_range((pos1, pos2))
class QuickCopyAction(PaneJumpAction):
def __init__(self, stdscr, options_prefix='@copytk-quickcopy-'):
@ -1140,6 +1277,9 @@ def run_easycopy(stdscr):
nkeys = int(args.search_nkeys)
EasyCopyAction(stdscr, nkeys).run()
def run_linecopy(stdscr):
LineCopyAction(stdscr).run()
def run_quickcopy(stdscr):
QuickCopyAction(stdscr).run()
@ -1232,6 +1372,8 @@ try:
curses.wrapper(run_easymotion)
elif args.action == 'easycopy':
curses.wrapper(run_easycopy)
elif args.action == 'linecopy':
curses.wrapper(run_linecopy)
elif args.action == 'quickcopy':
curses.wrapper(run_quickcopy)
elif args.action == 'quickopen':

View file

@ -30,6 +30,10 @@ tmux bind-key -T copytk Y run-shell -b "python3 $CURRENT_DIR/copytk.py easycopy
tmux bind-key -T prefix S run-shell -b "python3 $CURRENT_DIR/copytk.py easycopy --search-nkeys 1"
tmux bind-key -T prefix C-s run-shell -b "python3 $CURRENT_DIR/copytk.py easycopy --search-nkeys 1"
# tmux prefix: linecopy action bindings
tmux bind-key -T prefix W run-shell -b "python3 $CURRENT_DIR/copytk.py linecopy"
tmux bind-key -T prefix C-w run-shell -b "python3 $CURRENT_DIR/copytk.py linecopy"
# tmux prefix: quickcopy action bindings
tmux bind-key -T prefix Q run-shell -b "python3 $CURRENT_DIR/copytk.py quickcopy"
tmux bind-key -T prefix C-q run-shell -b "python3 $CURRENT_DIR/copytk.py quickcopy"