Skip to content

Commit 0dc5b48

Browse files
Upgrade to prompt_toolkit 2.0 + added Windows support.
1 parent 08e0783 commit 0dc5b48

30 files changed

+2095
-3772
lines changed

pymux/arrangement.py

Lines changed: 60 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,10 @@
88
arranged by ordering them in HSplit/VSplit instances.
99
"""
1010
from __future__ import unicode_literals
11-
from .process import Process
1211

12+
from ptterm import Terminal
13+
from prompt_toolkit.application.current import get_app, set_app
1314
from prompt_toolkit.buffer import Buffer
14-
from prompt_toolkit.document import Document
15-
from prompt_toolkit.interface import CommandLineInterface
16-
from prompt_toolkit.search_state import SearchState
17-
from prompt_toolkit.token import Token
1815

1916
import math
2017
import os
@@ -50,10 +47,10 @@ class Pane(object):
5047
"""
5148
_pane_counter = 1000 # Start at 1000, to be sure to not confuse this with pane indexes.
5249

53-
def __init__(self, process):
54-
assert isinstance(process, Process)
50+
def __init__(self, terminal=None):
51+
assert isinstance(terminal, Terminal)
5552

56-
self.process = process
53+
self.terminal = terminal
5754
self.chosen_name = None
5855

5956
# Displayed the clock instead of this pane content.
@@ -73,10 +70,9 @@ def __init__(self, process):
7370
self.display_scroll_buffer = False
7471
self.scroll_buffer_title = ''
7572

76-
# Search buffer, for use in copy mode. (Each pane gets its own search buffer.)
77-
self.search_buffer = Buffer()
78-
self.is_searching = False
79-
self.search_state = SearchState(ignore_case=False)
73+
@property
74+
def process(self):
75+
return self.terminal.process
8076

8177
@property
8278
def name(self):
@@ -99,41 +95,13 @@ def enter_copy_mode(self):
9995
Suspend the process, and copy the screen content to the `scroll_buffer`.
10096
That way the user can search through the history and copy/paste.
10197
"""
102-
document, get_tokens_for_line = self.process.create_copy_document()
103-
self._enter_scroll_buffer('Copy', document, get_tokens_for_line)
104-
105-
def display_text(self, text, title=''):
106-
"""
107-
Display the given text in the scroll buffer.
108-
"""
109-
document = Document(text, 0)
110-
111-
def get_tokens_for_line(lineno):
112-
return [(Token, document.lines[lineno])]
113-
114-
self._enter_scroll_buffer(
115-
title,
116-
document=document,
117-
get_tokens_for_line=get_tokens_for_line)
118-
119-
def _enter_scroll_buffer(self, title, document, get_tokens_for_line):
120-
# Suspend child process.
121-
self.process.suspend()
98+
self.terminal.enter_copy_mode()
12299

123-
self.scroll_buffer.set_document(document, bypass_readonly=True)
124-
self.copy_get_tokens_for_line = get_tokens_for_line
125-
self.display_scroll_buffer = True
126-
self.scroll_buffer_title = title
127-
128-
# Reset search state.
129-
self.search_state = SearchState(ignore_case=False)
130-
131-
def exit_scroll_buffer(self):
100+
def focus(self):
132101
"""
133-
Exit scroll buffer. (Exits help or copy mode.)
102+
Focus this pane.
134103
"""
135-
self.process.resume()
136-
self.display_scroll_buffer = False
104+
get_app().layout.focus(self.terminal)
137105

138106

139107
class _WeightsDictionary(weakref.WeakKeyDictionary):
@@ -210,6 +178,9 @@ def invalidation_hash(self):
210178
Return a hash (string) that can be used to determine when the layout
211179
has to be rebuild.
212180
"""
181+
# if not self.root:
182+
# return '<empty-window>'
183+
213184
def _hash_for_split(split):
214185
result = []
215186
for item in split:
@@ -382,7 +353,10 @@ def active_process(self):
382353
def focus_next(self, count=1):
383354
" Focus the next pane. "
384355
panes = self.panes
385-
self.active_pane = panes[(panes.index(self.active_pane) + count) % len(panes)]
356+
if panes:
357+
self.active_pane = panes[(panes.index(self.active_pane) + count) % len(panes)]
358+
else:
359+
self.active_pane = None # No panes left.
386360

387361
def focus_previous(self):
388362
" Focus the previous pane. "
@@ -590,65 +564,54 @@ def __init__(self):
590564
# is attached.
591565
self._last_active_window = None
592566

593-
def pane_has_priority(self, pane):
594-
"""
595-
Return True when this Pane sohuld get priority in the output processing.
596-
This is true for panes that have the focus in any of the visible windows.
597-
"""
598-
windows = set(self._active_window_for_cli.values())
599-
600-
for w in windows:
601-
if w.active_pane == pane:
602-
return True
603-
604-
return False
605-
606-
def invalidation_hash(self, cli):
567+
def invalidation_hash(self):
607568
"""
608569
When this changes, the layout needs to be rebuild.
609570
"""
610-
w = self.get_active_window(cli)
571+
if not self.windows:
572+
return '<no-windows>'
573+
574+
w = self.get_active_window()
611575
return w.invalidation_hash()
612576

613-
def get_active_window(self, cli):
577+
def get_active_window(self):
614578
"""
615579
The current active :class:`.Window`.
616580
"""
617-
assert isinstance(cli, CommandLineInterface)
581+
app = get_app()
618582

619583
try:
620-
return self._active_window_for_cli[cli]
584+
return self._active_window_for_cli[app]
621585
except KeyError:
622-
self._active_window_for_cli[cli] = self._last_active_window or self.windows[0]
586+
self._active_window_for_cli[app] = self._last_active_window or self.windows[0]
623587
return self.windows[0]
624588

625-
def set_active_window(self, cli, window):
626-
assert isinstance(cli, CommandLineInterface)
589+
def set_active_window(self, window):
627590
assert isinstance(window, Window)
591+
app = get_app()
628592

629-
previous = self.get_active_window(cli)
630-
self._prev_active_window_for_cli[cli] = previous
631-
self._active_window_for_cli[cli] = window
593+
previous = self.get_active_window()
594+
self._prev_active_window_for_cli[app] = previous
595+
self._active_window_for_cli[app] = window
632596
self._last_active_window = window
633597

634-
def set_active_window_from_pane_id(self, cli, pane_id):
598+
def set_active_window_from_pane_id(self, pane_id):
635599
"""
636600
Make the window with this pane ID the active Window.
637601
"""
638-
assert isinstance(cli, CommandLineInterface)
639602
assert isinstance(pane_id, int)
640603

641604
for w in self.windows:
642605
for p in w.panes:
643606
if p.pane_id == pane_id:
644-
self.set_active_window(cli, w)
607+
self.set_active_window(w)
645608

646-
def get_previous_active_window(self, cli):
609+
def get_previous_active_window(self):
647610
" The previous active Window or None if unknown. "
648-
assert isinstance(cli, CommandLineInterface)
611+
app = get_app()
649612

650613
try:
651-
return self._prev_active_window_for_cli[cli]
614+
return self._prev_active_window_for_cli[app]
652615
except KeyError:
653616
return None
654617

@@ -658,17 +621,15 @@ def get_window_by_index(self, index):
658621
if w.index == index:
659622
return w
660623

661-
def create_window(self, cli, pane, name=None, set_active=True):
624+
def create_window(self, pane, name=None, set_active=True):
662625
"""
663626
Create a new window that contains just this pane.
664627
665-
:param cli: If been given, this window will be focussed for that client.
666628
:param pane: The :class:`.Pane` instance to put in the new window.
667629
:param name: If given, name for the new window.
668630
:param set_active: When True, focus the new window.
669631
"""
670632
assert isinstance(pane, Pane)
671-
assert cli is None or isinstance(cli, CommandLineInterface)
672633
assert name is None or isinstance(name, six.text_type)
673634

674635
# Take the first available index.
@@ -686,8 +647,10 @@ def create_window(self, cli, pane, name=None, set_active=True):
686647
# Sort windows by index.
687648
self.windows = sorted(self.windows, key=lambda w: w.index)
688649

689-
if cli is not None and set_active:
690-
self.set_active_window(cli, w)
650+
app = get_app(return_none=True)
651+
652+
if app is not None and set_active:
653+
self.set_active_window(w)
691654

692655
if name is not None:
693656
w.chosen_name = name
@@ -707,13 +670,11 @@ def move_window(self, window, new_index):
707670
# Sort windows by index.
708671
self.windows = sorted(self.windows, key=lambda w: w.index)
709672

710-
def get_active_pane(self, cli):
673+
def get_active_pane(self):
711674
"""
712675
The current :class:`.Pane` from the current window.
713676
"""
714-
assert isinstance(cli, CommandLineInterface)
715-
716-
w = self.get_active_window(cli)
677+
w = self.get_active_window()
717678
if w is not None:
718679
return w.active_pane
719680

@@ -729,49 +690,42 @@ def remove_pane(self, pane):
729690
# No panes left in this window?
730691
if not w.has_panes:
731692
# Focus next.
732-
for cli, active_w in self._active_window_for_cli.items():
693+
for app, active_w in self._active_window_for_cli.items():
733694
if w == active_w:
734-
self.focus_next_window(cli)
695+
with set_app(app):
696+
self.focus_next_window()
735697

736698
self.windows.remove(w)
737699

738-
def focus_previous_window(self, cli):
739-
assert isinstance(cli, CommandLineInterface)
740-
741-
w = self.get_active_window(cli)
700+
def focus_previous_window(self):
701+
w = self.get_active_window()
742702

743-
self.set_active_window(cli, self.windows[
703+
self.set_active_window(self.windows[
744704
(self.windows.index(w) - 1) % len(self.windows)])
745705

746-
def focus_next_window(self, cli):
747-
assert isinstance(cli, CommandLineInterface)
706+
def focus_next_window(self):
707+
w = self.get_active_window()
748708

749-
w = self.get_active_window(cli)
750-
751-
self.set_active_window(cli, self.windows[
709+
self.set_active_window(self.windows[
752710
(self.windows.index(w) + 1) % len(self.windows)])
753711

754-
def break_pane(self, cli, set_active=True):
712+
def break_pane(self, set_active=True):
755713
"""
756714
When the current window has multiple panes, remove the pane from this
757715
window and put it in a new window.
758716
759717
:param set_active: When True, focus the new window.
760718
"""
761-
assert isinstance(cli, CommandLineInterface)
762-
763-
w = self.get_active_window(cli)
719+
w = self.get_active_window()
764720

765721
if len(w.panes) > 1:
766722
pane = w.active_pane
767-
self.get_active_window(cli).remove_pane(pane)
768-
self.create_window(cli, pane, set_active=set_active)
723+
self.get_active_window().remove_pane(pane)
724+
self.create_window(pane, set_active=set_active)
769725

770-
def rotate_window(self, cli, count=1):
726+
def rotate_window(self, count=1):
771727
" Rotate the panes in the active window. "
772-
assert isinstance(cli, CommandLineInterface)
773-
774-
w = self.get_active_window(cli)
728+
w = self.get_active_window()
775729
w.rotate(count=count)
776730

777731
@property

pymux/client/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from __future__ import unicode_literals
2+
from .base import Client
3+
from .defaults import create_client, list_clients

pymux/client/base.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from __future__ import unicode_literals
2+
3+
from prompt_toolkit.output import ColorDepth
4+
from abc import ABCMeta
5+
from six import with_metaclass
6+
7+
8+
__all__ = [
9+
'Client',
10+
]
11+
12+
13+
class Client(with_metaclass(ABCMeta, object)):
14+
def run_command(self, command, pane_id=None):
15+
"""
16+
Ask the server to run this command.
17+
"""
18+
19+
def attach(self, detach_other_clients=False, color_depth=ColorDepth.DEPTH_8_BIT):
20+
"""
21+
Attach client user interface.
22+
"""

pymux/client/defaults.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from __future__ import unicode_literals
2+
from prompt_toolkit.utils import is_windows
3+
__all__ = [
4+
'create_client',
5+
'list_clients',
6+
]
7+
8+
9+
def create_client(socket_name):
10+
if is_windows():
11+
from .windows import WindowsClient
12+
return WindowsClient(socket_name)
13+
else:
14+
from .posix import PosixClient
15+
return PosixClient(socket_name)
16+
17+
18+
def list_clients():
19+
if is_windows():
20+
from .windows import list_clients
21+
return list_clients()
22+
else:
23+
from .posix import list_clients
24+
return list_clients()

0 commit comments

Comments
 (0)