From 4422e1fbb74a85fd7167d9506c837cdfd09311ee Mon Sep 17 00:00:00 2001 From: Vincent Driessen Date: Fri, 20 Aug 2010 10:27:41 +0200 Subject: Implemented the "reflow" command. For example, if you have the following table in your source file: Type Message Foo I like bananas very much. Even so much that I adore them. Position your cursor in it and call ReformatTable(). This makes the following: +======+===========================================================+ | Type | Message | +======+===========================================================+ | Foo | I like bananas very much. Even so much that I adore them. | +------+-----------------------------------------------------------+ But if you don't like these kinds of long wrappings, you can now simply remove (or add, for that matter) some of the '='-signs from the top row, like this: +======+================================+ | Type | Message | +======+===========================================================+ | Foo | I like bananas very much. Even so much that I adore them. | +------+-----------------------------------------------------------+ And then, call ReflowTable(): +======+================================+ | Type | Message | +======+================================+ | Foo | I like bananas very much. Even | | | so much that I adore them. | +------+--------------------------------+ This fixed issue #1. --- ftplugin/rst_tables.vim | 52 ++++++++++++++++++++++++++++++++++++++--- src/base.vim | 5 +++- src/rst_tables.py | 47 +++++++++++++++++++++++++++++++++++-- tests/fixtures/reflow.txt | 14 +++++++++++ tests/test_rst_tables.py | 59 ++++++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 168 insertions(+), 9 deletions(-) create mode 100644 tests/fixtures/reflow.txt diff --git a/ftplugin/rst_tables.vim b/ftplugin/rst_tables.vim index 1077557..ae0979a 100644 --- a/ftplugin/rst_tables.vim +++ b/ftplugin/rst_tables.vim @@ -19,6 +19,7 @@ let loaded_rst_tables_ftplugin = 1 python << endpython import vim import re +import textwrap from vim_bridge import bridged @@ -191,6 +192,24 @@ def get_column_widths(table): return widths +def get_column_widths_from_border_spec(slice): + border = None + for row in slice: + if line_is_separator(row): + border = row.strip() + break + + if border is None: + raise RuntimeError('Cannot reflow this table. Top table border not found.') + + left = right = None + if border[0] == '+': + left = 1 + if border[-1] == '+': + right = -1 + return map(lambda drawing: max(0, len(drawing) - 2), border[left:right].split('+')) + + def pad_fields(row, widths): """Pads fields of the given row, so each field lines up nicely with the others. @@ -207,11 +226,22 @@ def pad_fields(row, widths): return new_row -def draw_table(table): +def reflow_row_contents(row, widths): + new_row = [] + for i, field in enumerate(row): + wrapped_lines = textwrap.wrap(field.replace('\n', ' '), widths[i]) + new_row.append("\n".join(wrapped_lines)) + return new_row + + +def draw_table(table, manual_widths=None): if table == []: return [] - col_widths = get_column_widths(table) + if manual_widths is None: + col_widths = get_column_widths(table) + else: + col_widths = manual_widths # Reserve room for the spaces sep_col_widths = map(lambda x: x + 2, col_widths) @@ -222,6 +252,9 @@ def draw_table(table): first = True for row in table: + if manual_widths: + row = reflow_row_contents(row, manual_widths) + row_lines = split_row_into_lines(row) # draw the lines (num_lines) for this row @@ -247,12 +280,25 @@ def reformat_table(): slice = draw_table(table) vim.current.buffer[upper - 1:lower] = slice + +@bridged +def reflow_table(): + upper, lower = get_table_bounds() + slice = vim.current.buffer[upper - 1:lower] + widths = get_column_widths_from_border_spec(slice) + table = parse_table(slice) + slice = draw_table(table, widths) + vim.current.buffer[upper - 1:lower] = slice + endpython " Add mappings, unless the user didn't want this. " The default mapping is registered, unless the user remapped it already. if !exists("no_plugin_maps") && !exists("no_rst_table_maps") if !hasmapto('ReformatTable(') - noremap f :call ReformatTable() + noremap c :call ReformatTable() + endif + if !hasmapto('ReflowTable(') + noremap f :call ReflowTable() endif endif diff --git a/src/base.vim b/src/base.vim index 46bdb05..037a9dd 100644 --- a/src/base.vim +++ b/src/base.vim @@ -24,6 +24,9 @@ endpython " The default mapping is registered, unless the user remapped it already. if !exists("no_plugin_maps") && !exists("no_rst_table_maps") if !hasmapto('ReformatTable(') - noremap f :call ReformatTable() + noremap c :call ReformatTable() + endif + if !hasmapto('ReflowTable(') + noremap f :call ReflowTable() endif endif diff --git a/src/rst_tables.py b/src/rst_tables.py index f689b35..1d4b04b 100644 --- a/src/rst_tables.py +++ b/src/rst_tables.py @@ -1,5 +1,6 @@ import vim import re +import textwrap from vim_bridge import bridged @@ -172,6 +173,24 @@ def get_column_widths(table): return widths +def get_column_widths_from_border_spec(slice): + border = None + for row in slice: + if line_is_separator(row): + border = row.strip() + break + + if border is None: + raise RuntimeError('Cannot reflow this table. Top table border not found.') + + left = right = None + if border[0] == '+': + left = 1 + if border[-1] == '+': + right = -1 + return map(lambda drawing: max(0, len(drawing) - 2), border[left:right].split('+')) + + def pad_fields(row, widths): """Pads fields of the given row, so each field lines up nicely with the others. @@ -188,11 +207,22 @@ def pad_fields(row, widths): return new_row -def draw_table(table): +def reflow_row_contents(row, widths): + new_row = [] + for i, field in enumerate(row): + wrapped_lines = textwrap.wrap(field.replace('\n', ' '), widths[i]) + new_row.append("\n".join(wrapped_lines)) + return new_row + + +def draw_table(table, manual_widths=None): if table == []: return [] - col_widths = get_column_widths(table) + if manual_widths is None: + col_widths = get_column_widths(table) + else: + col_widths = manual_widths # Reserve room for the spaces sep_col_widths = map(lambda x: x + 2, col_widths) @@ -203,6 +233,9 @@ def draw_table(table): first = True for row in table: + if manual_widths: + row = reflow_row_contents(row, manual_widths) + row_lines = split_row_into_lines(row) # draw the lines (num_lines) for this row @@ -227,3 +260,13 @@ def reformat_table(): table = parse_table(slice) slice = draw_table(table) vim.current.buffer[upper - 1:lower] = slice + + +@bridged +def reflow_table(): + upper, lower = get_table_bounds() + slice = vim.current.buffer[upper - 1:lower] + widths = get_column_widths_from_border_spec(slice) + table = parse_table(slice) + slice = draw_table(table, widths) + vim.current.buffer[upper - 1:lower] = slice diff --git a/tests/fixtures/reflow.txt b/tests/fixtures/reflow.txt new file mode 100644 index 0000000..f1dbc1e --- /dev/null +++ b/tests/fixtures/reflow.txt @@ -0,0 +1,14 @@ +This is paragraph text *before* the table. + ++==========+==========================+ +| Column 1 | Column 2 | ++==========+================================================================================================+ +| Foo | Put two (or more) spaces as a field separator. | ++----------+------------------------------------------------------------------------------------------------+ +| Bar | Even very very long lines like these are fine, as long as you do not put in line endings here. | ++----------+------------------------------------------------------------------------------------------------+ +| Qux | This is the last line. | ++----------+------------------------------------------------------------------------------------------------+ + +This is paragraph text *after* the table, with +a line ending. diff --git a/tests/test_rst_tables.py b/tests/test_rst_tables.py index 7d72e25..a86e401 100644 --- a/tests/test_rst_tables.py +++ b/tests/test_rst_tables.py @@ -21,9 +21,10 @@ import unittest # Load test subjects from rst_tables import get_table_bounds, reformat_table, parse_table, \ - draw_table, table_line, get_column_widths, \ - pad_fields, unify_table, join_rows, \ - partition_raw_lines, split_row_into_lines + reflow_table, draw_table, table_line, get_column_widths, \ + get_column_widths_from_border_spec, pad_fields, unify_table, \ + join_rows, partition_raw_lines, split_row_into_lines, \ + reflow_row_contents class TestRSTTableFormatter(unittest.TestCase): @@ -210,6 +211,13 @@ class TestRSTTableFormatter(unittest.TestCase): get_column_widths([['Foo\nBar\nQux', 'This\nis\nreally\nneat!']])) + def testGetColumnWidthsFromBorderSpec(self): + input = ['+====+=====+==+=======+', + '| xx | xxx | | xxxxx |', + '+====+=====+==+=======+'] + self.assertEquals([2, 3, 0, 5], + get_column_widths_from_border_spec(input)) + def testPadFields(self): table = [['Name', 'Type', 'Description'], ['Lollypop', 'Candy', 'Yummy'], @@ -222,6 +230,23 @@ class TestRSTTableFormatter(unittest.TestCase): for input, expect in zip(table, expected_padding): self.assertEquals(expect, pad_fields(input, widths)) + def testReflowRowContentsWithEnoughWidth(self): + input = ['Foo\nbar', 'This line\nis spread\nout over\nfour lines.'] + expect = ['Foo bar', 'This line is spread out over four lines.'] + self.assertEquals(expect, reflow_row_contents(input, [99,99])) + + def testReflowRowContentsWithWrapping(self): + input = ['Foo\nbar', 'This line\nis spread\nout over\nfour lines.'] + expect = ['Foo bar', 'This line is spread\nout over four lines.'] + self.assertEquals(expect, reflow_row_contents(input, [10,20])) + + input = ['Foo\nbar', 'This line\nis spread\nout over\nfour lines.'] + expect = ['Foo bar', 'This\nline\nis\nspread\nout\nover\nfour\nlines.'] + self.assertEquals(expect, reflow_row_contents(input, [10,6])) + + def testReflowRowContentsWithoutRoom(self): + #self.assertEquals(expect, reflow_row_contents(input)) + pass def testDrawTable(self): self.assertEquals([], draw_table([])) @@ -236,6 +261,7 @@ class TestRSTTableFormatter(unittest.TestCase): '+-----+----+'], draw_table([['Foo', 'Mu'], ['x', 'y']])) + def testCreateTable(self): self.load_fixture_in_vim('default') expect = """\ @@ -278,3 +304,30 @@ a line ending. +----------------+---------------------------------------------------------------+ """.rstrip().split('\n') self.assertEquals(expect, draw_table(parse_table(raw_lines))) + + def testReflowTable(self): + self.load_fixture_in_vim('reflow') + expect = """\ +This is paragraph text *before* the table. + ++==========+==========================+ +| Column 1 | Column 2 | ++==========+==========================+ +| Foo | Put two (or more) spaces | +| | as a field separator. | ++----------+--------------------------+ +| Bar | Even very very long | +| | lines like these are | +| | fine, as long as you do | +| | not put in line endings | +| | here. | ++----------+--------------------------+ +| Qux | This is the last line. | ++----------+--------------------------+ + +This is paragraph text *after* the table, with +a line ending. +""".split('\n') + reflow_table() + self.assertEquals(expect, vim.current.buffer) + -- cgit v1.2.3