Merge pull request #948 from ZyX-I/tabline

Add tabline support
This commit is contained in:
Nikolai Aleksandrovich Pavlov 2014-08-03 13:24:26 +04:00
commit 1cc46c7126
17 changed files with 842 additions and 282 deletions

View File

@ -272,6 +272,12 @@ Themes
highlighting group is defined in the :ref:`highlight_group
option <config-themes-seg-highlight_group>`.
``segments_list``
Sub-list of segments. This list only allows :ref:`name
<config-themes-seg-name>`, :ref:`segments
<config-themes-seg-segments>` and :ref:`args
<config-themes-seg-args>` options.
``module``
.. _config-themes-seg-module:
@ -282,7 +288,7 @@ Themes
``name``
.. _config-themes-seg-name:
Function name, only required for function segments.
Function name, only required for function and list segments.
``highlight_group``
.. _config-themes-seg-highlight_group:
@ -366,3 +372,6 @@ Themes
Boolean. If false disables displaying of the segment.
Defaults to ``True``.
``segments``
A list of subsegments.

View File

@ -84,10 +84,10 @@ else:
def bufvar_exists(buffer, varname): # NOQA
if not buffer or buffer.number == vim.current.buffer.number:
return vim.eval('exists("b:{0}")'.format(varname))
return int(vim.eval('exists("b:{0}")'.format(varname)))
else:
return vim.eval('has_key(getbufvar({0}, ""), {1})'
.format(buffer.number, varname))
return int(vim.eval('has_key(getbufvar({0}, ""), {1})'
.format(buffer.number, varname)))
def vim_getwinvar(segment_info, varname): # NOQA
result = vim.eval('getwinvar({0}, "{1}")'.format(segment_info['winnr'], varname))
@ -104,6 +104,79 @@ else:
return getbufvar(info['bufnr'], '&' + option)
if hasattr(vim, 'tabpages'):
current_tabpage = lambda: vim.current.tabpage
list_tabpages = lambda: vim.tabpages
else:
class FalseObject(object):
@staticmethod
def __nonzero__():
return False
__bool__ = __nonzero__
def get_buffer(number):
for buffer in vim.buffers:
if buffer.number == number:
return buffer
raise KeyError(number)
class WindowVars(object):
__slots__ = ('tabnr', 'winnr')
def __init__(self, window):
self.tabnr = window.tabnr
self.winnr = window.number
def __getitem__(self, key):
has_key = vim.eval('has_key(gettabwinvar({0}, {1}, ""), "{2}")'.format(self.tabnr, self.winnr, key))
if has_key == '0':
raise KeyError
return vim.eval('gettabwinvar({0}, {1}, "{2}")'.format(self.tabnr, self.winnr, key))
def get(self, key, default=None):
try:
return self[key]
except KeyError:
return default
class Window(FalseObject):
__slots__ = ('tabnr', 'number', '_vars')
def __init__(self, tabnr, number):
self.tabnr = tabnr
self.number = number
self.vars = WindowVars(self)
@property
def buffer(self):
return get_buffer(int(vim.eval('tabpagebuflist({0})[{1}]'.format(self.tabnr, self.number - 1))))
class Tabpage(FalseObject):
__slots__ = ('number',)
def __init__(self, number):
self.number = number
def __eq__(self, tabpage):
if not isinstance(tabpage, Tabpage):
raise NotImplementedError
return self.number == tabpage.number
@property
def window(self):
return Window(self.number, int(vim.eval('tabpagewinnr({0})'.format(self.number))))
def _last_tab_nr():
return int(vim.eval('tabpagenr("$")'))
def current_tabpage(): # NOQA
return Tabpage(int(vim.eval('tabpagenr()')))
def list_tabpages(): # NOQA
return [Tabpage(nr) for nr in range(1, _last_tab_nr() + 1)]
if sys.version_info < (3,) or not hasattr(vim, 'bindeval'):
getbufvar = vim_get_func('getbufvar')
else:

View File

@ -11,6 +11,9 @@
"file_name_empty": "file_directory",
"line_percent": "information:additional",
"line_count": "line_current",
"position": "information:additional"
"position": "information:additional",
"single_tab": "line_current",
"many_tabs": "line_current",
"tabnr": "file_directory"
}
}

View File

@ -39,6 +39,8 @@
"colorscheme": "default",
"theme": "default",
"local_themes": {
"__tabline__": "tabline",
"cmdwin": "cmdwin",
"help": "help",
"quickfix": "quickfix",

View File

@ -0,0 +1,44 @@
{
"default_module": "powerline.segments.vim",
"segments": {
"left": [
{
"type": "segment_list",
"name": "tabbuflister",
"segments": [
{
"name": "tabnr",
"after": " ",
"draw_soft_divider": false,
"exclude_modes": ["tab", "buf"],
"priority": 5
},
{
"name": "file_directory",
"draw_soft_divider": false,
"priority": 40
},
{
"name": "file_name",
"args": {
"display_no_file": true
},
"priority": 10
}
]
},
{
"type": "string",
"highlight_group": ["background"],
"draw_soft_divider": false,
"draw_hard_divider": false,
"width": "auto"
}
],
"right": [
{
"name": "single_tab"
}
]
}
}

View File

@ -71,6 +71,8 @@ class DelayedEchoErr(EchoErr):
def __nonzero__(self):
return not not self.errs
__bool__ = __nonzero__
class Spec(object):
def __init__(self, **keys):
@ -93,15 +95,21 @@ class Spec(object):
self.did_type = True
return self
def copy(self):
return self.__class__()._update(self.__dict__)
def copy(self, copied=None):
copied = copied or {}
try:
return copied[id(self)]
except KeyError:
instance = self.__class__()
copied[id(self)] = instance
return self.__class__()._update(self.__dict__, copied)
def _update(self, d):
def _update(self, d, copied):
self.__dict__.update(d)
self.keys = copy(self.keys)
self.checks = copy(self.checks)
self.uspecs = copy(self.uspecs)
self.specs = [spec.copy() for spec in self.specs]
self.specs = [spec.copy(copied) for spec in self.specs]
return self
def unknown_spec(self, keyfunc, spec):
@ -379,6 +387,9 @@ class WithPath(object):
def check_matcher_func(ext, match_name, data, context, echoerr):
import_paths = [os.path.expanduser(path) for path in context[0][1].get('common', {}).get('paths', [])]
if match_name == '__tabline__':
return True, False
match_module, separator, match_function = match_name.rpartition('.')
if not separator:
match_module = 'powerline.matchers.{0}'.format(ext)
@ -681,13 +692,14 @@ type_keys = {
'function': set(('args', 'module', 'draw_inner_divider')),
'string': set(('contents', 'type', 'highlight_group', 'divider_highlight_group')),
'filler': set(('type', 'highlight_group', 'divider_highlight_group')),
'segment_list': set(('segments', 'module', 'args', 'type')),
}
required_keys = {
'function': set(),
'function': set(('name',)),
'string': set(('contents',)),
'filler': set(),
'segment_list': set(('name', 'segments',)),
}
function_keys = set(('args', 'module'))
highlight_keys = set(('highlight_group', 'name'))
@ -712,7 +724,7 @@ def check_key_compatibility(segment, data, context, echoerr):
problem_mark=list(unknown_keys)[0].mark)
hadproblem = True
if not (keys > required_keys[segment_type]):
if not (keys >= required_keys[segment_type]):
missing_keys = required_keys[segment_type] - keys
echoerr(context='Error while checking segments (key {key})'.format(key=context_key(context)),
context_mark=context[-1][1].mark,
@ -862,7 +874,7 @@ def check_segment_name(name, data, context, echoerr):
hadproblem = True
return True, False, hadproblem
else:
elif context[-2][1].get('type') != 'segment_list':
if name not in context[0][1].get('segment_data', {}):
top_theme_name = data['main_config'].get('ext', {}).get(ext, {}).get('theme', None)
if data['theme'] == top_theme_name:
@ -1070,32 +1082,34 @@ args_spec = Spec(
).unknown_spec(Spec(), Spec()).optional().copy
highlight_group_spec = Spec().type(unicode).copy
segment_module_spec = Spec().type(unicode).func(check_segment_module).optional().copy
segments_spec = Spec().optional().list(
Spec(
type=Spec().oneof(type_keys).optional(),
name=Spec().re('^[a-zA-Z_]\w+$').func(check_segment_name).optional(),
exclude_modes=Spec().list(vim_mode_spec()).optional(),
include_modes=Spec().list(vim_mode_spec()).optional(),
draw_hard_divider=Spec().type(bool).optional(),
draw_soft_divider=Spec().type(bool).optional(),
draw_inner_divider=Spec().type(bool).optional(),
display=Spec().type(bool).optional(),
module=segment_module_spec(),
priority=Spec().type(int, float, type(None)).optional(),
after=Spec().type(unicode).optional(),
before=Spec().type(unicode).optional(),
width=Spec().either(Spec().unsigned(), Spec().cmp('eq', 'auto')).optional(),
align=Spec().oneof(set('lr')).optional(),
args=args_spec().func(lambda *args, **kwargs: check_args(get_one_segment_variant, *args, **kwargs)),
contents=Spec().type(unicode).optional(),
highlight_group=Spec().list(
highlight_group_spec().re('^(?:(?!:divider$).)+$',
lambda value: 'it is recommended that only divider highlight group names end with ":divider"')
).func(check_highlight_groups).optional(),
divider_highlight_group=highlight_group_spec().func(check_highlight_group).re(':divider$',
lambda value: 'it is recommended that divider highlight group names end with ":divider"').optional(),
).func(check_full_segment_data),
).copy
sub_segments_spec = Spec()
segment_spec = Spec(
type=Spec().oneof(type_keys).optional(),
name=Spec().re('^[a-zA-Z_]\w+$').func(check_segment_name).optional(),
exclude_modes=Spec().list(vim_mode_spec()).optional(),
include_modes=Spec().list(vim_mode_spec()).optional(),
draw_hard_divider=Spec().type(bool).optional(),
draw_soft_divider=Spec().type(bool).optional(),
draw_inner_divider=Spec().type(bool).optional(),
display=Spec().type(bool).optional(),
module=segment_module_spec(),
priority=Spec().type(int, float, type(None)).optional(),
after=Spec().type(unicode).optional(),
before=Spec().type(unicode).optional(),
width=Spec().either(Spec().unsigned(), Spec().cmp('eq', 'auto')).optional(),
align=Spec().oneof(set('lr')).optional(),
args=args_spec().func(lambda *args, **kwargs: check_args(get_one_segment_variant, *args, **kwargs)),
contents=Spec().type(unicode).optional(),
highlight_group=Spec().list(
highlight_group_spec().re('^(?:(?!:divider$).)+$',
lambda value: 'it is recommended that only divider highlight group names end with ":divider"')
).func(check_highlight_groups).optional(),
divider_highlight_group=highlight_group_spec().func(check_highlight_group).re(':divider$',
lambda value: 'it is recommended that divider highlight group names end with ":divider"').optional(),
segments=sub_segments_spec,
).func(check_full_segment_data)
sub_segments_spec.optional().list(segment_spec)
segments_spec = Spec().optional().list(segment_spec).copy
segdict_spec=Spec(
left=segments_spec().context_message('Error while loading segments from left side (key {key})'),
right=segments_spec().context_message('Error while loading segments from right side (key {key})'),

View File

@ -222,20 +222,44 @@ class Renderer(object):
segments = theme.get_segments(side, line, self.get_segment_info(segment_info, mode))
# Handle excluded/included segments for the current mode
segments = [self._get_highlighting(segment, mode) for segment in segments
if mode not in segment['exclude_modes'] and (not segment['include_modes'] or mode in segment['include_modes'])]
segments = [segment for segment in self._render_segments(theme, segments)]
segments = [
self._get_highlighting(segment, segment['mode'] or mode)
for segment in segments
if (
mode not in segment['exclude_modes']
and (
not segment['include_modes']
or mode in segment['include_modes']
)
)
]
if not width:
# No width specified, so we don't need to crop or pad anything
return construct_returned_value(''.join([segment['_rendered_hl'] for segment in segments]) + self.hlstyle(), segments, output_raw)
return construct_returned_value(''.join([
segment['_rendered_hl']
for segment in self._render_segments(theme, segments)
]) + self.hlstyle(), segments, output_raw)
divider_lengths = {
'left': {
'hard': self.strwidth(theme.get_divider('left', 'hard')),
'soft': self.strwidth(theme.get_divider('left', 'soft')),
},
'right': {
'hard': self.strwidth(theme.get_divider('right', 'hard')),
'soft': self.strwidth(theme.get_divider('right', 'soft')),
},
}
length = self._render_length(theme, segments, divider_lengths)
# Create an ordered list of segments that can be dropped
segments_priority = sorted((segment for segment in segments if segment['priority'] is not None), key=lambda segment: segment['priority'], reverse=True)
while sum([segment['_len'] for segment in segments]) > width and len(segments_priority):
segments.remove(segments_priority[0])
segments_priority.pop(0)
for segment in segments_priority:
if self._render_length(theme, segments, divider_lengths) <= width:
break
segments.remove(segment)
# Distribute the remaining space on spacer segments
segments_spacers = [segment for segment in segments if segment['width'] == 'auto']
@ -256,6 +280,38 @@ class Renderer(object):
return construct_returned_value(rendered_highlighted, segments, output_raw)
def _render_length(self, theme, segments, divider_lengths):
'''Update segments lengths and return them
'''
segments_len = len(segments)
ret = 0
divider_spaces = theme.get_spaces()
for index, segment in enumerate(segments):
side = segment['side']
if segment['_contents_len'] is None:
segment_len = segment['_contents_len'] = self.strwidth(segment['contents'])
else:
segment_len = segment['_contents_len']
prev_segment = segments[index - 1] if index > 0 else theme.EMPTY_SEGMENT
next_segment = segments[index + 1] if index < segments_len - 1 else theme.EMPTY_SEGMENT
compare_segment = next_segment if side == 'left' else prev_segment
divider_type = 'soft' if compare_segment['highlight']['bg'] == segment['highlight']['bg'] else 'hard'
outer_padding = int(bool(
(index == 0 and side == 'left') or
(index == segments_len - 1 and side == 'right')
))
draw_divider = segment['draw_' + divider_type + '_divider']
segment_len += segment['_space_left'] + segment['_space_right'] + outer_padding
if draw_divider:
segment_len += divider_lengths[side][divider_type] + divider_spaces
segment['_len'] = segment_len
ret += segment_len
return ret
def _render_segments(self, theme, segments, render_highlighted=True):
'''Internal segment rendering method.
@ -268,19 +324,19 @@ class Renderer(object):
statusline if render_highlighted is True.
'''
segments_len = len(segments)
divider_spaces = theme.get_spaces()
for index, segment in enumerate(segments):
segment['_rendered_raw'] = ''
segment['_rendered_hl'] = ''
side = segment['side']
prev_segment = segments[index - 1] if index > 0 else theme.EMPTY_SEGMENT
next_segment = segments[index + 1] if index < segments_len - 1 else theme.EMPTY_SEGMENT
compare_segment = next_segment if segment['side'] == 'left' else prev_segment
outer_padding = ' ' if (index == 0 and segment['side'] == 'left') or (index == segments_len - 1 and segment['side'] == 'right') else ''
compare_segment = next_segment if side == 'left' else prev_segment
outer_padding = int(bool(
(index == 0 and side == 'left') or
(index == segments_len - 1 and side == 'right')
)) * ' '
divider_type = 'soft' if compare_segment['highlight']['bg'] == segment['highlight']['bg'] else 'hard'
divider_raw = theme.get_divider(segment['side'], divider_type)
divider_spaces = theme.get_spaces()
divider_highlighted = ''
contents_raw = segment['contents']
contents_highlighted = ''
@ -288,48 +344,64 @@ class Renderer(object):
# Pad segments first
if draw_divider:
if segment['side'] == 'left':
contents_raw = outer_padding + (segment['_space_left'] * ' ') + contents_raw + ((divider_spaces + segment['_space_right']) * ' ')
divider_raw = theme.get_divider(side, divider_type).replace(' ', NBSP)
if side == 'left':
contents_raw = (
outer_padding + (segment['_space_left'] * ' ')
+ contents_raw
+ ((divider_spaces + segment['_space_right']) * ' ')
)
else:
contents_raw = ((divider_spaces + segment['_space_left']) * ' ') + contents_raw + (segment['_space_right'] * ' ') + outer_padding
contents_raw = (
((divider_spaces + segment['_space_left']) * ' ')
+ contents_raw
+ (segment['_space_right'] * ' ') + outer_padding
)
else:
if segment['side'] == 'left':
contents_raw = outer_padding + (segment['_space_left'] * ' ') + contents_raw + (segment['_space_right'] * ' ')
if side == 'left':
contents_raw = (
outer_padding + (segment['_space_left'] * ' ')
+ contents_raw
+ (segment['_space_right'] * ' ')
)
else:
contents_raw = (segment['_space_left'] * ' ') + contents_raw + (segment['_space_right'] * ' ') + outer_padding
contents_raw = (
(segment['_space_left'] * ' ')
+ contents_raw
+ (segment['_space_right'] * ' ') + outer_padding
)
# Replace spaces with no-break spaces
divider_raw = divider_raw.replace(' ', NBSP)
contents_raw = contents_raw.translate(self.np_character_translations)
# Apply highlighting to padded dividers and contents
if render_highlighted:
if divider_type == 'soft':
divider_highlight_group_key = 'highlight' if segment['divider_highlight_group'] is None else 'divider_highlight'
divider_fg = segment[divider_highlight_group_key]['fg']
divider_bg = segment[divider_highlight_group_key]['bg']
else:
divider_fg = segment['highlight']['bg']
divider_bg = compare_segment['highlight']['bg']
divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False)
if draw_divider:
if divider_type == 'soft':
divider_highlight_group_key = 'highlight' if segment['divider_highlight_group'] is None else 'divider_highlight'
divider_fg = segment[divider_highlight_group_key]['fg']
divider_bg = segment[divider_highlight_group_key]['bg']
else:
divider_fg = segment['highlight']['bg']
divider_bg = compare_segment['highlight']['bg']
divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False)
contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight'])
# Append padded raw and highlighted segments to the rendered segment variables
if draw_divider:
if segment['side'] == 'left':
segment['_rendered_raw'] += contents_raw + divider_raw
segment['_rendered_hl'] += contents_highlighted + divider_highlighted
if side == 'left':
segment['_rendered_raw'] = contents_raw + divider_raw
segment['_rendered_hl'] = contents_highlighted + divider_highlighted
else:
segment['_rendered_raw'] += divider_raw + contents_raw
segment['_rendered_hl'] += divider_highlighted + contents_highlighted
segment['_rendered_raw'] = divider_raw + contents_raw
segment['_rendered_hl'] = divider_highlighted + contents_highlighted
else:
if segment['side'] == 'left':
segment['_rendered_raw'] += contents_raw
segment['_rendered_hl'] += contents_highlighted
if side == 'left':
segment['_rendered_raw'] = contents_raw
segment['_rendered_hl'] = contents_highlighted
else:
segment['_rendered_raw'] += contents_raw
segment['_rendered_hl'] += contents_highlighted
segment['_len'] = self.strwidth(segment['_rendered_raw'])
segment['_rendered_raw'] = contents_raw
segment['_rendered_hl'] = contents_highlighted
yield segment
@classmethod

View File

@ -2,7 +2,7 @@
from __future__ import absolute_import, unicode_literals
from powerline.bindings.vim import vim_get_func, environ
from powerline.bindings.vim import vim_get_func, environ, current_tabpage
from powerline.renderer import Renderer
from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE
from powerline.theme import Theme
@ -53,15 +53,19 @@ class VimRenderer(Renderer):
raise KeyError('There is already a local theme with given matcher')
self.local_themes[matcher] = theme
def get_matched_theme(self, match):
try:
return match['theme']
except KeyError:
match['theme'] = Theme(theme_config=match['config'], top_theme_config=self.theme_config, **self.theme_kwargs)
return match['theme']
def get_theme(self, matcher_info):
if matcher_info is None:
return self.get_matched_theme(self.local_themes[None])
for matcher in self.local_themes.keys():
if matcher(matcher_info):
match = self.local_themes[matcher]
try:
return match['theme']
except KeyError:
match['theme'] = Theme(theme_config=match['config'], top_theme_config=self.theme_config, **self.theme_kwargs)
return match['theme']
if matcher and matcher(matcher_info):
return self.get_matched_theme(self.local_themes[matcher])
else:
return self.theme
@ -80,28 +84,36 @@ class VimRenderer(Renderer):
def get_segment_info(self, segment_info, mode):
return segment_info or self.segment_info
def render(self, window, window_id, winnr):
def render(self, window=None, window_id=None, winnr=None):
'''Render all segments.'''
if window is vim.current.window:
mode = vim_mode(1)
mode = mode_translations.get(mode, mode)
else:
mode = 'nc'
segment_info = self.segment_info.copy()
segment_info.update({
'window': window,
'mode': mode,
'window_id': window_id,
'winnr': winnr,
})
segment_info['buffer'] = segment_info['window'].buffer
segment_info['bufnr'] = segment_info['buffer'].number
winwidth = segment_info['window'].width
if window is not None:
if window is vim.current.window:
mode = vim_mode(1)
mode = mode_translations.get(mode, mode)
else:
mode = 'nc'
segment_info.update(
window=window,
mode=mode,
window_id=window_id,
winnr=winnr,
buffer=window.buffer,
tabpage=current_tabpage(),
)
segment_info['tabnr'] = segment_info['tabpage'].number
segment_info['bufnr'] = segment_info['buffer'].number
winwidth = segment_info['window'].width
matcher_info = segment_info
else:
mode = None
winwidth = int(vim.eval('&columns'))
matcher_info = None
statusline = super(VimRenderer, self).render(
mode=mode,
width=winwidth,
segment_info=segment_info,
matcher_info=segment_info,
matcher_info=matcher_info,
)
return statusline

View File

@ -44,6 +44,7 @@ segment_getters = {
"function": get_function,
"string": get_string,
"filler": get_filler,
"segment_list": get_function,
}
@ -59,6 +60,69 @@ def get_attr_func(contents_func, key, args):
return lambda pl, shutdown_event: func(pl=pl, shutdown_event=shutdown_event, **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 in subsegments:
if subsegment_update:
subsegment = subsegment.copy()
subsegment.update(subsegment_update)
if subsegment_update['priority_multiplier'] and subsegment['priority']:
subsegment['priority'] *= subsegment_update['priority_multiplier']
process_segment(pl, side, subsegment_info, parsed_segments, subsegment)
return None
def process_segment(pl, side, segment_info, parsed_segments, segment):
if segment['type'] in ('function', 'segment_list'):
pl.prefix = segment['name']
try:
if segment['type'] == 'function':
contents = segment['contents_func'](pl, segment_info)
else:
contents = segment['contents_func'](pl, segment_info, parsed_segments, side)
except Exception as e:
pl.exception('Exception while computing segment: {0}', str(e))
return
if contents is None:
return
if isinstance(contents, list):
segment_base = segment.copy()
if contents:
draw_divider_position = -1 if side == 'left' else 0
for key, i, newval in (
('before', 0, ''),
('after', -1, ''),
('draw_soft_divider', draw_divider_position, True),
('draw_hard_divider', draw_divider_position, True),
):
try:
contents[i][key] = segment_base.pop(key)
segment_base[key] = newval
except KeyError:
pass
draw_inner_divider = None
if side == 'right':
append = parsed_segments.append
else:
pslen = len(parsed_segments)
append = lambda item: parsed_segments.insert(pslen, item)
for subsegment in (contents if side == 'right' else reversed(contents)):
segment_copy = segment_base.copy()
segment_copy.update(subsegment)
if draw_inner_divider is not None:
segment_copy['draw_soft_divider'] = draw_inner_divider
draw_inner_divider = segment_copy.pop('draw_inner_divider', None)
append(segment_copy)
else:
segment['contents'] = contents
parsed_segments.append(segment)
elif segment['width'] == 'auto' or (segment['type'] == 'string' and segment['contents'] is not None):
parsed_segments.append(segment)
def gen_segment_getter(pl, ext, common_config, theme_configs, default_module=None):
data = {
'default_module': default_module or 'powerline.segments.' + ext,
@ -90,8 +154,50 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module=Non
else:
highlight_group = segment.get('highlight_group') or segment.get('name')
if segment_type == 'function':
if segment_type in ('function', 'segment_list'):
args = dict(((str(k), v) for k, v in get_key(segment, module, 'args', {}).items()))
if segment_type == 'segment_list':
# Handle startup and shutdown of _contents_func?
subsegments = [
get(subsegment, side)
for subsegment in segment['segments']
]
return {
'name': segment.get('name'),
'type': segment_type,
'highlight_group': None,
'divider_highlight_group': None,
'before': None,
'after': None,
'contents_func': lambda pl, segment_info, parsed_segments, side: process_segment_lister(
pl, segment_info, parsed_segments, side,
patcher_args=args,
subsegments=subsegments,
lister=_contents_func,
),
'contents': None,
'priority': None,
'draw_soft_divider': None,
'draw_hard_divider': None,
'draw_inner_divider': None,
'side': side,
'exclude_modes': segment.get('exclude_modes', []),
'include_modes': segment.get('include_modes', []),
'width': None,
'align': None,
'startup': None,
'shutdown': None,
'mode': None,
'_rendered_raw': '',
'_rendered_hl': '',
'_len': None,
'_contents_len': None,
'_space_left': 0,
'_space_right': 0,
}
if segment_type == 'function':
startup_func = get_attr_func(_contents_func, 'startup', args)
shutdown_func = get_attr_func(_contents_func, 'shutdown', None)
@ -117,7 +223,6 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module=Non
'after': get_key(segment, module, 'after', ''),
'contents_func': contents_func,
'contents': contents,
'args': args if segment_type == 'function' else {},
'priority': segment.get('priority', None),
'draw_hard_divider': segment.get('draw_hard_divider', True),
'draw_soft_divider': segment.get('draw_soft_divider', True),
@ -129,9 +234,11 @@ def gen_segment_getter(pl, ext, common_config, theme_configs, default_module=Non
'align': segment.get('align', 'l'),
'startup': startup_func,
'shutdown': shutdown_func,
'mode': None,
'_rendered_raw': '',
'_rendered_hl': '',
'_len': 0,
'_len': None,
'_contents_len': None,
'_space_left': 0,
'_space_right': 0,
}

View File

@ -15,7 +15,8 @@ except ImportError:
from powerline.bindings.vim import (vim_get_func, getbufvar, vim_getbufoption,
buffer_name, vim_getwinvar,
register_buffer_cache)
register_buffer_cache, current_tabpage,
list_tabpages)
from powerline.theme import requires_segment_info, requires_filesystem_watcher
from powerline.lib import add_divider_highlight_group
from powerline.lib.vcs import guess, tree_status
@ -485,3 +486,138 @@ def trailing_whitespace(pl, segment_info):
ret = None
trailing_whitespace_cache[bufnr] = (changedtick, ret)
return ret
@requires_segment_info
def tabnr(pl, segment_info, show_current=False):
'''Show tabpage number
:param bool show_current:
If False do not show current tabpage number. This is default because
tabnr is by default only present in tabline.
'''
try:
tabnr = segment_info['tabnr']
except KeyError:
return None
if show_current or tabnr != current_tabpage().number:
return str(tabnr)
def single_tab(pl, single_text='Bufs', multiple_text='Tabs'):
'''Show one text if there is only one tab and another if there are many
Mostly useful for tabline to indicate what kind of data is shown there.
:param str single_text:
Text displayed when there is only one tabpage. May be None if you do not
want to display anything.
:param str multiple_text:
Text displayed when there is more then one tabpage. May be None if you
do not want to display anything.
Highlight groups used: ``single_tab``, ``many_tabs``.
'''
if len(list_tabpages()) == 1:
return single_text and [{
'contents': single_text,
'highlight_group': ['single_tab'],
}]
else:
return multiple_text and [{
'contents': multiple_text,
'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

@ -1,6 +1,6 @@
# vim:fileencoding=utf-8:noet
from powerline.segment import gen_segment_getter
from powerline.segment import gen_segment_getter, process_segment
from powerline.lib.unicode import u
import itertools
@ -32,6 +32,11 @@ class Theme(object):
run_once=False,
shutdown_event=None):
self.dividers = theme_config.get('dividers', common_config['dividers'])
self.dividers = dict((
(key, dict((k, u(v))
for k, v in val.items()))
for key, val in self.dividers.items()
))
self.spaces = theme_config.get('spaces', common_config['spaces'])
self.segments = []
self.EMPTY_SEGMENT = {
@ -91,53 +96,7 @@ class Theme(object):
for side in [side] if side else ['left', 'right']:
parsed_segments = []
for segment in self.segments[line][side]:
if segment['type'] == 'function':
self.pl.prefix = segment['name']
try:
contents = segment['contents_func'](self.pl, segment_info)
except Exception as e:
self.pl.exception('Exception while computing segment: {0}', str(e))
continue
if contents is None:
continue
if isinstance(contents, list):
segment_base = segment.copy()
if contents:
draw_divider_position = -1 if side == 'left' else 0
for key, i, newval in (
('before', 0, ''),
('after', -1, ''),
('draw_soft_divider', draw_divider_position, True),
('draw_hard_divider', draw_divider_position, True),
):
try:
contents[i][key] = segment_base.pop(key)
segment_base[key] = newval
except KeyError:
pass
draw_inner_divider = None
if side == 'right':
append = parsed_segments.append
else:
pslen = len(parsed_segments)
append = lambda item: parsed_segments.insert(pslen, item)
for subsegment in (contents if side == 'right' else reversed(contents)):
segment_copy = segment_base.copy()
segment_copy.update(subsegment)
if draw_inner_divider is not None:
segment_copy['draw_soft_divider'] = draw_inner_divider
draw_inner_divider = segment_copy.pop('draw_inner_divider', None)
append(segment_copy)
else:
segment['contents'] = contents
parsed_segments.append(segment)
elif segment['width'] == 'auto' or (segment['type'] == 'string' and segment['contents'] is not None):
parsed_segments.append(segment)
else:
continue
process_segment(self.pl, side, segment_info, parsed_segments, segment)
for segment in parsed_segments:
segment['contents'] = segment['before'] + u(segment['contents'] if segment['contents'] is not None else '') + segment['after']
# Align segment contents

View File

@ -72,7 +72,8 @@ class VimPowerline(Powerline):
return {}
self.get_matcher = gen_matcher_getter(self.ext, self.import_paths)
return dict(((self.get_matcher(key), {'config': self.load_theme_config(val)})
return dict(((None if key == '__tabline__' else self.get_matcher(key),
{'config': self.load_theme_config(val)})
for key, val in local_themes.items()))
def get_config_paths(self):
@ -141,6 +142,9 @@ class VimPowerline(Powerline):
return 'No window {0}'.format(window_id)
return self.render(window, window_id, winnr)
def tabline(self):
return self.render()
def new_window(self):
window, window_id, winnr = self.win_idx(None)
return self.render(window, window_id, winnr)
@ -202,3 +206,4 @@ def setup(pyeval=None, pycmd=None, can_replace_pyeval=True):
# Is immediately changed after new_window function is run. Good for global
# value.
vim.command('set statusline=%!{pyeval}(\'powerline.new_window()\')'.format(pyeval=pyeval))
vim.command('set tabline=%!{pyeval}(\'powerline.tabline()\')'.format(pyeval=pyeval))

View File

@ -1,5 +1,7 @@
#!/usr/bin/vim -S
set nocompatible
tabedit abc
tabedit def
try
source powerline/bindings/vim/plugin/powerline.vim
catch
@ -16,4 +18,4 @@ if len(mess)>1
call writefile(mess, 'message.fail')
cquit
endif
quit!
qall!

View File

@ -32,7 +32,7 @@ class TestConfig(TestCase):
local_themes_raw = json.load(f)['ext']['vim']['local_themes']
# Don't run tests on external/plugin segments
local_themes = dict((k, v) for (k, v) in local_themes_raw.items())
self.assertEqual(len(buffers), len(local_themes))
self.assertEqual(len(buffers), len(local_themes) - 1)
outputs = {}
i = 0
@ -53,6 +53,8 @@ class TestConfig(TestCase):
outputs[out] = (i, (args, kwargs), mode)
with vim_module._with('bufname', '/tmp/foo.txt'):
out = powerline.render()
outputs[out] = (-1, (None, None), 'tab')
with vim_module._with('globals', powerline_config_path=cfg_path):
exclude = set(('no', 'v', 'V', VBLOCK, 's', 'S', SBLOCK, 'R', 'Rv', 'c', 'cv', 'ce', 'r', 'rm', 'r?', '!'))
try:

View File

@ -787,6 +787,25 @@ class TestVim(TestCase):
self.assertEqual(trailing_whitespace(), None)
self.assertEqual(trailing_whitespace(), None)
def test_tabnr(self):
pl = Pl()
segment_info = vim_module._get_segment_info()
self.assertEqual(vim.tabnr(pl=pl, segment_info=segment_info, show_current=True), '1')
self.assertEqual(vim.tabnr(pl=pl, segment_info=segment_info, show_current=False), None)
def test_single_tab(self):
pl = Pl()
single_tab = partial(vim.single_tab, pl=pl)
with vim_module._with('tabpage'):
self.assertEqual(single_tab(), [{'highlight_group': ['many_tabs'], 'contents': 'Tabs'}])
self.assertEqual(single_tab(single_text='s', multiple_text='m'), [{'highlight_group': ['many_tabs'], 'contents': 'm'}])
self.assertEqual(single_tab(multiple_text='m'), [{'highlight_group': ['many_tabs'], 'contents': 'm'}])
self.assertEqual(single_tab(single_text='s'), [{'highlight_group': ['many_tabs'], 'contents': 'Tabs'}])
self.assertEqual(single_tab(), [{'highlight_group': ['single_tab'], 'contents': 'Bufs'}])
self.assertEqual(single_tab(single_text='s', multiple_text='m'), [{'highlight_group': ['single_tab'], 'contents': 's'}])
self.assertEqual(single_tab(multiple_text='m'), [{'highlight_group': ['single_tab'], 'contents': 'Bufs'}])
self.assertEqual(single_tab(single_text='s'), [{'highlight_group': ['single_tab'], 'contents': 's'}])
old_cwd = None

34
tests/test_tabline.vim Executable file
View File

@ -0,0 +1,34 @@
#!/usr/bin/vim -S
source powerline/bindings/vim/plugin/powerline.vim
edit abc
tabedit def
tabedit ghi
try
let &columns = 80
let result = eval(&tabline[2:])
catch
call writefile(['Exception while evaluating &tabline', v:exception], 'message.fail')
cquit
endtry
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# Tabs '
call writefile(['Unexpected result', result], 'message.fail')
cquit
endif
tabonly!
try
let result = eval(&tabline[2:])
catch
call writefile(['Exception while evaluating &tabline', v:exception], 'message.fail')
cquit
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 '
call writefile(['Unexpected result (2)', result], 'message.fail')
cquit
endif
qall!

View File

@ -2,12 +2,13 @@
_log = []
vars = {}
vvars = {'version': 703}
_window = 0
_tabpage = 0
_mode = 'n'
_buf_purge_events = set()
options = {
'paste': 0,
'ambiwidth': 'single',
'columns': 80,
}
_last_bufnr = 0
_highlights = {}
@ -29,6 +30,12 @@ def _set_thread_id():
_set_thread_id()
def _print_log():
for item in _log:
print (item)
_log[:] = ()
def _vim(func):
from functools import wraps
from threading import current_thread
@ -67,6 +74,10 @@ class _Buffers(object):
def __init__(self):
self.d = {}
@_vim
def __len__(self):
return len(self.d)
@_vim
def __getitem__(self, item):
return self.d[item]
@ -75,38 +86,35 @@ class _Buffers(object):
def __setitem__(self, item, value):
self.d[item] = value
@_vim
def __iter__(self):
return iter(self.d.values())
@_vim
def __contains__(self, item):
return item in self.d
@_vim
def __nonzero__(self):
return bool(self.d)
@_vim
def keys(self):
def _keys(self):
return self.d.keys()
@_vim
def pop(self, *args, **kwargs):
def _pop(self, *args, **kwargs):
return self.d.pop(*args, **kwargs)
buffers = _Buffers()
class _Windows(object):
class _ObjList(object):
@_vim
def __init__(self):
def __init__(self, objtype):
self.l = []
self.objtype = objtype
@_vim
def __getitem__(self, item):
return self.l[item]
@_vim
def __setitem__(self, item, value):
self.l[item] = value
return self.l[item - int(item > 0)]
@_vim
def __len__(self):
@ -117,24 +125,22 @@ class _Windows(object):
return iter(self.l)
@_vim
def __nonzero__(self):
return not not self.l
@_vim
def _pop(self, *args, **kwargs):
return self.l.pop(*args, **kwargs)
def _pop(self, idx):
obj = self.l.pop(idx - 1)
for moved_obj in self.l[idx - 1:]:
moved_obj.number -= 1
return obj
@_vim
def _append(self, *args, **kwargs):
return self.l.append(*args, **kwargs)
windows = _Windows()
@_vim
def _buffer():
return windows[_window - 1].buffer.number
@_vim
def _new(self, *args, **kwargs):
number = len(self) + 1
new_obj = self.objtype(number, *args, **kwargs)
self._append(new_obj)
return new_obj
def _construct_result(r):
@ -227,23 +233,50 @@ def eval(expr):
import re
match = re.match(r'^getwinvar\((\d+), "(\w+)"\)$', expr)
if not match:
raise NotImplementedError
raise NotImplementedError(expr)
winnr = int(match.group(1))
varname = match.group(2)
return _emul_getwinvar(winnr, varname)
elif expr.startswith('has_key('):
import re
match = re.match(r'^has_key\(getwinvar\((\d+), ""\), "(\w+)"\)$', expr)
if not match:
raise NotImplementedError
winnr = int(match.group(1))
varname = match.group(2)
return 0 + (varname in windows[winnr - 1].vars)
if match:
winnr = int(match.group(1))
varname = match.group(2)
return 0 + (varname in current.tabpage.windows[winnr].vars)
else:
match = re.match(r'^has_key\(gettabwinvar\((\d+), (\d+), ""\), "(\w+)"\)$', expr)
if not match:
raise NotImplementedError(expr)
tabnr = int(match.group(1))
winnr = int(match.group(2))
varname = match.group(3)
return 0 + (varname in tabpages[tabnr].windows[winnr].vars)
elif expr == 'getbufvar("%", "NERDTreeRoot").path.str()':
import os
assert os.path.basename(buffers[_buffer()].name).startswith('NERD_tree_')
assert os.path.basename(current.buffer.name).startswith('NERD_tree_')
return '/usr/include'
raise NotImplementedError
elif expr == 'tabpagenr()':
return current.tabpage.number
elif expr == 'tabpagenr("$")':
return len(tabpages)
elif expr.startswith('tabpagewinnr('):
tabnr = int(expr[len('tabpagewinnr('):-1])
return tabpages[tabnr].window.number
elif expr.startswith('tabpagebuflist('):
import re
match = re.match(r'tabpagebuflist\((\d+)\)\[(\d+)\]', expr)
tabnr = int(match.group(1))
winnr = int(match.group(2)) + 1
return tabpages[tabnr].windows[winnr].buffer.number
elif expr.startswith('gettabwinvar('):
import re
match = re.match(r'gettabwinvar\((\d+), (\d+), "(\w+)"\)', expr)
tabnr = int(match.group(1))
winnr = int(match.group(2))
varname = match.group(3)
return tabpages[tabnr].windows[winnr].vars[varname]
raise NotImplementedError(expr)
@_vim
@ -277,7 +310,7 @@ def _emul_getbufvar(bufnr, varname):
import re
if varname[0] == '&':
if bufnr == '%':
bufnr = buffers[_buffer()].number
bufnr = current.buffer.number
if bufnr not in buffers:
return ''
try:
@ -289,7 +322,7 @@ def _emul_getbufvar(bufnr, varname):
return ''
elif re.match('^[a-zA-Z_]+$', varname):
if bufnr == '%':
bufnr = buffers[_buffer()].number
bufnr = current.buffer.number
if bufnr not in buffers:
return ''
return buffers[bufnr].vars[varname]
@ -299,25 +332,25 @@ def _emul_getbufvar(bufnr, varname):
@_vim
@_str_func
def _emul_getwinvar(winnr, varname):
return windows[winnr - 1].vars.get(varname, '')
return current.tabpage.windows[winnr].vars.get(varname, '')
@_vim
def _emul_setwinvar(winnr, varname, value):
windows[winnr - 1].vars[varname] = value
current.tabpage.windows[winnr].vars[varname] = value
@_vim
def _emul_virtcol(expr):
if expr == '.' or isinstance(expr, list):
return windows[_window - 1].cursor[1] + 1
return current.window.cursor[1] + 1
raise NotImplementedError
@_vim
def _emul_getpos(expr):
if expr == '.' or expr == 'v':
return [0, windows[_window - 1].cursor[0] + 1, windows[_window - 1].cursor[1] + 1, 0]
return [0, current.window.cursor[0] + 1, current.window.cursor[1] + 1, 0]
raise NotImplementedError
@ -342,7 +375,7 @@ def _emul_fnamemodify(path, modstring):
def _emul_expand(expr):
global _abuf
if expr == '<abuf>':
return _abuf or _buffer()
return _abuf or current.buffer.number
raise NotImplementedError
@ -362,7 +395,7 @@ def _emul_exists(varname):
@_vim
def _emul_line2byte(line):
buflines = _buf_lines[_buffer()]
buflines = current.buffer._buf_lines
if line == len(buflines) + 1:
return sum((len(s) for s in buflines)) + 1
raise NotImplementedError
@ -370,8 +403,8 @@ def _emul_line2byte(line):
@_vim
def _emul_line(expr):
cursorline = windows[_window - 1].cursor[0] + 1
numlines = len(_buf_lines[_buffer()])
cursorline = current.window.cursor[0] + 1
numlines = len(current.buffer._buf_lines)
if expr == 'w0':
return max(cursorline - 5, 1)
if expr == 'w$':
@ -395,16 +428,15 @@ def _emul_bufname(bufnr):
return b''
_window_ids = [None]
_window_id = 0
class _Window(object):
def __init__(self, buffer=None, cursor=(1, 0), width=80):
def __init__(self, number, buffer=None, cursor=(1, 0), width=80):
global _window_id
self.cursor = cursor
self.width = width
self.number = len(windows) + 1
self.number = number
if buffer:
if type(buffer) is _Buffer:
self.buffer = buffer
@ -412,19 +444,45 @@ class _Window(object):
self.buffer = _Buffer(**buffer)
else:
self.buffer = _Buffer()
windows._append(self)
_window_id += 1
_window_ids.append(_window_id)
self._window_id = _window_id
self.options = {}
self.vars = {}
self.vars = {
'powerline_window_id': self._window_id,
}
def __repr__(self):
return '<window ' + str(self.number - 1) + '>'
_buf_lines = {}
_undostate = {}
_undo_written = {}
class _Tabpage(object):
def __init__(self, number):
self.windows = _ObjList(_Window)
self.number = number
def _new_window(self, **kwargs):
self.window = self.windows._new(**kwargs)
return self.window
def _close_window(self, winnr, open_window=True):
curwinnr = self.window.number
win = self.windows._pop(winnr)
if self.windows and winnr == curwinnr:
self.window = self.windows[-1]
elif open_window:
current.tabpage._new_window()
return win
def _close(self):
while self.windows:
self._close_window(1, False)
tabpages._pop(self.number)
_tabpage = len(tabpages)
tabpages = _ObjList(_Tabpage)
_abuf = None
@ -446,10 +504,9 @@ class _Buffer(object):
'fileencoding': 'utf-8',
'textwidth': 80,
}
_buf_lines[bufnr] = ['']
from copy import copy
_undostate[bufnr] = [copy(_buf_lines[bufnr])]
_undo_written[bufnr] = len(_undostate[bufnr])
self._buf_lines = ['']
self._undostate = [self._buf_lines[:]]
self._undo_written = len(self._undostate)
buffers[bufnr] = self
@property
@ -471,27 +528,27 @@ class _Buffer(object):
self._name = os.path.abspath(name)
def __getitem__(self, line):
return _buf_lines[self.number][line]
return self._buf_lines[line]
def __setitem__(self, line, value):
self.options['modified'] = 1
self.vars['changedtick'] += 1
_buf_lines[self.number][line] = value
self._buf_lines[line] = value
from copy import copy
_undostate[self.number].append(copy(_buf_lines[self.number]))
self._undostate.append(copy(self._buf_lines))
def __setslice__(self, *args):
self.options['modified'] = 1
self.vars['changedtick'] += 1
_buf_lines[self.number].__setslice__(*args)
self._buf_lines.__setslice__(*args)
from copy import copy
_undostate[self.number].append(copy(_buf_lines[self.number]))
self._undostate.append(copy(self._buf_lines))
def __getslice__(self, *args):
return _buf_lines[self.number].__getslice__(*args)
return self._buf_lines.__getslice__(*args)
def __len__(self):
return len(_buf_lines[self.number])
return len(self._buf_lines)
def __repr__(self):
return '<buffer ' + str(self.name) + '>'
@ -514,22 +571,20 @@ class _Buffer(object):
exec(event, __main__.__dict__)
finally:
_abuf = None
if _buf_lines:
_buf_lines.pop(bufnr)
if _undostate:
_undostate.pop(bufnr)
if _undo_written:
_undo_written.pop(bufnr)
class _Current(object):
@property
def buffer(self):
return buffers[_buffer()]
return self.window.buffer
@property
def window(self):
return windows[_window - 1]
return self.tabpage.window
@property
def tabpage(self):
return tabpages[_tabpage - 1]
current = _Current()
@ -549,7 +604,7 @@ def _init():
for varname, value in globals().items():
if varname[0] != '_':
_dict[varname] = value
_new()
_tabnew()
return _dict
@ -561,12 +616,17 @@ def _get_segment_info():
}
mode = _mode
mode = mode_translations.get(mode, mode)
window = current.window
buffer = current.buffer
tabpage = current.tabpage
return {
'window': windows[_window - 1],
'winnr': _window,
'buffer': buffers[_buffer()],
'bufnr': _buffer(),
'window_id': _window_ids[_window],
'window': window,
'winnr': window.number,
'buffer': buffer,
'bufnr': buffer.number,
'tabpage': tabpage,
'tabnr': tabpage.number,
'window_id': window._window_id,
'mode': mode,
}
@ -588,85 +648,78 @@ def _start_mode(mode):
@_vim
def _undo():
if len(_undostate[_buffer()]) == 1:
if len(current.buffer._undostate) == 1:
return
_undostate[_buffer()].pop(-1)
_buf_lines[_buffer()] = _undostate[_buffer()][-1]
buf = current.buffer
if _undo_written[_buffer()] == len(_undostate[_buffer()]):
buf.options['modified'] = 0
buffer = current.buffer
buffer._undostate.pop(-1)
buffer._buf_lines = buffer._undostate[-1]
if buffer._undo_written == len(buffer._undostate):
buffer.options['modified'] = 0
@_vim
def _edit(name=None):
global _last_bufnr
if _buffer() and buffers[_buffer()].name is None:
buf = buffers[_buffer()]
buf.name = name
if current.buffer.name is None:
buffer = current.buffer
buffer.name = name
else:
buf = _Buffer(name)
windows[_window - 1].buffer = buf
buffer = _Buffer(name)
current.window.buffer = buffer
@_vim
def _tabnew(name=None):
global windows
tabpage = tabpages._new()
windows = tabpage.windows
_tabpage = len(tabpages)
_new(name)
return tabpage
@_vim
def _new(name=None):
global _window
_Window(buffer={'name': name})
_window = len(windows)
current.tabpage._new_window(buffer={'name': name})
@_vim
def _split():
global _window
_Window(buffer=buffers[_buffer()])
_window = len(windows)
@_vim
def _del_window(winnr):
win = windows._pop(winnr - 1)
_window_ids.pop(winnr)
return win
current.tabpage._new_window(buffer=current.buffer)
@_vim
def _close(winnr, wipe=True):
global _window
win = _del_window(winnr)
if _window == winnr:
_window = len(windows)
win = current.tabpage._close_window(winnr)
if wipe:
for w in windows:
for w in current.tabpage.windows:
if w.buffer.number == win.buffer.number:
break
else:
_bw(win.buffer.number)
if not windows:
_Window()
@_vim
def _bw(bufnr=None):
bufnr = bufnr or _buffer()
bufnr = bufnr or current.buffer.number
winnr = 1
for win in windows:
for win in current.tabpage.windows:
if win.buffer.number == bufnr:
_close(winnr, wipe=False)
winnr += 1
buffers.pop(bufnr)
buffers._pop(bufnr)
if not buffers:
_Buffer()
_b(max(buffers.keys()))
_b(max(buffers._keys()))
@_vim
def _b(bufnr):
windows[_window - 1].buffer = buffers[bufnr]
current.window.buffer = buffers[bufnr]
@_vim
def _set_cursor(line, col):
windows[_window - 1].cursor = (line, col)
current.window.cursor = (line, col)
if _mode == 'n':
_launch_event('CursorMoved')
elif _mode == 'i':
@ -675,12 +728,12 @@ def _set_cursor(line, col):
@_vim
def _get_buffer():
return buffers[_buffer()]
return current.buffer
@_vim
def _set_bufoption(option, value, bufnr=None):
buffers[bufnr or _buffer()].options[option] = value
buffers[bufnr or current.buffer.number].options[option] = value
if option == 'filetype':
_launch_event('FileType')
@ -691,7 +744,7 @@ class _WithNewBuffer(object):
def __enter__(self):
self.call()
self.bufnr = _buffer()
self.bufnr = current.buffer.number
return _get_segment_info()
def __exit__(self, *args):
@ -720,7 +773,7 @@ class _WithBufOption(object):
self.new = new
def __enter__(self):
self.buffer = buffers[_buffer()]
self.buffer = current.buffer
self.old = _set_dict(self.buffer.options, self.new, _set_bufoption)[0]
def __exit__(self, *args):
@ -768,7 +821,7 @@ class _WithBufName(object):
def __enter__(self):
import os
buffer = buffers[_buffer()]
buffer = current.buffer
self.buffer = buffer
self.old = buffer.name
buffer.name = self.new
@ -780,6 +833,18 @@ class _WithBufName(object):
self.buffer.name = self.old
class _WithNewTabPage(object):
def __init__(self, *args, **kwargs):
self.args = args
self.kwargs = kwargs
def __enter__(self):
self.tab = _tabnew(*self.args, **self.kwargs)
def __exit__(self, *args):
self.tab._close()
@_vim
def _with(key, *args, **kwargs):
if key == 'buffer':
@ -795,11 +860,13 @@ def _with(key, *args, **kwargs):
elif key == 'globals':
return _WithDict(vars, **kwargs)
elif key == 'wvars':
return _WithDict(windows[_window - 1].vars, **kwargs)
return _WithDict(current.window.vars, **kwargs)
elif key == 'environ':
return _WithDict(_environ, **kwargs)
elif key == 'split':
return _WithSplit()
elif key == 'tabpage':
return _WithNewTabPage(*args, **kwargs)
class error(Exception):