Release 2.8.3

This commit is contained in:
PHP Wellnitz 2022-05-19 17:09:36 +09:00
parent c86faf23cd
commit 8b07f63961
15 changed files with 350 additions and 111 deletions

View File

@ -54,7 +54,7 @@ Generic requirements
Pip installation
================
Due to a naming conflict with an unrelated project powerline is available on
Due to a naming conflict with an unrelated project, powerline is available on
PyPI under the ``powerline-status`` name:
.. code-block:: sh
@ -66,7 +66,7 @@ development version
.. code-block:: sh
pip install --user git+git://github.com/powerline/powerline
pip install --user git+https://github.com/powerline/powerline
may be used. If powerline was already checked out into some directory
@ -85,10 +85,9 @@ will have to be done (:file:`~/.local/bin` should be replaced with some path
present in ``$PATH``).
.. note::
If ISP blocks git protocol for some reason github also provides ``ssh``
(``git+ssh://git@github.com/powerline/powerline``) and ``https``
(``git+https://github.com/powerline/powerline``) protocols. ``git`` protocol
should be the fastest, but least secure one though.
We can use either ``https``(``git+ssh://git@github.com/powerline/powerline``)
or ``https``(``git+https://github.com/powerline/powerline``) protocols.
``git`` protocol is deprecated by Github.
Fonts installation
==================

View File

@ -29,7 +29,7 @@ should be followed:
.. code-block:: sh
pip install --user git+git://github.com/powerline/powerline
pip install --user git+https://github.com/powerline/powerline
will get the latest development version.

View File

@ -16,9 +16,13 @@ Python package
brew install python
.. note::
In case :file:`powerline.sh` as a client ``socat`` and ``coreutils`` need
to be installed. ``coreutils`` may be installed using ``brew install
coreutils``.
There are three variants of the powerline client. The fastest is
written in C and will be compiled if the compiler and libraries are
detected during installation. The second fastest option is
:file:`powerline.sh` which requires ``socat`` and ``coreutils``.
``coreutils`` may be installed using ``brew install
coreutils``. If neither of these are viable, then Powerline will
utilize a fallback client written in Python.
2. Install Powerline using one of the following commands:
@ -30,7 +34,7 @@ Python package
.. code-block:: sh
pip install --user git+git://github.com/powerline/powerline
pip install --user git+https://github.com/powerline/powerline
will get latest development version.

View File

@ -193,8 +193,12 @@ I am suffering bad lags before displaying shell prompt
To get rid of these lags there currently are two options:
* Run ``powerline-daemon``. Powerline does not automatically start it for you.
See installation instructions for more details.
* Compile and install ``libzpython`` module that lives in
https://bitbucket.org/ZyX_I/zpython. This variant is zsh-specific.
* If you are a python package manager, be sure to set ``POWERLINE_COMMAND``
to your Powerline command. See installation instructions for details.
Prompt is spoiled after completing files in ksh
-----------------------------------------------

View File

@ -5,8 +5,9 @@ Shell prompts
*************
.. note::
Powerline daemon is not run automatically by any of my bindings. It is
advised to add
Powerline can operate without a background daemon, but the shell performance
can be very slow. The Powerline daemon improves this performance
significantly, but must be started separately. It is advised to add
.. code-block:: bash
@ -142,3 +143,19 @@ following in ``~/.profile``:
.. warning::
Busybox has two shells: ``ash`` and ``hush``. Second is known to segfault in
busybox 1.22.1 when using :file:`powerline.sh` script.
Python Virtual Environments (conda, pyenv)
==========================================
If your system uses virtual environments to manage different Python versions,
such as pyenv or anaconda, these tools will add a performance delay to every
shell prompt. This delay can be bypassed by explicitly specifying your command
path.
.. code-block:: bash
export POWERLINE_COMMAND={path_to_powerline}
where ``{path_to_powerline}`` is the full filepath for powerline. If you
installed Powerline within an environment, you can find this path for pyenv
with ``pyenv which powerline`` or for conda with ``which powerline``.

View File

@ -1,14 +1,13 @@
# vim:fileencoding=utf-8:noet
from __future__ import (unicode_literals, division, absolute_import, print_function)
from weakref import ref
from atexit import register as atexit
from IPython.terminal.prompts import Prompts
from pygments.token import Token # NOQA
from powerline.ipython import IPythonPowerline
from powerline.renderers.ipython.since_7 import PowerlinePromptStyle
from powerline.bindings.ipython.post_0_11 import PowerlineMagics, ShutdownHook
from powerline.bindings.ipython.post_0_11 import PowerlineMagics
class ConfigurableIPythonPowerline(IPythonPowerline):
@ -20,7 +19,7 @@ class ConfigurableIPythonPowerline(IPythonPowerline):
super(ConfigurableIPythonPowerline, self).init(
renderer_module='.since_7')
def do_setup(self, ip, prompts, shutdown_hook):
def do_setup(self, ip, prompts):
prompts.powerline = self
msfn_missing = ()
@ -50,18 +49,16 @@ class ConfigurableIPythonPowerline(IPythonPowerline):
magics = PowerlineMagics(ip, self)
ip.register_magics(magics)
if shutdown_hook:
shutdown_hook.powerline = ref(self)
atexit(self.shutdown)
class PowerlinePrompts(Prompts):
'''Class that returns powerline prompts
'''
def __init__(self, shell):
shutdown_hook = ShutdownHook(shell)
powerline = ConfigurableIPythonPowerline(shell)
self.shell = shell
powerline.do_setup(shell, self, shutdown_hook)
powerline.do_setup(shell, self)
self.last_output_count = None
self.last_output = {}

View File

@ -197,7 +197,7 @@ _powerline_add_widget() {
}
if test -z "${POWERLINE_CONFIG_COMMAND}" ; then
if which powerline-config >/dev/null ; then
if which powerline-config >/dev/null 2>/dev/null ; then
typeset -g POWERLINE_CONFIG_COMMAND=powerline-config
else
typeset -g POWERLINE_CONFIG_COMMAND="${_POWERLINE_SOURCED:h:h:h:h}/scripts/powerline-config"

View File

@ -51,6 +51,7 @@
"cwd:current_folder": "information:regular",
"cwd:divider": { "fg": "gray7", "bg": "gray4", "attrs": [] },
"virtualenv": { "fg": "white", "bg": "darkcyan", "attrs": [] },
"attached_clients": { "fg": "gray8", "bg": "gray0", "attrs": [] }
"attached_clients": { "fg": "gray8", "bg": "gray0", "attrs": [] },
"workspace": "information:regular"
}
}

View File

@ -1,10 +1,6 @@
{
"segments": {
"right": [
{
"function": "powerline.segments.common.wthr.weather",
"priority": 50
},
{
"function": "powerline.segments.common.time.date"
},
@ -15,14 +11,6 @@
"format": "%H:%M",
"istime": true
}
},
{
"function": "powerline.segments.common.mail.email_imap_alert",
"priority": 10,
"args": {
"username": "",
"password": ""
}
}
]
}

View File

@ -122,7 +122,7 @@ class BaseConstructor:
mapping = {}
for key_node, value_node in node.value:
key = self.construct_object(key_node, deep=deep)
if not isinstance(key, collections.Hashable):
if not isinstance(key, collections.abc.Hashable):
self.echoerr(
'While constructing a mapping', node.start_mark,
'found unhashable key', key_node.start_mark

View File

@ -26,20 +26,20 @@ def output_lister(pl, segment_info):
def workspace_lister(pl, segment_info, only_show=None, output=None):
'''List all workspaces in segment_info format
Sets the segment info values of ``workspace`` and ``output`` to the name of
Sets the segment info values of ``workspace`` and ``output`` to the name of
the i3 workspace and the ``xrandr`` output respectively and the keys
``"visible"``, ``"urgent"`` and ``"focused"`` to a boolean indicating these
states.
:param list only_show:
Specifies which workspaces to list. Valid entries are ``"visible"``,
``"urgent"`` and ``"focused"``. If omitted or ``null`` all workspaces
Specifies which workspaces to list. Valid entries are ``"visible"``,
``"urgent"`` and ``"focused"``. If omitted or ``null`` all workspaces
are listed.
:param str output:
May be set to the name of an X output. If specified, only workspaces
on that output are listed. Overrides automatic output detection by
the lemonbar renderer and bindings. Set to ``false`` to force
May be set to the name of an X output. If specified, only workspaces
on that output are listed. Overrides automatic output detection by
the lemonbar renderer and bindings. Set to ``false`` to force
all workspaces to be shown.
'''

View File

@ -6,17 +6,18 @@ import re
from powerline.theme import requires_segment_info
from powerline.bindings.wm import get_i3_connection
WORKSPACE_REGEX = re.compile(r'^[0-9]+: ?')
def workspace_groups(w):
group = []
if w.focused:
group.append('workspace:focused')
group.append('w_focused')
if w.urgent:
group.append('workspace:urgent')
group.append('w_urgent')
if w.visible:
group.append('workspace:visible')
group.append('w_visible')
group.append('workspace')
return group
@ -28,58 +29,191 @@ def format_name(name, strip=False):
return name
def is_empty_workspace(workspace, containers):
if workspace.focused or workspace.visible:
return False
wins = [win for win in containers[workspace.name].leaves()]
return False if len(wins) > 0 else True
WS_ICONS = {"multiple": "M"}
def get_icon(workspace, separator, icons, show_multiple_icons, ws_containers):
icons_tmp = WS_ICONS
icons_tmp.update(icons)
icons = icons_tmp
wins = [win for win in ws_containers[workspace.name].leaves() \
if win.parent.scratchpad_state == 'none']
if len(wins) == 0:
return ''
result = ''
cnt = 0
for key in icons:
if not icons[key] or len(icons[key]) < 1:
continue
if any(key in win.window_class for win in wins if win.window_class):
result += (separator if cnt > 0 else '') + icons[key]
cnt += 1
if not show_multiple_icons and cnt > 1:
if 'multiple' in icons:
return icons['multiple']
else:
return ''
return result
@requires_segment_info
def workspaces(pl, segment_info, only_show=None, output=None, strip=0):
def workspaces(pl, segment_info, only_show=None, output=None, strip=0, format='{name}',
icons=WS_ICONS, sort_workspaces=False, show_output=False, priority_workspaces=[],
hide_empty_workspaces=False):
'''Return list of used workspaces
:param list only_show:
Specifies which workspaces to show. Valid entries are ``"visible"``,
``"urgent"`` and ``"focused"``. If omitted or ``null`` all workspaces
Specifies which workspaces to show. Valid entries are ``"visible"``,
``"urgent"`` and ``"focused"``. If omitted or ``null`` all workspaces
are shown.
:param str output:
May be set to the name of an X output. If specified, only workspaces
on that output are shown. Overrides automatic output detection by
May be set to the name of an X output. If specified, only workspaces
on that output are shown. Overrides automatic output detection by
the lemonbar renderer and bindings.
Use "__all__" to show workspaces on all outputs.
:param int strip:
Specifies how many characters from the front of each workspace name
Specifies how many characters from the front of each workspace name
should be stripped (e.g. to remove workspace numbers). Defaults to zero.
:param str format:
Specifies the format used to display workspaces; defaults to ``{name}``.
Valid fields are: ``name`` (workspace name), ``number`` (workspace number
if present), `stipped_name`` (workspace name stripped of leading number),
``icon`` (if available, icon for application running in the workspace,
uses the ``multiple`` icon instead of multiple different icons), ``multi_icon``
(similar to ``icon``, but does not use ``multiple``, instead joins all icons
with a single space)
:param dict icons:
A dictionary mapping a substring of window classes to strings to be used as an
icon for that window class.
Further, there is a ``multiple`` icon for workspaces containing more than one
window.
:param bool sort_workspaces:
Sort the workspaces displayed by their name according to the natural ordering.
:param bool show_output:
Shows the name of the output if more than one output is connected.
:param list priority_workspaces:
A list of workspace names to be sorted before any other workspaces in the given
order.
:param bool hide_empty_workspaces:
Hides all workspaces without any open window.
Also hides non-focussed workspaces containing only an open scratchpad.
Highlight groups used: ``workspace`` or ``w_visible``, ``workspace`` or ``w_focused``, ``workspace`` or ``w_urgent``.
Highlight groups used: ``workspace`` or ``w_visible``, ``workspace:visible``, ``workspace`` or ``w_focused``, ``workspace:focused``, ``workspace`` or ``w_urgent``, ``workspace:urgent``, ``workspace`` or ``output``.
'''
output = output or segment_info.get('output')
conn = get_i3_connection()
return [
{
'contents': w.name[strip:],
if not output == "__all__":
output = output or segment_info.get('output')
else:
output = None
if output:
output = [output]
else:
output = [o.name for o in conn.get_outputs() if o.active]
def sort_ws(ws):
if sort_workspaces:
def natural_key(ws):
str = ws.name
return [int(s) if s.isdigit() else s for s in re.split(r'(\d+)', str)]
ws = sorted(ws, key=natural_key)
result = []
for n in priority_workspaces:
result += [w for w in ws if w.name == n]
return result + [w for w in ws if not w.name in priority_workspaces]
ws_containers = {w_con.name : w_con for w_con in conn.get_tree().workspaces()}
if len(output) <= 1:
res = []
if show_output:
res += [{
'contents': output[0],
'highlight_groups': ['output']
}]
res += [{
'contents': format.format(name = w.name[min(len(w.name), strip):],
stripped_name = format_name(w.name, strip=True),
number = w.num,
icon = get_icon(w, '', icons, False, ws_containers),
multi_icon = get_icon(w, ' ', icons, True, ws_containers)),
'highlight_groups': workspace_groups(w)
}
for w in get_i3_connection().get_workspaces()
if ((not only_show or any(getattr(w, typ) for typ in only_show))
and (not output or w.output == output))
]
} for w in sort_ws(conn.get_workspaces()) \
if (not only_show or any(getattr(w, tp) for tp in only_show)) \
if w.output == output[0] \
if not (hide_empty_workspaces and is_empty_workspace(w, ws_containers))]
return res
else:
res = []
for n in output:
if show_output:
res += [{
'contents': n,
'highlight_groups': ['output']
}]
res += [{
'contents': format.format(name = w.name[min(len(w.name), strip):],
stripped_name = format_name(w.name, strip=True),
number = w.num,
icon = get_icon(w, '', icons, False, ws_containers),
multi_icon = get_icon(w, ' ', icons, True, ws_containers)),
'highlight_groups': workspace_groups(w)
} for w in sort_ws(conn.get_workspaces()) \
if (not only_show or any(getattr(w, tp) for tp in only_show)) \
if w.output == n \
if not (hide_empty_workspaces and is_empty_workspace(w, ws_containers))]
return res
@requires_segment_info
def workspace(pl, segment_info, workspace=None, strip=False):
def workspace(pl, segment_info, workspace=None, strip=False, format=None, icons=WS_ICONS):
'''Return the specified workspace name
:param str workspace:
Specifies which workspace to show. If unspecified, may be set by the
``list_workspaces`` lister if used, otherwise falls back to
Specifies which workspace to show. If unspecified, may be set by the
``list_workspaces`` lister if used, otherwise falls back to
currently focused workspace.
:param bool strip:
Specifies whether workspace numbers (in the ``1: name`` format) should
Specifies whether workspace numbers (in the ``1: name`` format) should
be stripped from workspace names before being displayed. Defaults to false.
Deprecated: Use {name} or {stripped_name} of format instead.
Highlight groups used: ``workspace`` or ``w_visible``, ``workspace`` or ``w_focused``, ``workspace`` or ``w_urgent``.
:param str format:
Specifies the format used to display workspaces; defaults to ``{name}``.
Valid fields are: ``name`` (workspace name), ``number`` (workspace number
if present), `stipped_name`` (workspace name stripped of leading number),
``icon`` (if available, icon for application running in the workspace,
uses the ``multiple`` icon instead of multiple different icons), ``multi_icon``
(similar to ``icon``, but does not use ``multiple``, instead joins all icons
with a single space)
:param dict icons:
A dictionary mapping a substring of window classes to strings to be used as an
icon for that window class.
Further, there is a ``multiple`` icon for workspaces containing more than one
window.
Highlight groups used: ``workspace`` or ``w_visible``, ``workspace:visible``, ``workspace`` or ``w_focused``, ``workspace:focused``, ``workspace`` or ``w_urgent``, ``workspace:urgent``, ``workspace``.
'''
if format == None:
format = '{stripped_name}' if strip else '{name}'
conn = get_i3_connection()
ws_containers = {w_con.name : w_con for w_con in conn.get_tree().workspaces()}
if workspace:
try:
w = next((
w for w in get_i3_connection().get_workspaces()
w for w in conn.get_workspaces()
if w.name == workspace
))
except StopIteration:
@ -89,16 +223,20 @@ def workspace(pl, segment_info, workspace=None, strip=False):
else:
try:
w = next((
w for w in get_i3_connection().get_workspaces()
w for w in conn.get_workspaces()
if w.focused
))
except StopIteration:
return None
return [{
'contents': format_name(w.name, strip=strip),
'contents': format.format(name = w.name,
stripped_name = format_name(w.name, strip=True),
number = w.num,
icon = get_icon(w, '', icons, False, ws_containers),
multi_icon = get_icon(w, ' ', icons, True, ws_containers)),
'highlight_groups': workspace_groups(w)
}]
}]
@requires_segment_info
@ -139,7 +277,7 @@ def scratchpad(pl, icons=SCRATCHPAD_ICONS):
'''Returns the windows currently on the scratchpad
:param dict icons:
Specifies the strings to show for the different scratchpad window states. Must
Specifies the strings to show for the different scratchpad window states. Must
contain the keys ``fresh`` and ``changed``.
Highlight groups used: ``scratchpad`` or ``scratchpad:visible``, ``scratchpad`` or ``scratchpad:focused``, ``scratchpad`` or ``scratchpad:urgent``.
@ -153,3 +291,19 @@ def scratchpad(pl, icons=SCRATCHPAD_ICONS):
for w in get_i3_connection().get_tree().descendants()
if w.scratchpad_state != 'none'
]
def active_window(pl, cutoff=100):
'''Returns the title of the currently active window.
:param int cutoff:
Maximum title length. If the title is longer, the window_class is used instead.
Highlight groups used: ``active_window_title``.
'''
focused = get_i3_connection().get_tree().find_focused()
cont = focused.name
if len(cont) > cutoff:
cont = focused.window_class
return [{'contents': cont, 'highlight_groups': ['active_window_title']}] if focused.name != focused.workspace().name else []

View File

@ -1,10 +1,7 @@
# vim:fileencoding=utf-8:noet
from __future__ import (unicode_literals, division, absolute_import, print_function)
import subprocess
from traceback import print_exc
__version__ = "2.8.2"
__version__ = "2.8.3"
def get_version():
return __version__

View File

@ -33,6 +33,7 @@ class PowerlineDummyTest(object):
class PowerlineTestSuite(object):
def __init__(self, name):
self.name = name
self.suite = ''
def __enter__(self):
self.saved_current_suite = os.environ['POWERLINE_CURRENT_SUITE']

View File

@ -1001,81 +1001,158 @@ class TestWthr(TestCommon):
class TestI3WM(TestCase):
@staticmethod
def get_workspaces():
return iter([
Args(name='1: w1', output='LVDS1', focused = False, urgent = False, visible = False),
Args(name='2: w2', output='LVDS1', focused = False, urgent = False, visible = True),
Args(name='3: w3', output='HDMI1', focused = False, urgent = True, visible = True),
Args(name='4: w4', output='DVI01', focused = True, urgent = True, visible = True),
])
def test_workspaces(self):
class Conn(object):
def get_tree(self):
return self
def descendents(self):
nodes_unfocused = [Args(focused = False)]
nodes_focused = [Args(focused = True)]
workspace_scratch = lambda: Args(name='__i3_scratch')
workspace_noscratch = lambda: Args(name='2: w2')
return [
Args(scratchpad_state='fresh', urgent=False, workspace=workspace_scratch, nodes=nodes_unfocused),
Args(scratchpad_state='changed', urgent=True, workspace=workspace_noscratch, nodes=nodes_focused),
Args(scratchpad_state='fresh', urgent=False, workspace=workspace_scratch, nodes=nodes_unfocused),
Args(scratchpad_state=None, urgent=False, workspace=workspace_noscratch, nodes=nodes_unfocused),
Args(scratchpad_state='fresh', urgent=False, workspace=workspace_scratch, nodes=nodes_focused),
Args(scratchpad_state=None, urgent=True, workspace=workspace_noscratch, nodes=nodes_unfocused),
]
def workspaces(self):
return iter([
Args(name='1: w1', output='LVDS1', focused=False, urgent=False, visible=False, num=1, leaves=lambda: []),
Args(name='2: w2', output='LVDS1', focused=False, urgent=False, visible=True, num=2, leaves=lambda: []),
Args(name='3: w3', output='HDMI1', focused=False, urgent=True, visible=True, num=3, leaves=lambda: []),
Args(name='4: w4', output='DVI01', focused=True, urgent=True, visible=True, num=None, leaves=lambda: [])
])
def get_workspaces(self):
return iter([
Args(name='1: w1', output='LVDS1', focused=False, urgent=False, visible=False, num=1, leaves=lambda: []),
Args(name='2: w2', output='LVDS1', focused=False, urgent=False, visible=True, num=2, leaves=lambda: []),
Args(name='3: w3', output='HDMI1', focused=False, urgent=True, visible=True, num=3, leaves=lambda: []),
Args(name='4: w4', output='DVI01', focused=True, urgent=True, visible=True, num=None, leaves=lambda: [])
])
def get_outputs(self):
return iter([
Args(name='LVDS1', active=True),
Args(name='HDMI1', active=True),
Args(name='DVI01', active=True),
Args(name='HDMI2', active=False),
])
pl = Pl()
with replace_attr(i3wm, 'get_i3_connection', lambda: Args(get_workspaces=self.get_workspaces)):
with replace_attr(i3wm, 'get_i3_connection', lambda: Conn()):
segment_info = {}
self.assertEqual(i3wm.workspaces(pl=pl, segment_info=segment_info), [
{'contents': '1: w1', 'highlight_groups': ['workspace']},
{'contents': '2: w2', 'highlight_groups': ['w_visible', 'workspace']},
{'contents': '3: w3', 'highlight_groups': ['w_urgent', 'w_visible', 'workspace']},
{'contents': '4: w4', 'highlight_groups': ['w_focused', 'w_urgent', 'w_visible', 'workspace']},
{'contents': '2: w2', 'highlight_groups': ['workspace:visible', 'w_visible', 'workspace']},
{'contents': '3: w3', 'highlight_groups': ['workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']},
{'contents': '4: w4', 'highlight_groups': ['workspace:focused', 'w_focused', 'workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']},
])
self.assertEqual(i3wm.workspaces(pl=pl, segment_info=segment_info, only_show=None), [
{'contents': '1: w1', 'highlight_groups': ['workspace']},
{'contents': '2: w2', 'highlight_groups': ['w_visible', 'workspace']},
{'contents': '3: w3', 'highlight_groups': ['w_urgent', 'w_visible', 'workspace']},
{'contents': '4: w4', 'highlight_groups': ['w_focused', 'w_urgent', 'w_visible', 'workspace']},
{'contents': '2: w2', 'highlight_groups': ['workspace:visible', 'w_visible', 'workspace']},
{'contents': '3: w3', 'highlight_groups': ['workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']},
{'contents': '4: w4', 'highlight_groups': ['workspace:focused', 'w_focused', 'workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']},
])
self.assertEqual(i3wm.workspaces(pl=pl, segment_info=segment_info, only_show=['focused', 'urgent']), [
{'contents': '3: w3', 'highlight_groups': ['w_urgent', 'w_visible', 'workspace']},
{'contents': '4: w4', 'highlight_groups': ['w_focused', 'w_urgent', 'w_visible', 'workspace']},
{'contents': '3: w3', 'highlight_groups': ['workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']},
{'contents': '4: w4', 'highlight_groups': ['workspace:focused', 'w_focused', 'workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']},
])
self.assertEqual(i3wm.workspaces(pl=pl, segment_info=segment_info, only_show=['visible']), [
{'contents': '2: w2', 'highlight_groups': ['w_visible', 'workspace']},
{'contents': '3: w3', 'highlight_groups': ['w_urgent', 'w_visible', 'workspace']},
{'contents': '4: w4', 'highlight_groups': ['w_focused', 'w_urgent', 'w_visible', 'workspace']},
{'contents': '2: w2', 'highlight_groups': ['workspace:visible', 'w_visible', 'workspace']},
{'contents': '3: w3', 'highlight_groups': ['workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']},
{'contents': '4: w4', 'highlight_groups': ['workspace:focused', 'w_focused', 'workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']},
])
self.assertEqual(i3wm.workspaces(pl=pl, segment_info=segment_info, only_show=['visible'], strip=3), [
{'contents': 'w2', 'highlight_groups': ['w_visible', 'workspace']},
{'contents': 'w3', 'highlight_groups': ['w_urgent', 'w_visible', 'workspace']},
{'contents': 'w4', 'highlight_groups': ['w_focused', 'w_urgent', 'w_visible', 'workspace']},
{'contents': 'w2', 'highlight_groups': ['workspace:visible', 'w_visible', 'workspace']},
{'contents': 'w3', 'highlight_groups': ['workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']},
{'contents': 'w4', 'highlight_groups': ['workspace:focused', 'w_focused', 'workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']},
])
self.assertEqual(i3wm.workspaces(pl=pl, segment_info=segment_info, only_show=['focused', 'urgent'], output='DVI01'), [
{'contents': '4: w4', 'highlight_groups': ['w_focused', 'w_urgent', 'w_visible', 'workspace']},
{'contents': '4: w4', 'highlight_groups': ['workspace:focused', 'w_focused', 'workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']},
])
self.assertEqual(i3wm.workspaces(pl=pl, segment_info=segment_info, only_show=['visible'], output='HDMI1'), [
{'contents': '3: w3', 'highlight_groups': ['w_urgent', 'w_visible', 'workspace']},
{'contents': '3: w3', 'highlight_groups': ['workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']},
])
self.assertEqual(i3wm.workspaces(pl=pl, segment_info=segment_info, only_show=['visible'], strip=3, output='LVDS1'), [
{'contents': 'w2', 'highlight_groups': ['w_visible', 'workspace']},
{'contents': 'w2', 'highlight_groups': ['workspace:visible', 'w_visible', 'workspace']},
])
segment_info['output'] = 'LVDS1'
self.assertEqual(i3wm.workspaces(pl=pl, segment_info=segment_info, only_show=['visible'], output='HDMI1'), [
{'contents': '3: w3', 'highlight_groups': ['w_urgent', 'w_visible', 'workspace']},
{'contents': '3: w3', 'highlight_groups': ['workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']},
])
self.assertEqual(i3wm.workspaces(pl=pl, segment_info=segment_info, only_show=['visible'], strip=3), [
{'contents': 'w2', 'highlight_groups': ['w_visible', 'workspace']},
{'contents': 'w2', 'highlight_groups': ['workspace:visible', 'w_visible', 'workspace']},
])
def test_workspace(self):
class Conn(object):
def get_tree(self):
return self
def descendents(self):
nodes_unfocused = [Args(focused = False)]
nodes_focused = [Args(focused = True)]
workspace_scratch = lambda: Args(name='__i3_scratch')
workspace_noscratch = lambda: Args(name='2: w2')
return [
Args(scratchpad_state='fresh', urgent=False, workspace=workspace_scratch, nodes=nodes_unfocused),
Args(scratchpad_state='changed', urgent=True, workspace=workspace_noscratch, nodes=nodes_focused),
Args(scratchpad_state='fresh', urgent=False, workspace=workspace_scratch, nodes=nodes_unfocused),
Args(scratchpad_state=None, urgent=False, workspace=workspace_noscratch, nodes=nodes_unfocused),
Args(scratchpad_state='fresh', urgent=False, workspace=workspace_scratch, nodes=nodes_focused),
Args(scratchpad_state=None, urgent=True, workspace=workspace_noscratch, nodes=nodes_unfocused),
]
def workspaces(self):
return iter([
Args(name='1: w1', output='LVDS1', focused=False, urgent=False, visible=False, num=1, leaves=lambda: []),
Args(name='2: w2', output='LVDS1', focused=False, urgent=False, visible=True, num=2, leaves=lambda: []),
Args(name='3: w3', output='HDMI1', focused=False, urgent=True, visible=True, num=3, leaves=lambda: []),
Args(name='4: w4', output='DVI01', focused=True, urgent=True, visible=True, num=None, leaves=lambda: [])
])
def get_workspaces(self):
return iter([
Args(name='1: w1', output='LVDS1', focused=False, urgent=False, visible=False, num=1, leaves=lambda: []),
Args(name='2: w2', output='LVDS1', focused=False, urgent=False, visible=True, num=2, leaves=lambda: []),
Args(name='3: w3', output='HDMI1', focused=False, urgent=True, visible=True, num=3, leaves=lambda: []),
Args(name='4: w4', output='DVI01', focused=True, urgent=True, visible=True, num=None, leaves=lambda: [])
])
def get_outputs(self):
return iter([
Args(name='LVDS1', active=True),
Args(name='HDMI1', active=True),
Args(name='DVI01', active=True),
Args(name='HDMI2', active=False),
])
pl = Pl()
with replace_attr(i3wm, 'get_i3_connection', lambda: Args(get_workspaces=self.get_workspaces)):
with replace_attr(i3wm, 'get_i3_connection', lambda: Conn()):
segment_info = {}
self.assertEqual(i3wm.workspace(pl=pl, segment_info=segment_info, workspace='1: w1'), [
{'contents': '1: w1', 'highlight_groups': ['workspace']},
])
self.assertEqual(i3wm.workspace(pl=pl, segment_info=segment_info, workspace='3: w3', strip=True), [
{'contents': 'w3', 'highlight_groups': ['w_urgent', 'w_visible', 'workspace']},
{'contents': 'w3', 'highlight_groups': ['workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']},
])
self.assertEqual(i3wm.workspace(pl=pl, segment_info=segment_info, workspace='9: w9'), None)
self.assertEqual(i3wm.workspace(pl=pl, segment_info=segment_info), [
{'contents': '4: w4', 'highlight_groups': ['w_focused', 'w_urgent', 'w_visible', 'workspace']},
{'contents': '4: w4', 'highlight_groups': ['workspace:focused', 'w_focused', 'workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']},
])
segment_info['workspace'] = next(self.get_workspaces())
segment_info['workspace'] = next(Conn().get_workspaces())
self.assertEqual(i3wm.workspace(pl=pl, segment_info=segment_info, workspace='4: w4'), [
{'contents': '4: w4', 'highlight_groups': ['w_focused', 'w_urgent', 'w_visible', 'workspace']},
{'contents': '4: w4', 'highlight_groups': ['workspace:focused', 'w_focused', 'workspace:urgent', 'w_urgent', 'workspace:visible', 'w_visible', 'workspace']},
])
self.assertEqual(i3wm.workspace(pl=pl, segment_info=segment_info, strip=True), [
{'contents': 'w1', 'highlight_groups': ['workspace']},