diff --git a/babi/buf.py b/babi/buf.py index 9b1cec4c..e8bbe8ef 100644 --- a/babi/buf.py +++ b/babi/buf.py @@ -1,5 +1,6 @@ import bisect import contextlib +import re from typing import Callable from typing import Generator from typing import Iterator @@ -60,7 +61,7 @@ class Buf: def __init__(self, lines: List[str], tab_size: int = 4) -> None: self._lines = lines self.expandtabs = True - self.tab_size = tab_size + self.tab_size = self.get_tab_size(tab_size) self.file_y = self.y = self._x = self._x_hint = 0 self._set_callbacks: List[SetCallback] = [self._set_cb] @@ -138,6 +139,16 @@ def restore_eof_invariant(self) -> None: if self[-1] != '': self.append('') + def get_tab_size(self, tab_size: int) -> int: + for line in self._lines: + file_tab_size = re.search(r'(?=.*\S)^\s+', line) + if file_tab_size is not None: + # if hard tabs are detected use default + if not file_tab_size.group(0).startswith('\t'): + tab_size = len(file_tab_size.group(0)) + break + return tab_size + def set_tab_size(self, tab_size: int) -> None: self.tab_size = tab_size self._positions = [None] diff --git a/tests/buf_test.py b/tests/buf_test.py index 3d496b1f..09ddb6df 100644 --- a/tests/buf_test.py +++ b/tests/buf_test.py @@ -176,3 +176,20 @@ def test_buf_pop_idx(): buf.apply(modifications) assert lst == ['a', 'b', 'c'] + + +@pytest.mark.parametrize( + ('lines', 'exp_tab_size'), + ( + pytest.param(['a', ' b', ' c'], 2, id='not_default'), + pytest.param(['a', '\tb', 'c'], 4, id='hard_tab'), + pytest.param(['a', 'b', 'c'], 4, id='unknown'), + pytest.param(['a', ' ', ' b'], 2, id='tr_ws_first'), + pytest.param(['a', '\t\t', '\tb'], 4, id='tr_ws_first_hard_tab'), + pytest.param(['a', ' ', 'b'], 4, id='tr_ws_unknown'), + pytest.param([], 4, id='empty'), + ), +) +def test_buf_get_tabsize(lines, exp_tab_size): + buf = Buf(lines) + assert buf.tab_size == exp_tab_size diff --git a/tests/features/indent_test.py b/tests/features/indent_test.py index f81ab6cf..f853cf54 100644 --- a/tests/features/indent_test.py +++ b/tests/features/indent_test.py @@ -80,12 +80,12 @@ def test_dedent_exactly_one_indent(run): def test_dedent_selection(run, tmpdir): f = tmpdir.join('f') - f.write('1\n 2\n 3\n') + f.write('1\n 2\n 3\n 4\n') with run(str(f)) as h, and_exit(h): for _ in range(3): h.press('S-Down') h.press('BTab') - h.await_text('\n1\n2\n 3\n') + h.await_text('\n1\n2\n 3\n4\n') def test_dedent_selection_with_noexpandtabs(run, tmpdir):