Add support for csv files
Equivalent to airline csv Christian Brabandt plugin integration, but uses Python own facilities. Note: filetype detection is out of the scope of the powerline responsibilities. CSV is supported by powerline, but only as long as filetype detection is done by Vim correctly. By default CSV files are neither detected nor highlighted. Some known differences between powerline code and @chrisbra plugin with corresponding airline integration: 1. Detection work differently. I cannot say which is better because I have not tested it much, but it is definitely different. 2. My variant is able to detect whether there is (no) header. (Of course, relying on Python code.) Airline is using a setting. (Both do not allow manual per-filetype setting.) Of course, user can force either variant (no headers at all or headers always). 3. My variant makes it possible to configure header output format, including truncating it at 15 characters (the default). 4. CSV plugin does not work with multiline CSV items (in some dialects one can use code like `abc;"I<CR>am<CR>multiline<CR>string"`). See `:h csv-column` on how to fix this if possible (will require also changing `<sid>WColumn` function because currently it only works with one line). 5. AFAIK Python does not such a thing as “fixed width CSV”. Thus I do not work with this, but CSV plugin does. Not sure whether it is different with space-separated CSV files though.
This commit is contained in:
parent
28435f05d2
commit
d392cf3322
|
@ -19,6 +19,9 @@
|
|||
"winnr": "information:unimportant",
|
||||
"tabnr": "file_directory",
|
||||
|
||||
"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",
|
||||
"tab_nc:tabnr": "tab_nc:file_directory",
|
||||
|
|
|
@ -95,6 +95,10 @@
|
|||
"width": 4,
|
||||
"align": "r"
|
||||
},
|
||||
{
|
||||
"function": "csv_col_current",
|
||||
"priority": 30
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "line_current_symbol",
|
||||
|
|
|
@ -5,6 +5,9 @@ import os
|
|||
import re
|
||||
|
||||
from collections import defaultdict
|
||||
from csv import Sniffer, reader
|
||||
|
||||
from csv import Error as CSVError
|
||||
|
||||
try:
|
||||
import vim
|
||||
|
@ -22,6 +25,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 +631,99 @@ 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 = 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
|
||||
|
||||
|
||||
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 CSVError 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 CSVError 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(list(reader(
|
||||
buffer[max(0, line - CSV_PARSE_LINES):line - 1] + [buffer[line - 1][:col]], dialect=dialect))[-1]) or 1
|
||||
if has_header:
|
||||
try:
|
||||
header = next(reader(buffer[0:1], dialect=dialect))
|
||||
except UnicodeDecodeError:
|
||||
header = next(reader([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 [])
|
||||
|
|
|
@ -22,19 +22,19 @@ def initialize():
|
|||
ruby
|
||||
if (not ($command_t.respond_to? 'active_finder'))
|
||||
def $command_t.active_finder
|
||||
@active_finder.class.name
|
||||
@active_finder and @active_finder.class.name or ''
|
||||
end
|
||||
end
|
||||
if (not ($command_t.respond_to? 'path'))
|
||||
def $command_t.path
|
||||
@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.gsub(/'/, "''")}'"
|
||||
::VIM::command "let g:powerline_commandt_reply = '#{($command_t.path or '').gsub(/'/, "''")}'"
|
||||
end
|
||||
'''
|
||||
))
|
||||
|
|
|
@ -1196,6 +1196,73 @@ 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']
|
||||
}])
|
||||
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')))
|
||||
|
|
Loading…
Reference in New Issue