Skip to content

Add the ability to append windows to a session #656

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions docs/cli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,23 @@ directory may be loaded with:

$ tmuxp load .

If you try to load a config file from within a tmux session, it will ask you
if you want to load and attach to the new session, or just load detached.
You can also load a config file and append the windows to 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 of these options can be preselected to skip the prompt:

.. code-block:: bash
$ tmuxp load -y config # load attached
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In tmux lore, typically -a is reserved for attaching.

Is there any other posix / unix-like apps that have another arg they use to mean append? (Not a requirement of course)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm looking through this PR as a whole, and -a / (a)ppend seems to make the most sense and "fit" the best to me. Nice

$ tmuxp load -d config # load detached
$ tmuxp load -a config # append windows

Multiple sessions can be loaded at once. The first ones will be created
without being attached. The last one will be attached if there is no
``-d`` flag on the command line.
Expand Down
3 changes: 2 additions & 1 deletion docs/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 a configuration, and append the windows to
the current active session.

You can also have a custom tmuxp config directory by setting the
``TMUX_CONFIGDIR`` in your environment variables.
Expand Down
14 changes: 14 additions & 0 deletions tests/fixtures/workspacebuilder/three_windows.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
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'
108 changes: 108 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
import json
import os

try:
from unittest.mock import MagicMock
except ImportError:
from mock import MagicMock

import pytest

import click
Expand All @@ -18,6 +23,8 @@
from tmuxp import cli, config, exc
from tmuxp.cli import (
_reattach,
_load_attached,
_load_append_windows_to_current_session,
command_debug_info,
command_ls,
get_config_dir,
Expand Down Expand Up @@ -1088,6 +1095,107 @@ def test_reattach_plugins(server):
assert proc.stdout[0] == "'plugin_test_r'"


def test_load_attached(server, monkeypatch):
# Load a session and attach from outside tmux
monkeypatch.delenv('TMUX', raising=False)

attach_session_mock = MagicMock()
attach_session_mock.return_value.stderr = None

monkeypatch.setattr("libtmux.session.Session.attach_session", attach_session_mock)

yaml_config = loadfixture("workspacebuilder/two_pane.yaml")
sconfig = kaptan.Kaptan(handler='yaml')
sconfig = sconfig.import_config(yaml_config).get()

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

_load_attached(builder, False)

assert builder.session.attach_session.call_count == 1


def test_load_attached_detached(server, monkeypatch):
# Load a session but don't attach
monkeypatch.delenv('TMUX', raising=False)

attach_session_mock = MagicMock()
attach_session_mock.return_value.stderr = None

monkeypatch.setattr("libtmux.session.Session.attach_session", attach_session_mock)

yaml_config = loadfixture("workspacebuilder/two_pane.yaml")
sconfig = kaptan.Kaptan(handler='yaml')
sconfig = sconfig.import_config(yaml_config).get()

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

_load_attached(builder, True)

assert builder.session.attach_session.call_count == 0


def test_load_attached_within_tmux(server, monkeypatch):
# Load a session and attach from within tmux
monkeypatch.setenv('TMUX', "/tmp/tmux-1234/default,123,0")

switch_client_mock = MagicMock()
switch_client_mock.return_value.stderr = None

monkeypatch.setattr("libtmux.session.Session.switch_client", switch_client_mock)

yaml_config = loadfixture("workspacebuilder/two_pane.yaml")
sconfig = kaptan.Kaptan(handler='yaml')
sconfig = sconfig.import_config(yaml_config).get()

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

_load_attached(builder, False)

assert builder.session.switch_client.call_count == 1


def test_load_attached_within_tmux_detached(server, monkeypatch):
# Load a session and attach from within tmux
monkeypatch.setenv('TMUX', "/tmp/tmux-1234/default,123,0")

switch_client_mock = MagicMock()
switch_client_mock.return_value.stderr = None

monkeypatch.setattr("libtmux.session.Session.switch_client", switch_client_mock)

yaml_config = loadfixture("workspacebuilder/two_pane.yaml")
sconfig = kaptan.Kaptan(handler='yaml')
sconfig = sconfig.import_config(yaml_config).get()

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

_load_attached(builder, True)

assert builder.session.switch_client.call_count == 1

def test_load_append_windows_to_current_session(server, monkeypatch):
yaml_config = loadfixture("workspacebuilder/two_pane.yaml")
sconfig = kaptan.Kaptan(handler='yaml')
sconfig = sconfig.import_config(yaml_config).get()

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

assert len(server.list_sessions()) == 1
assert len(server._list_windows()) == 3

# Assign an active pane to the session
monkeypatch.setenv("TMUX_PANE", server._list_panes()[0]["pane_id"])

builder = WorkspaceBuilder(sconf=sconfig, server=server)
_load_append_windows_to_current_session(builder)

assert len(server.list_sessions()) == 1
assert len(server._list_windows()) == 6



def test_debug_info_cli(monkeypatch, tmpdir):
monkeypatch.setenv('SHELL', '/bin/bash')

Expand Down
87 changes: 87 additions & 0 deletions tests/test_workspacebuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -779,3 +779,90 @@ def test_plugin_system_multiple_plugins(session):
# override methods are currently written
proc = session.cmd('display-message', '-p', "'#W'")
assert proc.stdout[0] == "'mp_test_awf'"


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()
assert len(server.sessions) == 2
assert len(server.sessions[1]._windows) == 2

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[1], True)

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


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


def test_find_current_active_pane(server, monkeypatch):
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()

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.list_sessions()) == 2

# Assign an active pane to the session
second_session = server.list_sessions()[1]
first_pane_on_second_session_id = (
second_session.list_windows()[0].list_panes()[0]["pane_id"]
)
monkeypatch.setenv("TMUX_PANE", first_pane_on_second_session_id)

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

assert builder.find_current_attached_session() == second_session
Loading