87 lines
3.0 KiB
Python
87 lines
3.0 KiB
Python
|
#!/bin/env python3
|
||
|
import sys
|
||
|
import re
|
||
|
import json
|
||
|
from subprocess import run, PIPE
|
||
|
import argparse
|
||
|
|
||
|
|
||
|
def get_ws():
|
||
|
return json.loads(run('i3-msg -t get_workspaces'.split(), stdout=PIPE).stdout)
|
||
|
|
||
|
|
||
|
def to_empty(current=None, strict=False, right=True, move=False, wrap=True, min_num=1):
|
||
|
"""Move to nearest empty numbered workspace"""
|
||
|
if current is None:
|
||
|
current = [w for w in get_ws() if w['focused']][0]
|
||
|
|
||
|
numbered = re.compile('^\d+$' if strict else '^\d+|(\d+:.*)$')
|
||
|
|
||
|
ns = {w['num'] for w in get_ws() if numbered.match(w['name']) != None and
|
||
|
(not strict or w['name'] == str(w['num']))}
|
||
|
|
||
|
if numbered.match(current['name']) is None:
|
||
|
# If current workspace is unnumbered, pick rightmost or leftmost
|
||
|
if len(ns) == 0:
|
||
|
new_ix = min_num
|
||
|
else:
|
||
|
new_ix = min(ns) - 1 if right else max(ns) + 1
|
||
|
else:
|
||
|
# Find numbered workspace nearest to current
|
||
|
new_ix = current['num']
|
||
|
|
||
|
while new_ix in ns:
|
||
|
new_ix += 1 if right else -1
|
||
|
|
||
|
# Wrap around
|
||
|
if new_ix < min_num:
|
||
|
if wrap:
|
||
|
new_ix = max(ns) + 1
|
||
|
else:
|
||
|
return
|
||
|
|
||
|
if move:
|
||
|
s = 'i3-msg move container to workspace {0}; workspace {0}'.format(new_ix)
|
||
|
else:
|
||
|
s = 'i3-msg workspace ' + str(new_ix)
|
||
|
print(s)
|
||
|
run(s.split())
|
||
|
|
||
|
|
||
|
def to_empty_near(num, relative=False, **kwargs):
|
||
|
"""Move to empty numbered workspace nearest to workspace 'num'"""
|
||
|
if relative:
|
||
|
if 0 <= num < len(get_ws()):
|
||
|
current = get_ws()[num]
|
||
|
else:
|
||
|
return
|
||
|
else:
|
||
|
ns = [w for w in get_ws() if w['num'] == num]
|
||
|
current = ns[0] if len(ns) > 0 else {'num': num, 'name': str(num)}
|
||
|
to_empty(current, **kwargs)
|
||
|
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
parser = argparse.ArgumentParser(description='Switch to an empty numbered workspace.')
|
||
|
parser.add_argument('direction', type=str, nargs='?', default='next',
|
||
|
help='either next (default) or prev')
|
||
|
parser.add_argument('number', type=int, nargs='?',
|
||
|
help='workspace to start searching from (default: current)')
|
||
|
parser.add_argument('-r', '--relative', dest='rel', action='store_true',
|
||
|
help='use workspace indices, not numbers (default: no)')
|
||
|
parser.add_argument('-w', '--nowrap', dest='wrap', action='store_false',
|
||
|
help='if at edge, wrap around to other edge (default: yes)')
|
||
|
parser.add_argument('-s', '--nostrict', dest='strict', action='store_false',
|
||
|
help='numbered workspaces have a numeric name (default: yes)')
|
||
|
parser.add_argument('-m', '--move', dest='move', action='store_true',
|
||
|
help='move container to new workspace (default: no)')
|
||
|
|
||
|
args = parser.parse_args()
|
||
|
kwargs = {'right': args.direction.lower().strip() == 'next',
|
||
|
'wrap': args.wrap, 'strict': args.strict, 'move': args.move}
|
||
|
|
||
|
if type(args.number) is int:
|
||
|
to_empty_near(args.number, relative=args.rel, **kwargs)
|
||
|
else:
|
||
|
to_empty(**kwargs)
|