Merge pull request #1144 from ZyX-I/vim-plugins

Add support for csv and capslock and Command-T vim plugins
This commit is contained in:
Nikolai Aleksandrovich Pavlov 2014-11-10 01:19:08 +03:00
commit a6ba63a8e5
13 changed files with 432 additions and 20 deletions

View File

@ -21,6 +21,12 @@ Ctrl-P segments
.. automodule:: powerline.segments.vim.plugin.ctrlp
:members:
Command-T segments
------------------
.. automodule:: powerline.segments.vim.plugin.commandt
:members:
Tagbar segments
---------------
@ -32,3 +38,9 @@ NERDTree segments
.. automodule:: powerline.segments.vim.plugin.nerdtree
:members:
Capslock segments
-----------------
.. automodule:: powerline.segments.vim.plugin.capslock
:members:

View File

@ -124,6 +124,22 @@ else:
vim_get_func = VimFunc
if hasattr(vim, 'Function'):
def vim_func_exists(f):
try:
vim.Function(f)
except ValueError:
return False
else:
return True
else:
def vim_func_exists(f):
try:
return bool(int(vim.eval('type(function("{0}")) == 2'.format(f))))
except vim.error:
return False
if type(vim) is object:
vim_get_func = lambda *args, **kwargs: None
@ -398,3 +414,16 @@ def on_bwipe():
environ = VimEnviron()
def create_ruby_dpowerline():
vim.command((
'''
ruby
if $powerline == nil
class Powerline
end
$powerline = Powerline.new
end
'''
))

View File

@ -18,6 +18,10 @@
"bufnr": "file_directory",
"winnr": "information:unimportant",
"tabnr": "file_directory",
"capslock_indicator": "paste_indicator",
"csv:column_number": "line_current",
"csv:column_name": "line_current_symbol",
"tab_nc:file_directory": "information:unimportant",
"tab_nc:file_name": "tab_nc:file_directory",
@ -26,6 +30,11 @@
"buf_nc:file_directory": "tab_nc:file_directory",
"buf_nc:file_name": "tab_nc:file_name",
"buf_nc:bufnr": "tab_nc:tabnr",
"buf_nc:modified_indicator": "tab_nc:modified_indicator"
"buf_nc:modified_indicator": "tab_nc:modified_indicator",
"commandt:label": "file_name",
"commandt:background": "background",
"commandt:finder": "file_name",
"commandt:path": "file_directory"
}
}

View File

@ -36,6 +36,7 @@
"powerline.matchers.vim.plugin.nerdtree.nerdtree": "plugin_nerdtree",
"powerline.matchers.vim.plugin.ctrlp.ctrlp": "plugin_ctrlp",
"powerline.matchers.vim.plugin.commandt.commandt": "plugin_commandt",
"powerline.matchers.vim.plugin.gundo.gundo": "plugin_gundo",
"powerline.matchers.vim.plugin.gundo.gundo_preview": "plugin_gundo-preview"
}

View File

@ -15,6 +15,11 @@
"exclude_modes": ["nc"],
"priority": 10
},
{
"function": "powerline.segments.vim.plugin.capslock.capslock_indicator",
"include_modes": ["i", "R", "Rv"],
"priority": 10
},
{
"function": "branch",
"exclude_modes": ["nc"],
@ -95,6 +100,10 @@
"width": 4,
"align": "r"
},
{
"function": "csv_col_current",
"priority": 30
},
{
"type": "string",
"name": "line_current_symbol",

View File

@ -0,0 +1,26 @@
{
"segments": {
"left": [
{
"type": "string",
"contents": "Command-T",
"highlight_group": ["commandt:label"]
},
{
"function": "powerline.segments.vim.plugin.commandt.finder"
},
{
"function": "powerline.segments.vim.plugin.commandt.path"
},
{
"type": "string",
"highlight_group": ["commandt:background"],
"draw_soft_divider": false,
"draw_hard_divider": false,
"width": "auto"
}
],
"right": [
]
}
}

View File

@ -0,0 +1,11 @@
# vim:fileencoding=utf-8:noet
from __future__ import (unicode_literals, division, absolute_import, print_function)
import os
from powerline.bindings.vim import buffer_name
def commandt(matcher_info):
name = buffer_name(matcher_info)
return name and os.path.basename(name) == b'GoToFile'

View File

@ -3,6 +3,8 @@ from __future__ import (unicode_literals, division, absolute_import, print_funct
import os
import re
import csv
import sys
from collections import defaultdict
@ -22,6 +24,7 @@ from powerline.lib.humanize_bytes import humanize_bytes
from powerline.lib import wraps_saveargs as wraps
from powerline.segments.common.vcs import BranchSegment
from powerline.segments import with_docstring
from powerline.lib.unicode import string, unicode
try:
from __builtin__ import xrange as range
@ -627,3 +630,118 @@ def winnr(pl, segment_info, show_current=True):
winnr = segment_info['winnr']
if show_current or winnr != vim.current.window.number:
return str(winnr)
csv_cache = None
sniffer = csv.Sniffer()
def detect_text_csv_dialect(text, display_name, header_text=None):
return (
sniffer.sniff(string(text)),
sniffer.has_header(string(header_text or text)) if display_name == 'auto' else display_name,
)
CSV_SNIFF_LINES = 100
CSV_PARSE_LINES = 10
if sys.version_info < (2, 7):
def read_csv(l, dialect, fin=next):
try:
return fin(csv.reader(l, dialect))
except csv.Error as e:
if str(e) == 'newline inside string' and dialect.quotechar:
# Maybe we are inside an unfinished quoted string. Python-2.6
# does not handle this fine
return fin(csv.reader(l[:-1] + [l[-1] + dialect.quotechar]))
else:
raise
else:
def read_csv(l, dialect, fin=next):
return fin(csv.reader(l, dialect))
def process_csv_buffer(pl, buffer, line, col, display_name):
global csv_cache
if csv_cache is None:
csv_cache = register_buffer_cache(defaultdict(lambda: (None, None, None)))
try:
cur_first_line = buffer[0]
except UnicodeDecodeError:
cur_first_line = vim.eval('strtrans(getline(1))')
dialect, has_header, first_line = csv_cache[buffer.number]
if dialect is None or (cur_first_line != first_line and display_name == 'auto'):
try:
text = '\n'.join(buffer[:CSV_SNIFF_LINES])
except UnicodeDecodeError: # May happen in Python 3
text = vim.eval('join(map(getline(1, {0}), "strtrans(v:val)"), "\\n")'.format(CSV_SNIFF_LINES))
try:
dialect, has_header = detect_text_csv_dialect(text, display_name)
except csv.Error as e:
pl.warn('Failed to detect csv format: {0}', str(e))
# Try detecting using three lines only:
if line == 1:
rng = (0, line + 2)
elif line == len(buffer):
rng = (line - 3, line)
else:
rng = (line - 2, line + 1)
try:
dialect, has_header = detect_text_csv_dialect(
'\n'.join(buffer[rng[0]:rng[1]]),
display_name,
header_text='\n'.join(buffer[:4]),
)
except csv.Error as e:
pl.error('Failed to detect csv format: {0}', str(e))
return None, None
if len(buffer) > 2:
csv_cache[buffer.number] = dialect, has_header, cur_first_line
column_number = len(read_csv(
buffer[max(0, line - CSV_PARSE_LINES):line - 1] + [buffer[line - 1][:col]],
dialect=dialect,
fin=list,
)[-1]) or 1
if has_header:
try:
header = read_csv(buffer[0:1], dialect=dialect)
except UnicodeDecodeError:
header = read_csv([vim.eval('strtrans(getline(1))')], dialect=dialect)
column_name = header[column_number - 1]
else:
column_name = None
return unicode(column_number), column_name
@requires_segment_info
def csv_col_current(pl, segment_info, display_name='auto', name_format=' ({column_name:.15})'):
'''Display CSV column number and column name
Requires filetype to be set to ``csv``.
:param bool or str name:
May be ``True``, ``False`` and ``"auto"``. In the first case value from
the first raw will always be displayed. In the second case it will never
be displayed. In thi last case ``csv.Sniffer().has_header()`` will be
used to detect whether current file contains header in the first column.
:param str name_format:
String used to format column name (in case ``display_name`` is set to
``True`` or ``"auto"``). Accepts ``column_name`` keyword argument.
Highlight groups used: ``csv:column_number`` or ``csv``, ``csv:column_name`` or ``csv``.
'''
if vim_getbufoption(segment_info, 'filetype') != 'csv':
return None
line, col = segment_info['window'].cursor
column_number, column_name = process_csv_buffer(pl, segment_info['buffer'], line, col, display_name)
if not column_number:
return None
return [{
'contents': column_number,
'highlight_group': ['csv:column_number', 'csv'],
}] + ([{
'contents': name_format.format(column_name=column_name),
'highlight_group': ['csv:column_name', 'csv'],
}] if column_name else [])

View File

@ -0,0 +1,30 @@
# vim:fileencoding=utf-8:noet
from __future__ import (unicode_literals, division, absolute_import, print_function)
try:
import vim
except ImportError:
vim = object()
from powerline.bindings.vim import vim_func_exists
from powerline.theme import requires_segment_info
@requires_segment_info
def capslock_indicator(pl, segment_info, text='CAPS'):
'''Shows the indicator if tpope/vim-capslock plugin is enabled
.. _note::
In the current state plugin automatically disables itself when leaving
insert mode. So trying to use this segment not in insert or replace
modes is useless.
:param str text:
String to show when software capslock presented by this plugin is
active.
'''
if not vim_func_exists('CapsLockStatusline'):
return None
# CapsLockStatusline() function returns an empty string when plugin is
# disabled. If it is not then string is non-empty.
return text if vim.eval('CapsLockStatusline()') else None

View File

@ -0,0 +1,89 @@
# vim:fileencoding=utf-8:noet
from __future__ import (unicode_literals, division, absolute_import, print_function)
try:
import vim
except ImportError:
vim = object()
from powerline.bindings.vim import create_ruby_dpowerline
def initialize():
global initialized
if initialized:
return
initialized = True
create_ruby_dpowerline()
vim.command((
# When using :execute (vim.command uses the same code) one should not
# use << EOF.
'''
ruby
if (not ($command_t.respond_to? 'active_finder'))
def $command_t.active_finder
@active_finder and @active_finder.class.name or ''
end
end
if (not ($command_t.respond_to? 'path'))
def $command_t.path
@path or ''
end
end
def $powerline.commandt_set_active_finder
::VIM::command "let g:powerline_commandt_reply = '#{$command_t.active_finder}'"
end
def $powerline.commandt_set_path
::VIM::command "let g:powerline_commandt_reply = '#{($command_t.path or '').gsub(/'/, "''")}'"
end
'''
))
initialized = False
def finder(pl):
'''Display Command-T finder name
Requires $command_t.active_finder and methods (code above may monkey-patch
$command_t to add them). All Command-T finders have ``CommandT::`` module
prefix, but it is stripped out (actually, any ``CommandT::`` substring will
be stripped out).
'''
initialize()
vim.command('ruby $powerline.commandt_set_active_finder')
return [{
'highlight_group': ['commandt:finder'],
'contents': vim.eval('g:powerline_commandt_reply').replace('CommandT::', '')
}]
FINDERS_WITHOUT_PATH = set((
'CommandT::MRUBufferFinder',
'CommandT::BufferFinder',
'CommandT::TagFinder',
'CommandT::JumpFinder',
))
def path(pl):
'''Display path used by Command-T
Requires $command_t.active_finder and .path methods (code above may
monkey-patch $command_t to add them).
$command_t.active_finder is required in order to omit displaying path for
finders ``MRUBufferFinder``, ``BufferFinder``, ``TagFinder`` and
``JumpFinder`` (pretty much any finder, except ``FileFinder``).
'''
initialize()
vim.command('ruby $powerline.commandt_set_active_finder')
finder = vim.eval('g:powerline_commandt_reply')
if finder in FINDERS_WITHOUT_PATH:
return None
vim.command('ruby $powerline.commandt_set_path')
return [{
'highlight_group': ['commandt:path'],
'contents': vim.eval('g:powerline_commandt_reply')
}]

View File

@ -55,12 +55,15 @@ class TestVimConfig(TestCase):
(('bufname', '__Gundo__'), {}),
(('bufname', '__Gundo_Preview__'), {}),
(('bufname', 'ControlP'), {}),
# No Command-T tests here: requires +ruby or emulation
# No tabline here: tablines are tested separately
)
with open(os.path.join(cfg_path, 'config.json'), 'r') as f:
local_themes_raw = json.load(f)['ext']['vim']['local_themes']
# Dont 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) - 1)
# See end of the buffers definition above for `- 2`
self.assertEqual(len(buffers), len(local_themes) - 2)
outputs = {}
i = 0

View File

@ -1196,6 +1196,75 @@ class TestVim(TestCase):
'highlight_group': ['tab_modified_indicator', 'modified_indicator'],
}])
def test_csv_col_current(self):
pl = Pl()
segment_info = vim_module._get_segment_info()
def csv_col_current(**kwargs):
self.vim.csv_cache and self.vim.csv_cache.clear()
return self.vim.csv_col_current(pl=pl, segment_info=segment_info, **kwargs)
buffer = segment_info['buffer']
try:
self.assertEqual(csv_col_current(), None)
buffer.options['filetype'] = 'csv'
self.assertEqual(csv_col_current(), None)
buffer[:] = ['1;2;3', '4;5;6']
vim_module._set_cursor(1, 1)
self.assertEqual(csv_col_current(), [{
'contents': '1', 'highlight_group': ['csv:column_number', 'csv']
}])
vim_module._set_cursor(2, 3)
self.assertEqual(csv_col_current(), [{
'contents': '2', 'highlight_group': ['csv:column_number', 'csv']
}])
vim_module._set_cursor(2, 3)
self.assertEqual(csv_col_current(display_name=True), [{
'contents': '2', 'highlight_group': ['csv:column_number', 'csv']
}, {
'contents': ' (2)', 'highlight_group': ['csv:column_name', 'csv']
}])
buffer[:0] = ['Foo;Bar;Baz']
vim_module._set_cursor(2, 3)
self.assertEqual(csv_col_current(), [{
'contents': '2', 'highlight_group': ['csv:column_number', 'csv']
}, {
'contents': ' (Bar)', 'highlight_group': ['csv:column_name', 'csv']
}])
if sys.version_info < (2, 7):
raise SkipTest('csv module in Python-2.6 does not handle multiline csv files well')
buffer[len(buffer):] = ['1;"bc', 'def', 'ghi', 'jkl";3']
vim_module._set_cursor(5, 1)
self.assertEqual(csv_col_current(), [{
'contents': '2', 'highlight_group': ['csv:column_number', 'csv']
}, {
'contents': ' (Bar)', 'highlight_group': ['csv:column_name', 'csv']
}])
vim_module._set_cursor(7, 6)
self.assertEqual(csv_col_current(), [{
'contents': '3', 'highlight_group': ['csv:column_number', 'csv']
}, {
'contents': ' (Baz)', 'highlight_group': ['csv:column_name', 'csv']
}])
self.assertEqual(csv_col_current(name_format=' ({column_name:.1})'), [{
'contents': '3', 'highlight_group': ['csv:column_number', 'csv']
}, {
'contents': ' (B)', 'highlight_group': ['csv:column_name', 'csv']
}])
self.assertEqual(csv_col_current(display_name=True, name_format=' ({column_name:.1})'), [{
'contents': '3', 'highlight_group': ['csv:column_number', 'csv']
}, {
'contents': ' (B)', 'highlight_group': ['csv:column_name', 'csv']
}])
self.assertEqual(csv_col_current(display_name=False, name_format=' ({column_name:.1})'), [{
'contents': '3', 'highlight_group': ['csv:column_number', 'csv']
}])
self.assertEqual(csv_col_current(display_name=False), [{
'contents': '3', 'highlight_group': ['csv:column_number', 'csv']
}])
finally:
vim_module._bw(segment_info['bufnr'])
@classmethod
def setUpClass(cls):
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 'path')))

View File

@ -288,6 +288,12 @@ def eval(expr):
winnr = int(match.group(2))
varname = match.group(3)
return tabpages[tabnr].windows[winnr].vars[varname]
elif expr.startswith('type(function('):
import re
match = re.match(r'^type\(function\("([^"]+)"\)\) == 2$', expr)
if not match:
raise NotImplementedError(expr)
return 0
raise NotImplementedError(expr)