Skip to content

Load config appending windows in current active session #501

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 14 commits into from
Closed
1 change: 0 additions & 1 deletion doc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ Internals
CLI
---

.. automethod:: tmuxp.cli._reattach
.. automethod:: tmuxp.cli.get_config_dir
.. automethod:: tmuxp.cli.get_teamocil_dir
.. automethod:: tmuxp.cli.get_tmuxinator_dir
Expand Down
18 changes: 18 additions & 0 deletions doc/cli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,24 @@ without being attached. The last one will be attached if there is no

$ tmuxp load <filename1> <filename2> ...

If you try to load a config file within a tmux session, it will ask you
if you want to load and attach in the new session or just load detached.
You can also load config appending windows in the current active session.

::

Already inside TMUX, switch to session? yes/no
Or (a)ppend windows in the current active session?
[y/n/a]:

All this modes you can force doing:

.. code-block:: bash

$ tmuxp load -y config # load attached
$ tmuxp load -d config # load detached
$ tmuxp load -a config # load appending

.. _cli_import:

Import
Expand Down
3 changes: 2 additions & 1 deletion doc/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ Load multiple tmux sessions at once:
$ tmuxp load example.yaml anothersession.yaml

tmuxp will offer to ``switch-client`` for you if you're already in a
session.
session. You can also load config appending windows in the current
active session.

You can also have a custom tmuxp config directory by setting the
``TMUX_CONFIGDIR`` in your environment variables.
Expand Down
15 changes: 15 additions & 0 deletions tests/fixtures/workspacebuilder/three_windows.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
session_name: sample_three_windows
windows:
- window_name: first
panes:
- shell_command:
- echo 'first window'
- window_name: second
panes:
- shell_command:
- echo 'second window'
- window_name: third
panes:
- shell_command:
- echo 'third window'

10 changes: 10 additions & 0 deletions tests/fixtures/workspacebuilder/two_windows.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
session_name: sample_two_windows
windows:
- window_name: first
panes:
- shell_command:
- echo 'first window'
- window_name: second
panes:
- shell_command:
- echo 'second window'
48 changes: 48 additions & 0 deletions tests/test_workspacebuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -673,3 +673,51 @@ def test_before_load_true_if_test_passes_with_args(server):

with temp_session(server) as session:
builder.build(session=session)


def test_load_configs_same_session(server):
yaml_config = loadfixture("workspacebuilder/three_windows.yaml")
sconfig = kaptan.Kaptan(handler='yaml')
sconfig = sconfig.import_config(yaml_config).get()

builder = WorkspaceBuilder(sconf=sconfig, server=server)
builder.build()

assert len(server.sessions) == 1
assert len(server.sessions[0]._windows) == 3

yaml_config = loadfixture("workspacebuilder/two_windows.yaml")

sconfig = kaptan.Kaptan(handler='yaml')
sconfig = sconfig.import_config(yaml_config).get()

builder = WorkspaceBuilder(sconf=sconfig, server=server)
builder.build(server.sessions[0], True)

assert len(server.sessions) == 1
assert len(server.sessions[0]._windows) == 5


def test_load_configs_separate_sessions(server):
yaml_config = loadfixture("workspacebuilder/three_windows.yaml")
sconfig = kaptan.Kaptan(handler='yaml')
sconfig = sconfig.import_config(yaml_config).get()

builder = WorkspaceBuilder(sconf=sconfig, server=server)
builder.build()

assert len(server.sessions) == 1
assert len(server.sessions[0]._windows) == 3

yaml_config = loadfixture("workspacebuilder/two_windows.yaml")

sconfig = kaptan.Kaptan(handler='yaml')
sconfig = sconfig.import_config(yaml_config).get()

builder = WorkspaceBuilder(sconf=sconfig, server=server)
builder.build()

assert len(server.sessions) == 2
assert len(server.sessions[0]._windows) == 3
assert len(server.sessions[1]._windows) == 2

2 changes: 1 addition & 1 deletion tmuxp/__about__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
__title__ = 'tmuxp'
__package_name__ = 'tmuxp'
__version__ = '1.5.3'
__version__ = '1.6.0'
__description__ = 'tmux session manager'
__email__ = '[email protected]'
__author__ = 'Tony Narlock'
Expand Down
158 changes: 87 additions & 71 deletions tmuxp/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,36 +368,14 @@ def scan_config(config, config_dir=None):
return config


def _reattach(session):
"""
Reattach session (depending on env being inside tmux already or not)

Parameters
----------
session : :class:`libtmux.Session`

Notes
-----
If ``TMUX`` environmental variable exists in the environment this script is
running, that means we're in a tmux client. So ``tmux switch-client`` will
load the session.

If not, ``tmux attach-session`` loads the client to the target session.
"""
if 'TMUX' in os.environ:
session.switch_client()

else:
session.attach_session()


def load_workspace(
config_file,
socket_name=None,
socket_path=None,
colors=None,
detached=False,
answer_yes=False,
append=False,
):
"""
Load a tmux "workspace" session via tmuxp file.
Expand All @@ -416,7 +394,10 @@ def load_workspace(
detached : bool
Force detached state. default False.
answer_yes : bool
Assume yes when given prompt. default False.
Assume yes when given prompt to attach in a new sesion. default False.
append: bool
Assume current when given prompt to append windows in in same Session.
Default False.

Notes
-----
Expand Down Expand Up @@ -509,60 +490,41 @@ def load_workspace(
click.echo('%s is empty or parsed no config data' % config_file, err=True)
return

session_name = sconfig['session_name']

# if the session already exists, prompt the user to attach. tmuxp doesn't
# support incremental session building or appending (yet, PR's welcome!)
if builder.session_exists(session_name):
if not detached and (
answer_yes
or click.confirm(
'%s is already running. Attach?'
% click.style(session_name, fg='green'),
default=True,
)
):
_reattach(builder.session)
return

try:
click.echo(
click.style('[Loading] ', fg='green')
+ click.style(config_file, fg='blue', bold=True)
)

builder.build() # load tmux session via workspace builder
if detached:
load_detached(builder)
return builder.session

if append:
if 'TMUX' in os.environ:
load_append_windows_same_session(builder)
else:
load_attached(builder, detached)
return builder.session

if answer_yes:
load_attached(builder, detached)
return builder.session

if 'TMUX' in os.environ: # tmuxp ran from inside tmux
if not detached and (
answer_yes or click.confirm('Already inside TMUX, switch to session?')
):
# unset TMUX, save it, e.g. '/tmp/tmux-1000/default,30668,0'
tmux_env = os.environ.pop('TMUX')

if has_gte_version('2.6'):
set_layout_hook(builder.session, 'client-session-changed')

builder.session.switch_client() # switch client to new session

os.environ['TMUX'] = tmux_env # set TMUX back again
return builder.session
else: # session created in the background, from within tmux
if has_gte_version('2.6'): # prepare for both cases
set_layout_hook(builder.session, 'client-attached')
set_layout_hook(builder.session, 'client-session-changed')

sys.exit('Session created in detached state.')
else: # tmuxp ran from inside tmux
if has_gte_version('2.6'):
# if attaching for first time
set_layout_hook(builder.session, 'client-attached')

# for cases where user switches client for first time
set_layout_hook(builder.session, 'client-session-changed')

if not detached:
builder.session.attach_session()
msg = "Already inside TMUX, switch to session? yes/no\n"\
"Or (a)ppend windows in the current active session?\n[y/n/a]"
options = ['y', 'n', 'a']
choice = click.prompt(msg, value_proc=_validate_choices(options))

if choice == 'y':
load_attached(builder, detached)
elif choice == 'a':
load_append_windows_same_session(builder)
else:
return load_detached(builder)
else:
load_attached(builder, detached)

except exc.TmuxpException as e:
import traceback
Expand Down Expand Up @@ -590,6 +552,47 @@ def load_workspace(
return builder.session


def load_attached(builder, detached):
"""
Load config in a new session and attach.
"""
builder.build() # load config in a new session
if 'TMUX' in os.environ:
# unset TMUX, save it, e.g. '/tmp/tmux-1000/default,30668,0'
tmux_env = os.environ.pop('TMUX')
if has_gte_version('2.6'):
set_layout_hook(builder.session, 'client-session-changed')
builder.session.switch_client() # switch client to new session
os.environ['TMUX'] = tmux_env # set TMUX back again
else:
if has_gte_version('2.6'):
# if attaching for first time
set_layout_hook(builder.session, 'client-attached')
# for cases where user switches client for first time
set_layout_hook(builder.session, 'client-session-changed')
if not detached:
builder.session.attach_session()


def load_detached(builder):
builder.build()
if has_gte_version('2.6'): # prepare for both cases
set_layout_hook(builder.session, 'client-attached')
set_layout_hook(builder.session, 'client-session-changed')
print('Session created in detached state.')


def load_append_windows_same_session(builder):
"""
Load configs in the active session appending windows
"""
current_attached_sesssion = builder.find_current_attached_session()
builder.build(current_attached_sesssion, True)
if has_gte_version('2.6'): # prepare for both cases
set_layout_hook(builder.session, 'client-attached')
set_layout_hook(builder.session, 'client-session-changed')


@click.group(context_settings={'obj': {}})
@click.version_option(__version__, '-V', '--version', message='%(prog)s %(version)s')
@click.option(
Expand Down Expand Up @@ -746,6 +749,9 @@ def command_freeze(session_name, socket_name, socket_path):
@click.option(
'-d', 'detached', help='Load the session without attaching it', is_flag=True
)
@click.option(
'-a', 'append', help='Load appending windows in active session', is_flag=True
)
@click.option(
'colors',
'-2',
Expand All @@ -759,7 +765,16 @@ def command_freeze(session_name, socket_name, socket_path):
flag_value=88,
help='Like -2, but indicates that the terminal supports 88 colours.',
)
def command_load(ctx, config, socket_name, socket_path, answer_yes, detached, colors):
def command_load(
ctx,
config,
socket_name,
socket_path,
answer_yes,
detached,
colors,
append
):
"""Load a tmux workspace from each CONFIG.

CONFIG is a specifier for a configuration file.
Expand Down Expand Up @@ -790,6 +805,7 @@ def command_load(ctx, config, socket_name, socket_path, answer_yes, detached, co
'answer_yes': answer_yes,
'colors': colors,
'detached': detached,
'append': append
}

if not config:
Expand Down
Loading