Merge pull request #993 from ZyX-I/listers

Some lister improvements

Fixes #989
Closes #972
Ref #614
This commit is contained in:
Nikolai Aleksandrovich Pavlov 2014-08-16 14:44:44 +04:00
commit 1ea3b96f4c
21 changed files with 354 additions and 132 deletions

View File

@ -133,4 +133,5 @@ References
configuration/reference configuration/reference
configuration/segments configuration/segments
configuration/listers
configuration/local configuration/local

View File

@ -0,0 +1,23 @@
****************
Lister reference
****************
Listers are special segment collections which allow to show some list of
segments for each entity in the list of entities (multiply their segments list
by a list of entities). E.g. ``powerline.listers.vim.tablister`` presented with
``powerline.segments.vim.tabnr`` and ``….file_name`` as segments will emit
segments with buffer names and tabpage numbers for each tabpage shown by vim.
Listers appear in configuration as irregular segments having ``segment_list`` as
their type and ``segments`` key with a list of segments (a bit more details in
:ref:`Themes section of configuration reference <config-themes-segments>`).
More information in :ref:`Writing listers <dev-listers>` section.
Currently only Vim listers are available.
Vim listers
-----------
.. automodule:: powerline.listers.vim
:members:

View File

@ -448,6 +448,8 @@ ascii Theme without any unicode characters at all
background colors, soft ones are used between segments with same background colors, soft ones are used between segments with same
background. Both options default to ``True``. background. Both options default to ``True``.
.. _config-themes-seg-draw_inner_divider:
``draw_inner_divider`` ``draw_inner_divider``
Determines whether inner soft dividers are to be drawn for function Determines whether inner soft dividers are to be drawn for function
segments. Only applicable for functions returning multiple segments. segments. Only applicable for functions returning multiple segments.

View File

@ -15,13 +15,7 @@ Segments are regular Python functions, and they may accept arguments. All
arguments should have a default value which will be used for themes that arguments should have a default value which will be used for themes that
don't provide an ``args`` dict. don't provide an ``args`` dict.
A segment function must return one of the following values: More information is available in :ref:`Writing segments <dev-segments>` section.
* ``None``, which will remove the segment from the prompt/statusline.
* A string, which will be the segment contents.
* A list of dicts consisting of a ``contents`` string, and
a ``highlight_group`` list. This is useful for providing a particular
highlighting group depending on the segment contents.
Available segments Available segments
================== ==================

View File

@ -4,3 +4,31 @@ Vim segments
.. automodule:: powerline.segments.vim .. automodule:: powerline.segments.vim
:members: :members:
Plugin-specific segments
========================
Syntastic segments
------------------
.. automodule:: powerline.segments.vim.plugin.syntastic
:members:
Ctrl-P segments
---------------
.. automodule:: powerline.segments.vim.plugin.ctrlp
:members:
Tagbar segments
---------------
.. automodule:: powerline.segments.vim.plugin.tagbar
:members:
NERDTree segments
-----------------
.. automodule:: powerline.segments.vim.plugin.nerdtree
:members:

View File

@ -7,5 +7,6 @@ Developer guide
:glob: :glob:
develop/segments develop/segments
develop/listers
develop/local-themes develop/local-themes
develop/extensions develop/extensions

View File

@ -0,0 +1,51 @@
.. _dev-listers:
***************
Writing listers
***************
Listers allow you to show some segments multiple times: once per each entity
(buffer, tabpage, etc) lister knows. They are functions which receive the
following arguments:
``pl``
A :py:class:`powerline.PowerlineLogger` class instance. It must be used for
logging.
``segment_info``
Base segment info dictionary. Lister function or class must have
``powerline_requires_segment_info`` to receive this argument.
.. warning::
Listers are close to useless if they do not have access to this
argument.
Refer to :ref:`segment_info detailed description <dev-segments-info>` for
further details.
``draw_inner_divider``
If False (default) soft dividers between segments in the listed group will
not be drawn regardless of actual segment settings. If True they will be
drawn, again regardless of actual segment settings. Set it to ``None`` in
order to respect segment settings.
And also any other argument(s) specified by user in :ref:`args key
<config-themes-seg-args>` (no additional arguments by default).
Listers must return a sequence of pairs. First item in the pair must contain
a ``segment_info`` dictionary specific to one of the listed entities.
Second item must contain another dictionary: it will be used to modify the
resulting segment. In addition to usual keys that describe segment the following
keys may be present (it is advised that *only* the following keys will be used):
``mode``
Segment-specific mode. Used to alter segment highlighting.
``priority_multiplier``
Value (usually a ``float``) used to multiply segment priority. It is useful
for finer-grained controlling which segments disappear first: e.g. when
listing tab pages make first disappear directory names of the tabpages which
are most far away from current tabpage, then (when all directory names
disappeared) buffer names. Check out existing listers implementation in
:file:`powerline/listers/vim.py`.

View File

@ -1,3 +1,5 @@
.. _dev-segments:
**************** ****************
Writing segments Writing segments
**************** ****************
@ -51,16 +53,10 @@ Detailed description of used dictionary keys:
Text displayed by segment. Should be a ``unicode`` (Python2) or ``str`` Text displayed by segment. Should be a ``unicode`` (Python2) or ``str``
(Python3) instance. (Python3) instance.
``draw_hard_divider``, ``draw_soft_divider`` ``draw_hard_divider``, ``draw_soft_divider``, ``draw_inner_divider``
Determines whether given divider should be drawn. Both have the same meaning Determines whether given divider should be drawn. All have the same meaning
as :ref:`the similar keys in configuration as :ref:`the similar keys in configuration <config-themes-seg-draw_divider>`
<config-themes-seg-draw_divider>`. (:ref:`draw_inner_divider <config-themes-seg-draw_inner_divider>`).
.. _dev-segments-draw_inner_divider:
``draw_inner_divider``
Determines whether *any* divider between segments returned by function
should be drawn. Defaults to ``False``.
.. _dev-segments-highlight_group: .. _dev-segments-highlight_group:

View File

@ -48,20 +48,20 @@ Vim statusline
**Mode-dependent highlighting** **Mode-dependent highlighting**
* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-mode-normal.png * .. image:: _static/img/pl-mode-normal.png
:alt: Normal mode :alt: Normal mode
* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-mode-insert.png * .. image:: _static/img/pl-mode-insert.png
:alt: Insert mode :alt: Insert mode
* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-mode-visual.png * .. image:: _static/img/pl-mode-visual.png
:alt: Visual mode :alt: Visual mode
* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-mode-replace.png * .. image:: _static/img/pl-mode-replace.png
:alt: Replace mode :alt: Replace mode
**Automatic truncation of segments in small windows** **Automatic truncation of segments in small windows**
* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-truncate1.png * .. image:: _static/img/pl-truncate1.png
:alt: Truncation illustration :alt: Truncation illustration
* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-truncate2.png * .. image:: _static/img/pl-truncate2.png
:alt: Truncation illustration :alt: Truncation illustration
* .. image:: https://raw.github.com/Lokaltog/powerline/develop/docs/source/_static/img/pl-truncate3.png * .. image:: _static/img/pl-truncate3.png
:alt: Truncation illustration :alt: Truncation illustration

View File

@ -15,7 +15,7 @@
"position": "information:additional", "position": "information:additional",
"single_tab": "line_current", "single_tab": "line_current",
"many_tabs": "line_current", "many_tabs": "line_current",
"bufnr": "information:unimportant", "bufnr": "file_directory",
"winnr": "information:unimportant", "winnr": "information:unimportant",
"tabnr": "file_directory" "tabnr": "file_directory"
} }

View File

@ -51,6 +51,44 @@
"dark_green_gray": "gray5" "dark_green_gray": "gray5"
} }
}, },
"tab_nc": {
"colors": {
"brightyellow": "darkorange",
"brightestred": "darkred",
"gray0": "gray0",
"gray1": "gray0",
"gray2": "gray0",
"gray3": "gray1",
"gray4": "gray1",
"gray5": "gray1",
"gray6": "gray1",
"gray7": "gray4",
"gray8": "gray4",
"gray9": "gray4",
"gray10": "gray5",
"white": "gray6",
"dark_green_gray": "gray5"
}
},
"buf_nc": {
"colors": {
"brightyellow": "darkorange",
"brightestred": "darkred",
"gray0": "gray0",
"gray1": "gray0",
"gray2": "gray0",
"gray3": "gray1",
"gray4": "gray1",
"gray5": "gray1",
"gray6": "gray1",
"gray7": "gray4",
"gray8": "gray4",
"gray9": "gray4",
"gray10": "gray5",
"white": "gray6",
"dark_green_gray": "gray5"
}
},
"i": { "i": {
"colors": { "colors": {
"gray0": "darkestblue", "gray0": "darkestblue",

View File

@ -43,6 +43,26 @@
"oldlace": "gray61" "oldlace": "gray61"
} }
}, },
"tab_nc": {
"colors": {
"darkgreencopper": "royalblue5",
"lightskyblue4": "royalblue5",
"azure4": "darkgreencopper",
"gray61": "lightskyblue4",
"lightyellow": "azure4",
"oldlace": "gray61"
}
},
"buf_nc": {
"colors": {
"darkgreencopper": "royalblue5",
"lightskyblue4": "royalblue5",
"azure4": "darkgreencopper",
"gray61": "lightskyblue4",
"lightyellow": "azure4",
"oldlace": "gray61"
}
},
"i": { "i": {
"groups": { "groups": {
"background": { "fg": "oldlace", "bg": "darkgreencopper", "attr": [] }, "background": { "fg": "oldlace", "bg": "darkgreencopper", "attr": [] },

View File

@ -43,6 +43,26 @@
"gray13": "gray61" "gray13": "gray61"
} }
}, },
"tab_nc": {
"colors": {
"lightyellow": "darkgreencopper",
"azure4": "darkgreencopper",
"lightskyblue4": "lightyellow",
"gray61": "azure4",
"royalblue5": "lightskyblue4",
"gray13": "gray61"
}
},
"buf_nc": {
"colors": {
"lightyellow": "darkgreencopper",
"azure4": "darkgreencopper",
"lightskyblue4": "lightyellow",
"gray61": "azure4",
"royalblue5": "lightskyblue4",
"gray13": "gray61"
}
},
"i": { "i": {
"groups": { "groups": {
"background": { "fg": "gray13", "bg": "lightyellow", "attr": [] }, "background": { "fg": "gray13", "bg": "lightyellow", "attr": [] },

View File

@ -4,18 +4,23 @@
"left": [ "left": [
{ {
"type": "segment_list", "type": "segment_list",
"module": "powerline.listers.vim",
"name": "tabbuflister", "name": "tabbuflister",
"segments": [ "segments": [
{ {
"name": "tabnr", "name": "tabnr",
"after": " ", "after": " ",
"draw_soft_divider": false, "exclude_modes": ["tab", "buf", "buf_nc"],
"exclude_modes": ["tab", "buf"], "priority": 5
},
{
"name": "bufnr",
"after": " ",
"exclude_modes": ["tab", "buf", "tab_nc"],
"priority": 5 "priority": 5
}, },
{ {
"name": "file_directory", "name": "file_directory",
"draw_soft_divider": false,
"priority": 40 "priority": 40
}, },
{ {
@ -24,6 +29,11 @@
"display_no_file": true "display_no_file": true
}, },
"priority": 10 "priority": 10
},
{
"name": "modified_indicator",
"exclude_modes": ["tab", "tab_nc"],
"priority": 5
} }
] ]
}, },

View File

@ -733,7 +733,7 @@ top_colorscheme_spec = (Spec(
mode_translations_value_spec(), mode_translations_value_spec(),
).optional().context_message('Error while loading mode translations (key {key})').optional(), ).optional().context_message('Error while loading mode translations (key {key})').optional(),
).context_message('Error while loading top-level coloscheme')) ).context_message('Error while loading top-level coloscheme'))
vim_mode_spec = Spec().oneof(set(list(vim_modes) + ['nc'])).copy vim_mode_spec = Spec().oneof(set(list(vim_modes) + ['nc', 'tab_nc', 'buf_nc'])).copy
vim_colorscheme_spec = (Spec( vim_colorscheme_spec = (Spec(
name=name_spec(), name=name_spec(),
groups=groups_spec(), groups=groups_spec(),

View File

121
powerline/listers/vim.py Normal file
View File

@ -0,0 +1,121 @@
# vim:fileencoding=utf-8:noet
from __future__ import unicode_literals, absolute_import, division
try:
import vim
except ImportError:
vim = {} # NOQA
from powerline.theme import requires_segment_info
from powerline.bindings.vim import (current_tabpage, list_tabpages, vim_getbufoption)
def tabpage_updated_segment_info(segment_info, tabpage, mode):
segment_info = segment_info.copy()
window = tabpage.window
buffer = window.buffer
segment_info.update(
tabpage=tabpage,
tabnr=tabpage.number,
window=window,
winnr=window.number,
window_id=int(window.vars.get('powerline_window_id', -1)),
buffer=buffer,
bufnr=buffer.number,
mode=mode,
)
return segment_info
@requires_segment_info
def tablister(pl, segment_info, **kwargs):
'''List all tab pages in segment_info format
Specifically generates a list of segment info dictionaries with ``window``,
``winnr``, ``window_id``, ``buffer`` and ``bufnr`` keys set to tab-local
ones and additional ``tabpage`` and ``tabnr`` keys.
Sets segment ``mode`` to either ``tab`` (for current tab page) or ``tab_nc``
(for all other tab pages).
Works best with vim-7.4 or later: earlier versions miss tabpage object and
thus window objects are not available as well.
'''
cur_tabpage = current_tabpage()
cur_tabnr = cur_tabpage.number
def add_multiplier(tabpage, dct):
dct['priority_multiplier'] = 1 + (0.001 * abs(tabpage.number - cur_tabnr))
return dct
return (
(lambda tabpage, mode: (
tabpage_updated_segment_info(segment_info, tabpage, mode),
add_multiplier(tabpage, {'mode': mode})
))(tabpage, 'tab' if tabpage == cur_tabpage else 'tab_nc')
for tabpage in list_tabpages()
)
def buffer_updated_segment_info(segment_info, buffer, mode):
segment_info = segment_info.copy()
segment_info.update(
window=None,
winnr=None,
window_id=None,
buffer=buffer,
bufnr=buffer.number,
mode=mode,
)
return segment_info
@requires_segment_info
def bufferlister(pl, segment_info, show_unlisted=False, **kwargs):
'''List all buffers in segment_info format
Specifically generates a list of segment info dictionaries with ``buffer``
and ``bufnr`` keys set to buffer-specific ones, ``window``, ``winnr`` and
``window_id`` keys set to None.
Sets segment ``mode`` to either ``buf`` (for current buffer) or ``buf_nc``
(for all other buffers).
:param bool show_unlisted:
True if unlisted buffers should be shown as well. Current buffer is
always shown.
'''
cur_buffer = vim.current.buffer
cur_bufnr = cur_buffer.number
def add_multiplier(buffer, dct):
dct['priority_multiplier'] = 1 + (0.001 * abs(buffer.number - cur_bufnr))
return dct
return (
(
buf_segment_info,
add_multiplier(buf_segment_info['buffer'], {'mode': buf_segment_info['mode']})
)
for buf_segment_info in (
buffer_updated_segment_info(
segment_info,
buffer,
('buf' if buffer is cur_buffer else 'buf_nc')
)
for buffer in vim.buffers
) if (
buf_segment_info['buffer'] is cur_buffer
or show_unlisted
or int(vim_getbufoption(buf_segment_info, 'buflisted'))
)
)
@requires_segment_info
def tabbuflister(**kwargs):
if len(list_tabpages()) == 1:
return bufferlister(**kwargs)
else:
return tablister(**kwargs)

View File

@ -258,12 +258,14 @@ class Renderer(object):
# Handle excluded/included segments for the current mode # Handle excluded/included segments for the current mode
segments = [ segments = [
self._get_highlighting(segment, segment['mode'] or mode) self._get_highlighting(segment, segment['mode'] or mode)
for segment, segment_mode in (
(segment, segment['mode'])
for segment in segments for segment in segments
if ( ) if (
mode not in segment['exclude_modes'] segment_mode not in segment['exclude_modes']
and ( and (
not segment['include_modes'] not segment['include_modes']
or mode in segment['include_modes'] or segment_mode in segment['include_modes']
) )
) )
] ]

View File

@ -95,6 +95,8 @@ def get_attr_func(contents_func, key, args):
def process_segment_lister(pl, segment_info, parsed_segments, side, lister, subsegments, patcher_args): def process_segment_lister(pl, segment_info, parsed_segments, side, lister, subsegments, patcher_args):
for subsegment_info, subsegment_update in lister(pl=pl, segment_info=segment_info, **patcher_args): for subsegment_info, subsegment_update in lister(pl=pl, segment_info=segment_info, **patcher_args):
draw_inner_divider = subsegment_update.pop('draw_inner_divider', False)
old_pslen = len(parsed_segments)
for subsegment in subsegments: for subsegment in subsegments:
if subsegment_update: if subsegment_update:
subsegment = subsegment.copy() subsegment = subsegment.copy()
@ -102,6 +104,10 @@ def process_segment_lister(pl, segment_info, parsed_segments, side, lister, subs
if subsegment_update['priority_multiplier'] and subsegment['priority']: if subsegment_update['priority_multiplier'] and subsegment['priority']:
subsegment['priority'] *= subsegment_update['priority_multiplier'] subsegment['priority'] *= subsegment_update['priority_multiplier']
process_segment(pl, side, subsegment_info, parsed_segments, subsegment) process_segment(pl, side, subsegment_info, parsed_segments, subsegment)
new_pslen = len(parsed_segments)
if new_pslen > old_pslen + 1 and draw_inner_divider is not None:
for i in range(old_pslen, new_pslen - 1) if side == 'left' else range(old_pslen + 1, new_pslen):
parsed_segments[i]['draw_soft_divider'] = draw_inner_divider
return None return None

View File

@ -4,15 +4,13 @@ from __future__ import unicode_literals, absolute_import, division
import os import os
import re import re
try: try:
import vim import vim
except ImportError: except ImportError:
vim = {} # NOQA vim = {} # NOQA
try: from collections import defaultdict
from __builtin__ import xrange as range
except ImportError:
pass
from powerline.bindings.vim import (vim_get_func, getbufvar, vim_getbufoption, from powerline.bindings.vim import (vim_get_func, getbufvar, vim_getbufoption,
buffer_name, vim_getwinvar, buffer_name, vim_getwinvar,
@ -23,7 +21,11 @@ from powerline.lib import add_divider_highlight_group
from powerline.lib.vcs import guess, tree_status from powerline.lib.vcs import guess, tree_status
from powerline.lib.humanize_bytes import humanize_bytes from powerline.lib.humanize_bytes import humanize_bytes
from powerline.lib import wraps_saveargs as wraps from powerline.lib import wraps_saveargs as wraps
from collections import defaultdict
try:
from __builtin__ import xrange as range
except ImportError:
pass
vim_funcs = { vim_funcs = {
@ -629,96 +631,3 @@ def single_tab(pl, single_text='Bufs', multiple_text='Tabs'):
'contents': multiple_text, 'contents': multiple_text,
'highlight_group': ['many_tabs'], 'highlight_group': ['many_tabs'],
}] }]
def tabpage_updated_segment_info(segment_info, tabpage):
segment_info = segment_info.copy()
window = tabpage.window
buffer = window.buffer
segment_info.update(
tabpage=tabpage,
tabnr=tabpage.number,
window=window,
winnr=window.number,
window_id=int(window.vars.get('powerline_window_id', -1)),
buffer=buffer,
bufnr=buffer.number,
)
return segment_info
@requires_segment_info
def tablister(pl, segment_info):
'''List all tab pages in segment_info format
Specifically generates a list of segment info dictionaries with ``window``,
``winnr``, ``window_id``, ``buffer`` and ``bufnr`` keys set to tab-local
ones and additional ``tabpage`` and ``tabnr`` keys.
Sets segment ``mode`` to either ``tab`` (for current tab page) or ``nc``
(for all other tab pages).
Works best with vim-7.4 or later: earlier versions miss tabpage object and
thus window objects are not available as well.
'''
cur_tabpage = current_tabpage()
cur_tabnr = cur_tabpage.number
def add_multiplier(tabpage, dct):
dct['priority_multiplier'] = 1 + (0.001 * abs(tabpage.number - cur_tabnr))
return dct
return [
(
tabpage_updated_segment_info(segment_info, tabpage),
add_multiplier(tabpage, {'mode': ('tab' if tabpage == cur_tabpage else 'nc')})
)
for tabpage in list_tabpages()
]
def buffer_updated_segment_info(segment_info, buffer):
segment_info = segment_info.copy()
segment_info.update(
window=None,
winnr=None,
window_id=None,
buffer=buffer,
bufnr=buffer.number,
)
return segment_info
@requires_segment_info
def bufferlister(pl, segment_info):
'''List all buffers in segment_info format
Specifically generates a list of segment info dictionaries with ``buffer``
and ``bufnr`` keys set to buffer-specific ones, ``window``, ``winnr`` and
``window_id`` keys unset.
Sets segment ``mode`` to either ``buf`` (for current buffer) or ``nc``
(for all other buffers).
'''
cur_buffer = vim.current.buffer
cur_bufnr = cur_buffer.number
def add_multiplier(buffer, dct):
dct['priority_multiplier'] = 1 + (0.001 * abs(buffer.number - cur_bufnr))
return dct
return [
(
buffer_updated_segment_info(segment_info, buffer),
add_multiplier(buffer, {'mode': ('tab' if buffer == cur_buffer else 'nc')})
)
for buffer in vim.buffers
]
@requires_segment_info
def tabbuflister(*args, **kwargs):
if len(list_tabpages()) == 1:
return bufferlister(*args, **kwargs)
else:
return tablister(*args, **kwargs)

View File

@ -26,7 +26,7 @@ catch
cquit cquit
endtry endtry
if result isnot# '%#Pl_240_5789784_235_2500134_NONE# ./%#Pl_244_8421504_235_2500134_bold#abc %#Pl_244_8421504_235_2500134_NONE# %#Pl_240_5789784_235_2500134_NONE#./%#Pl_244_8421504_235_2500134_bold#def %#Pl_235_2500134_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                               %#Pl_252_13684944_236_3158064_NONE# %#Pl_235_2500134_252_13684944_bold# Bufs ' if result isnot# '%#Pl_240_5789784_235_2500134_NONE# 1 %#Pl_240_5789784_235_2500134_NONE#./%#Pl_244_8421504_235_2500134_bold#abc %#Pl_244_8421504_235_2500134_NONE# %#Pl_240_5789784_235_2500134_NONE#2 %#Pl_240_5789784_235_2500134_NONE#./%#Pl_244_8421504_235_2500134_bold#def %#Pl_235_2500134_240_5789784_NONE# %#Pl_250_12369084_240_5789784_NONE#./%#Pl_231_16777215_240_5789784_bold#ghi %#Pl_240_5789784_236_3158064_NONE# %#Pl_231_16777215_236_3158064_NONE#                                           %#Pl_252_13684944_236_3158064_NONE# %#Pl_235_2500134_252_13684944_bold# Bufs '
call writefile(['Unexpected tabline (2)', result], 'message.fail') call writefile(['Unexpected tabline (2)', result], 'message.fail')
cquit cquit
endif endif