Merge pull request #2 from powerline/develop

Upstream merge
This commit is contained in:
pachi-belero 2022-05-26 19:50:49 +02:00 committed by GitHub
commit 60366d023b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 541 additions and 286 deletions

View File

@ -528,10 +528,10 @@ i3wm
in lemonbar bindings.
``workspace``
dictionary containing the workspace name under the key ``"name"`` and
boolean values for the ``"visible"``, ``"urgent"`` and ``"focused"``
keys, indicating the state of the workspace. Currently only provided by
the :py:func:`powerline.listers.i3wm.workspace_lister` lister.
the `i3-ipc` workspace object corresponding to this workspace.
Contains string attributes ``name`` and ``output``, as well as boolean
attributes for ``visible``, ``urgent`` and ``focused``. Currently only
provided by the :py:func:`powerline.listers.i3wm.workspace_lister` lister.
Segment class
=============

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

@ -77,8 +77,7 @@ to run with i3, simply ``exec`` this in the i3 config file and set the ``--i3``
exec python /path/to/powerline/bindings/lemonbar/powerline-lemonbar.py --i3
Running the binding in i3-mode will require `i3ipc <https://github.com/acrisci/i3ipc-python>`_
(or the outdated `i3-py <https://github.com/ziberna/i3-py>`_).
Running the binding in i3-mode will require `i3ipc <https://github.com/acrisci/i3ipc-python>`_.
See the `lemonbar documentation <https://github.com/LemonBoy/bar>`_ for more
information and options.

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

@ -24,15 +24,6 @@ def i3_subscribe(conn, event, callback):
:param func callback:
Function to run on event.
'''
try:
import i3ipc
except ImportError:
import i3
conn.Subscription(callback, event)
return
else:
pass
conn.on(event, callback)
from threading import Thread
@ -57,12 +48,8 @@ def get_i3_connection():
'''
global conn
if not conn:
try:
import i3ipc
except ImportError:
import i3 as conn
else:
conn = i3ipc.Connection()
import i3ipc
conn = i3ipc.Connection()
return conn

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

@ -163,7 +163,7 @@ class Spec(object):
'''Define message which will be used when unknown key was found
Unknown is a key that was not provided at the initialization and via
:py:meth:`Spec.update` and did not match any ``keyfunc`` proided via
:py:meth:`Spec.update` and did not match any ``keyfunc`` provided via
:py:meth:`Spec.unknown_spec`.
:param msgfunc:

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.
'''
@ -50,19 +50,14 @@ def workspace_lister(pl, segment_info, only_show=None, output=None):
(
updated(
segment_info,
output=w['output'],
workspace={
'name': w['name'],
'visible': w['visible'],
'urgent': w['urgent'],
'focused': w['focused'],
},
output=w.output,
workspace=w,
),
{
'draw_inner_divider': None
}
)
for w in get_i3_connection().get_workspaces()
if (((not only_show or any(w[typ] for typ in only_show))
and (not output or w['output'] == output)))
if (((not only_show or any(getattr(w, typ) for typ in only_show))
and (not output or w.output == output)))
)

View File

@ -251,7 +251,7 @@ class Renderer(object):
for line in range(theme.get_line_number() - 1, 0, -1):
yield self.render(side=None, line=line, **kwargs)
def render(self, mode=None, width=None, side=None, line=0, output_raw=False, output_width=False, segment_info=None, matcher_info=None):
def render(self, mode=None, width=None, side=None, line=0, output_raw=False, output_width=False, segment_info=None, matcher_info=None, hl_args=None):
'''Render all segments.
When a width is provided, low-priority segments are dropped one at
@ -286,6 +286,11 @@ class Renderer(object):
:param matcher_info:
Matcher information. Is processed in :py:meth:`get_segment_info`
method.
:param dict hl_args:
Additional arguments to pass on the :py:meth:`hl` and
:py:meth`hlstyle` methods. They are ignored in the default
implementation, but renderer-specific overrides can make use of
them as run-time "configuration" information.
'''
theme = self.get_theme(matcher_info)
return self.do_render(
@ -297,6 +302,7 @@ class Renderer(object):
output_width=output_width,
segment_info=self.get_segment_info(segment_info, mode),
theme=theme,
hl_args=hl_args
)
def compute_divider_widths(self, theme):
@ -324,7 +330,7 @@ class Renderer(object):
:return: Results of joining these segments.
'''
def do_render(self, mode, width, side, line, output_raw, output_width, segment_info, theme):
def do_render(self, mode, width, side, line, output_raw, output_width, segment_info, theme, hl_args):
'''Like Renderer.render(), but accept theme in place of matcher_info
'''
segments = list(theme.get_segments(side, line, segment_info, mode))
@ -333,14 +339,16 @@ class Renderer(object):
self._prepare_segments(segments, output_width or width)
hl_args = hl_args or dict()
if not width:
# No width specified, so we dont need to crop or pad anything
if output_width:
current_width = self._render_length(theme, segments, self.compute_divider_widths(theme))
return construct_returned_value(self.hl_join([
segment['_rendered_hl']
for segment in self._render_segments(theme, segments)
]) + self.hlstyle(), segments, current_width, output_raw, output_width)
for segment in self._render_segments(theme, segments, hl_args)
]) + self.hlstyle(**hl_args), segments, current_width, output_raw, output_width)
divider_widths = self.compute_divider_widths(theme)
@ -394,10 +402,10 @@ class Renderer(object):
rendered_highlighted = self.hl_join([
segment['_rendered_hl']
for segment in self._render_segments(theme, segments)
for segment in self._render_segments(theme, segments, hl_args)
])
if rendered_highlighted:
rendered_highlighted += self.hlstyle()
rendered_highlighted += self.hlstyle(**hl_args)
return construct_returned_value(rendered_highlighted, segments, current_width, output_raw, output_width)
@ -470,7 +478,7 @@ class Renderer(object):
ret += segment_len
return ret
def _render_segments(self, theme, segments, render_highlighted=True):
def _render_segments(self, theme, segments, hl_args, render_highlighted=True):
'''Internal segment rendering method.
This method loops through the segment array and compares the
@ -527,6 +535,10 @@ class Renderer(object):
contents_highlighted = ''
draw_divider = segment['draw_' + divider_type + '_divider']
segment_hl_args = {}
segment_hl_args.update(segment['highlight'])
segment_hl_args.update(hl_args)
# XXX Make sure self.hl() calls are called in the same order
# segments are displayed. This is needed for Vim renderer to work.
if draw_divider:
@ -546,14 +558,14 @@ class Renderer(object):
if side == 'left':
if render_highlighted:
contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight'])
divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False)
contents_highlighted = self.hl(self.escape(contents_raw), **segment_hl_args)
divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False, **hl_args)
segment['_rendered_raw'] = contents_raw + divider_raw
segment['_rendered_hl'] = contents_highlighted + divider_highlighted
else:
if render_highlighted:
divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False)
contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight'])
divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False, **hl_args)
contents_highlighted = self.hl(self.escape(contents_raw), **segment_hl_args)
segment['_rendered_raw'] = divider_raw + contents_raw
segment['_rendered_hl'] = divider_highlighted + contents_highlighted
else:
@ -562,7 +574,7 @@ class Renderer(object):
else:
contents_raw = contents_raw + outer_padding
contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight'])
contents_highlighted = self.hl(self.escape(contents_raw), **segment_hl_args)
segment['_rendered_raw'] = contents_raw
segment['_rendered_hl'] = contents_highlighted
prev_segment = segment
@ -576,7 +588,7 @@ class Renderer(object):
'''
return string.translate(self.character_translations)
def hlstyle(fg=None, bg=None, attrs=None):
def hlstyle(fg=None, bg=None, attrs=None, **kwargs):
'''Output highlight style string.
Assuming highlighted string looks like ``{style}{contents}`` this method
@ -585,10 +597,10 @@ class Renderer(object):
'''
raise NotImplementedError
def hl(self, contents, fg=None, bg=None, attrs=None):
def hl(self, contents, fg=None, bg=None, attrs=None, **kwargs):
'''Output highlighted chunk.
This implementation just outputs :py:meth:`hlstyle` joined with
``contents``.
'''
return self.hlstyle(fg, bg, attrs) + (contents or '')
return self.hlstyle(fg, bg, attrs, **kwargs) + (contents or '')

View File

@ -17,7 +17,7 @@ class I3barRenderer(Renderer):
# We dont need to explicitly reset attributes, so skip those calls
return ''
def hl(self, contents, fg=None, bg=None, attrs=None):
def hl(self, contents, fg=None, bg=None, attrs=None, **kwargs):
segment = {
'full_text': contents,
'separator': False,

View File

@ -90,7 +90,7 @@ class IPythonPygmentsRenderer(IPythonRenderer):
def hl_join(segments):
return reduce(operator.iadd, segments, [])
def hl(self, contents, fg=None, bg=None, attrs=None):
def hl(self, contents, fg=None, bg=None, attrs=None, **kwargs):
'''Output highlighted chunk.
This implementation outputs a list containing a single pair

View File

@ -21,7 +21,7 @@ class LemonbarRenderer(Renderer):
# We dont need to explicitly reset attributes, so skip those calls
return ''
def hl(self, contents, fg=None, bg=None, attrs=None):
def hl(self, contents, fg=None, bg=None, attrs=None, **kwargs):
text = ''
if fg is not None:

View File

@ -15,7 +15,7 @@ class PangoMarkupRenderer(Renderer):
# We dont need to explicitly reset attributes, so skip those calls
return ''
def hl(self, contents, fg=None, bg=None, attrs=None):
def hl(self, contents, fg=None, bg=None, attrs=None, **kwargs):
'''Highlight a segment.'''
awesome_attr = []
if fg is not None:

View File

@ -105,7 +105,7 @@ class ShellRenderer(PromptRenderer):
self.used_term_escape_style = self.term_escape_style
return super(ShellRenderer, self).do_render(segment_info=segment_info, **kwargs)
def hlstyle(self, fg=None, bg=None, attrs=None):
def hlstyle(self, fg=None, bg=None, attrs=None, escape=True, **kwargs):
'''Highlight a segment.
If an argument is None, the argument is ignored. If an argument is
@ -162,7 +162,7 @@ class ShellRenderer(PromptRenderer):
r = '\033Ptmux;' + r.replace('\033', '\033\033') + '\033\\'
elif self.screen_escape:
r = '\033P' + r.replace('\033', '\033\033') + '\033\\'
return self.escape_hl_start + r + self.escape_hl_end
return self.escape_hl_start + r + self.escape_hl_end if escape else r
def get_theme(self, matcher_info):
if not matcher_info:

View File

@ -6,13 +6,91 @@ from powerline.renderers.shell import ShellRenderer
class BashPromptRenderer(ShellRenderer):
'''Powerline bash prompt segment renderer.'''
escape_hl_start = '\['
escape_hl_end = '\]'
escape_hl_start = '\\['
escape_hl_end = '\\]'
character_translations = ShellRenderer.character_translations.copy()
character_translations[ord('$')] = '\\$'
character_translations[ord('`')] = '\\`'
character_translations[ord('\\')] = '\\\\'
def do_render(self, side, line, width, output_width, output_raw, hl_args, **kwargs):
# we are rendering the normal left prompt
if side == 'left' and line == 0 and width is not None:
# we need left prompt's width to render the raw spacer
output_width = output_width or output_raw
left = super(BashPromptRenderer, self).do_render(
side=side,
line=line,
output_width=output_width,
width=width,
output_raw=output_raw,
hl_args=hl_args,
**kwargs
)
left_rendered = left[0] if output_width else left
# we don't escape color sequences in the right prompt so we can do escaping as a whole
if hl_args:
hl_args = hl_args.copy()
hl_args.update({'escape': False})
else:
hl_args = {'escape': False}
right = super(BashPromptRenderer, self).do_render(
side='right',
line=line,
output_width=True,
width=width,
output_raw=output_raw,
hl_args=hl_args,
**kwargs
)
ret = []
if right[-1] > 0:
# if the right prompt is not empty we embed it in the left prompt
# it must be escaped as a whole so readline doesn't see it
ret.append(''.join((
left_rendered,
self.escape_hl_start,
'\033[s', # save the cursor position
'\033[{0}C'.format(width), # move to the right edge of the terminal
'\033[{0}D'.format(right[-1] - 1), # move back to the right prompt position
right[0],
'\033[u', # restore the cursor position
self.escape_hl_end
)))
if output_raw:
ret.append(''.join((
left[1],
' ' * (width - left[-1] - right[-1]),
right[1]
)))
else:
ret.append(left_rendered)
if output_raw:
ret.append(left[1])
if output_width:
ret.append(left[-1])
if len(ret) == 1:
return ret[0]
else:
return ret
else:
return super(BashPromptRenderer, self).do_render(
side=side,
line=line,
width=width,
output_width=output_width,
output_raw=output_raw,
hl_args=hl_args,
**kwargs
)
renderer = BashPromptRenderer

View File

@ -38,7 +38,7 @@ class TmuxRenderer(Renderer):
width = 10
return super(TmuxRenderer, self).render(width=width, segment_info=segment_info, **kwargs)
def hlstyle(self, fg=None, bg=None, attrs=None):
def hlstyle(self, fg=None, bg=None, attrs=None, **kwargs):
'''Highlight a segment.'''
# We dont need to explicitly reset attributes, so skip those calls
if not attrs and not bg and not fg:

View File

@ -123,7 +123,7 @@ class VimRenderer(Renderer):
def reset_highlight(self):
self.hl_groups.clear()
def hlstyle(self, fg=None, bg=None, attrs=None):
def hlstyle(self, fg=None, bg=None, attrs=None, **kwargs):
'''Highlight a segment.
If an argument is None, the argument is ignored. If an argument is

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']:
if w.focused:
group.append('workspace:focused')
group.append('w_focused')
if w['urgent']:
if w.urgent:
group.append('workspace:urgent')
group.append('w_urgent')
if w['visible']:
if w.visible:
group.append('workspace:visible')
group.append('w_visible')
group.append('workspace')
return group
@ -28,59 +29,192 @@ 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(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()
if w['name'] == workspace
w for w in conn.get_workspaces()
if w.name == workspace
))
except StopIteration:
return None
@ -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()
if w['focused']
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``.
@ -150,6 +288,22 @@ def scratchpad(pl, icons=SCRATCHPAD_ICONS):
'contents': icons.get(w.scratchpad_state, icons['changed']),
'highlight_groups': scratchpad_groups(w)
}
for w in get_i3_connection().get_tree().descendents()
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

@ -4,11 +4,11 @@ from __future__ import (unicode_literals, division, absolute_import, print_funct
import subprocess
from traceback import print_exc
__version__ = "2.8.1"
__version__ = "2.8.3"
def get_version():
try:
return __version__ + '.dev9999+git.' + str(subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip())
return __version__ + 'b' + subprocess.check_output(['git', 'rev-list', '--count', __version__ + '..HEAD']).strip().decode()
except Exception:
print_exc()
return __version__

View File

@ -70,10 +70,10 @@ setup(
'Natural Language :: English',
'Operating System :: Microsoft :: Windows',
'Operating System :: POSIX',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
],

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

@ -8,14 +8,16 @@ from tests.modules import TestCase
class TestI3WM(TestCase):
workspaces = [
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),
]
@staticmethod
def get_workspaces():
return iter([
{'name': '1: w1', 'output': 'LVDS1', 'focused': False, 'urgent': False, 'visible': False},
{'name': '2: w2', 'output': 'LVDS1', 'focused': False, 'urgent': False, 'visible': True},
{'name': '3: w3', 'output': 'HDMI1', 'focused': False, 'urgent': True, 'visible': True},
{'name': '4: w4', 'output': 'DVI01', 'focused': True, 'urgent': True, 'visible': True},
])
return iter(TestI3WM.workspaces)
@staticmethod
def get_outputs(pl):
@ -46,42 +48,22 @@ class TestI3WM(TestCase):
({
'a': 1,
'output': 'LVDS1',
'workspace': {
'name': '1: w1',
'focused': False,
'urgent': False,
'visible': False
}
'workspace': self.workspaces[0],
}, {'draw_inner_divider': None}),
({
'a': 1,
'output': 'LVDS1',
'workspace': {
'name': '2: w2',
'focused': False,
'urgent': False,
'visible': True
}
'workspace': self.workspaces[1],
}, {'draw_inner_divider': None}),
({
'a': 1,
'output': 'HDMI1',
'workspace': {
'name': '3: w3',
'focused': False,
'urgent': True,
'visible': True
}
'workspace': self.workspaces[2],
}, {'draw_inner_divider': None}),
({
'a': 1,
'output': 'DVI01',
'workspace': {
'name': '4: w4',
'focused': True,
'urgent': True,
'visible': True
}
'workspace': self.workspaces[3],
}, {'draw_inner_divider': None}),
]
)
@ -92,22 +74,12 @@ class TestI3WM(TestCase):
({
'a': 1,
'output': 'LVDS1',
'workspace': {
'name': '1: w1',
'focused': False,
'urgent': False,
'visible': False
}
'workspace': self.workspaces[0],
}, {'draw_inner_divider': None}),
({
'a': 1,
'output': 'LVDS1',
'workspace': {
'name': '2: w2',
'focused': False,
'urgent': False,
'visible': True
}
'workspace': self.workspaces[1],
}, {'draw_inner_divider': None}),
]
)
@ -121,22 +93,12 @@ class TestI3WM(TestCase):
({
'a': 1,
'output': 'LVDS1',
'workspace': {
'name': '1: w1',
'focused': False,
'urgent': False,
'visible': False
}
'workspace': self.workspaces[0],
}, {'draw_inner_divider': None}),
({
'a': 1,
'output': 'LVDS1',
'workspace': {
'name': '2: w2',
'focused': False,
'urgent': False,
'visible': True
}
'workspace': self.workspaces[1],
}, {'draw_inner_divider': None}),
]
)
@ -151,42 +113,22 @@ class TestI3WM(TestCase):
({
'a': 1,
'output': 'LVDS1',
'workspace': {
'name': '1: w1',
'focused': False,
'urgent': False,
'visible': False
}
'workspace': self.workspaces[0],
}, {'draw_inner_divider': None}),
({
'a': 1,
'output': 'LVDS1',
'workspace': {
'name': '2: w2',
'focused': False,
'urgent': False,
'visible': True
}
'workspace': self.workspaces[1],
}, {'draw_inner_divider': None}),
({
'a': 1,
'output': 'HDMI1',
'workspace': {
'name': '3: w3',
'focused': False,
'urgent': True,
'visible': True
}
'workspace': self.workspaces[2],
}, {'draw_inner_divider': None}),
({
'a': 1,
'output': 'DVI01',
'workspace': {
'name': '4: w4',
'focused': True,
'urgent': True,
'visible': True
}
'workspace': self.workspaces[3],
}, {'draw_inner_divider': None}),
]
)
@ -201,22 +143,12 @@ class TestI3WM(TestCase):
({
'a': 1,
'output': 'HDMI1',
'workspace': {
'name': '3: w3',
'focused': False,
'urgent': True,
'visible': True
}
'workspace': self.workspaces[2],
}, {'draw_inner_divider': None}),
({
'a': 1,
'output': 'DVI01',
'workspace': {
'name': '4: w4',
'focused': True,
'urgent': True,
'visible': True
}
'workspace': self.workspaces[3],
}, {'draw_inner_divider': None}),
]
)

View File

@ -1001,81 +1001,158 @@ class TestWthr(TestCommon):
class TestI3WM(TestCase):
@staticmethod
def get_workspaces():
return iter([
{'name': '1: w1', 'output': 'LVDS1', 'focused': False, 'urgent': False, 'visible': False},
{'name': '2: w2', 'output': 'LVDS1', 'focused': False, 'urgent': False, 'visible': True},
{'name': '3: w3', 'output': 'HDMI1', 'focused': False, 'urgent': True, 'visible': True},
{'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']},
@ -1093,7 +1170,7 @@ class TestI3WM(TestCase):
def get_tree(self):
return self
def descendents(self):
def descendants(self):
nodes_unfocused = [Args(focused = False)]
nodes_focused = [Args(focused = True)]

View File

@ -4,6 +4,7 @@ set_theme_option() {
set_theme() {
export POWERLINE_CONFIG_OVERRIDES="ext.shell.theme=$1"
}
set_theme_option default.segment_data.hostname.args.only_if_ssh false
set_theme_option default_leftonly.segment_data.hostname.args.only_if_ssh false
ABOVE_LEFT='[{
"left": [
@ -40,7 +41,9 @@ VIRTUAL_ENV=
bgscript.sh & waitpid.sh
false
kill `cat pid` ; sleep 1s
set_theme_option default.segment_data.hostname.display false
set_theme_option default_leftonly.segment_data.hostname.display false
set_theme_option default.segment_data.user.display false
set_theme_option default_leftonly.segment_data.user.display false
echo '
abc
@ -56,14 +59,15 @@ cd ../'$(echo)'
cd ../'`echo`'
cd ../'«Unicode!»'
(exit 42)|(exit 43)
set_theme_option default_leftonly.segments.above "$ABOVE_LEFT"
set_theme default
set_theme_option default.segments.above "$ABOVE_LEFT"
export DISPLAYED_ENV_VAR=foo
unset DISPLAYED_ENV_VAR
set_theme_option default_leftonly.segments.above "$ABOVE_FULL"
set_theme_option default.segments.above "$ABOVE_FULL"
export DISPLAYED_ENV_VAR=foo
unset DISPLAYED_ENV_VAR
set_theme_option default_leftonly.segments.above
set_theme_option default_leftonly.dividers.left.hard \$ABC
set_theme_option default.segments.above
set_theme_option default.dividers.left.hard \$ABC
false
true is the last line
exit

View File

@ -7,7 +7,9 @@
  HOSTNAME  USER   BRANCH  …  tmp  shell  3rd  1  false
  HOSTNAME  USER   BRANCH  …  tmp  shell  3rd  1  1  kill `cat pid` ; sleep 1s
[1]+ Terminated bgscript.sh
  HOSTNAME  USER   BRANCH  …  tmp  shell  3rd  set_theme_option default.segment_data.hostname.display false
  HOSTNAME  USER   BRANCH  …  tmp  shell  3rd  set_theme_option default_leftonly.segment_data.hostname.display false
 USER   BRANCH  …  tmp  shell  3rd  set_theme_option default.segment_data.user.display false
 USER   BRANCH  …  tmp  shell  3rd  set_theme_option default_leftonly.segment_data.user.display false
  BRANCH  …  tmp  shell  3rd  echo '
                                   abc
@ -27,16 +29,17 @@ def
  BRANCH  …  shell  3rd  $(echo)  cd ../'`echo`'
  BRANCH  …  shell  3rd  `echo`  cd ../'«Unicode!»'
  BRANCH  …  shell  3rd  «Unicode!»  (exit 42)|(exit 43)
  BRANCH  …  shell  3rd  «Unicode!»  42  43  set_theme_option default_leftonly.segments.above "$ABOVE_LEFT"
  BRANCH  …  shell  3rd  «Unicode!»  export DISPLAYED_ENV_VAR=foo
  BRANCH  …  shell  3rd  «Unicode!»  42  43  set_theme default
   shell  3rd  «Unicode!»     BRANCH set_theme_option default.segments.above "$ABOVE_LEFT"
   shell  3rd  «Unicode!»     BRANCH export DISPLAYED_ENV_VAR=foo
 foo  
  BRANCH  …  shell  3rd  «Unicode!»  unset DISPLAYED_ENV_VAR
  BRANCH  …  shell  3rd  «Unicode!»  set_theme_option default_leftonly.segments.above "$ABOVE_FULL"
   shell  3rd  «Unicode!»     BRANCH unset DISPLAYED_ENV_VAR
   shell  3rd  «Unicode!»     BRANCH set_theme_option default.segments.above "$ABOVE_FULL"
                                                                                                                                                                                                                                                                                                            
  BRANCH  …  shell  3rd  «Unicode!»  export DISPLAYED_ENV_VAR=foo
   shell  3rd  «Unicode!»     BRANCH export DISPLAYED_ENV_VAR=foo
                                                                                                                                                                                                                                                                                                       foo 
  BRANCH  …  shell  3rd  «Unicode!»  unset DISPLAYED_ENV_VAR
   shell  3rd  «Unicode!»     BRANCH unset DISPLAYED_ENV_VAR
                                                                                                                                                                                                                                                                                                            
  BRANCH  …  shell  3rd  «Unicode!»  set_theme_option default_leftonly.segments.above
  BRANCH  …  shell  3rd  «Unicode!»  set_theme_option default_leftonly.dividers.left.hard \$ABC
  BRANCH $ABC…  shell  3rd  «Unicode!» $ABCfalse
   shell  3rd  «Unicode!»     BRANCH set_theme_option default.segments.above
   shell  3rd  «Unicode!»     BRANCH set_theme_option default.dividers.left.hard \$ABC
 …  shell  3rd  «Unicode!» $ABC   BRANCH false

View File

@ -7,7 +7,9 @@
  HOSTNAME  USER   BRANCH  …  tmp  shell  3rd  1  false
  HOSTNAME  USER   BRANCH  …  tmp  shell  3rd  1  1  kill `cat pid` ; sleep 1s
[1]+ Terminated bgscript.sh
  HOSTNAME  USER   BRANCH  …  tmp  shell  3rd  set_theme_option default.segment_data.hostname.display false
  HOSTNAME  USER   BRANCH  …  tmp  shell  3rd  set_theme_option default_leftonly.segment_data.hostname.display false
 USER   BRANCH  …  tmp  shell  3rd  set_theme_option default.segment_data.user.display false
 USER   BRANCH  …  tmp  shell  3rd  set_theme_option default_leftonly.segment_data.user.display false
  BRANCH  …  tmp  shell  3rd  echo '
   abc
@ -27,16 +29,17 @@ def
  BRANCH  …  shell  3rd  $(echo)  cd ../'`echo`'
  BRANCH  …  shell  3rd  `echo`  cd ../'«Unicode!»'
  BRANCH  …  shell  3rd  «Unicode!»  (exit 42)|(exit 43)
  BRANCH  …  shell  3rd  «Unicode!»  42  43  set_theme_option default_leftonly.segments.above "$ABOVE_LEFT"
  BRANCH  …  shell  3rd  «Unicode!»  export DISPLAYED_ENV_VAR=foo
  BRANCH  …  shell  3rd  «Unicode!»  42  43  set_theme default
   shell  3rd  «Unicode!»     BRANCH set_theme_option default.segments.above "$ABOVE_LEFT"
   shell  3rd  «Unicode!»     BRANCH export DISPLAYED_ENV_VAR=foo
 foo  
  BRANCH  …  shell  3rd  «Unicode!»  unset DISPLAYED_ENV_VAR
  BRANCH  …  shell  3rd  «Unicode!»  set_theme_option default_leftonly.segments.above "$ABOVE_FULL"
   shell  3rd  «Unicode!»     BRANCH unset DISPLAYED_ENV_VAR
   shell  3rd  «Unicode!»     BRANCH set_theme_option default.segments.above "$ABOVE_FULL"
                                                                                                                                                                                                                                                                                                            
  BRANCH  …  shell  3rd  «Unicode!»  export DISPLAYED_ENV_VAR=foo
   shell  3rd  «Unicode!»     BRANCH export DISPLAYED_ENV_VAR=foo
                                                                                                                                                                                                                                                                                                       foo 
  BRANCH  …  shell  3rd  «Unicode!»  unset DISPLAYED_ENV_VAR
   shell  3rd  «Unicode!»     BRANCH unset DISPLAYED_ENV_VAR
                                                                                                                                                                                                                                                                                                            
  BRANCH  …  shell  3rd  «Unicode!»  set_theme_option default_leftonly.segments.above
  BRANCH  …  shell  3rd  «Unicode!»  set_theme_option default_leftonly.dividers.left.hard \$ABC
  BRANCH $ABC…  shell  3rd  «Unicode!» $ABCfalse
   shell  3rd  «Unicode!»     BRANCH set_theme_option default.segments.above
   shell  3rd  «Unicode!»     BRANCH set_theme_option default.dividers.left.hard \$ABC
 …  shell  3rd  «Unicode!» $ABC   BRANCH false