Add :expandtabs and :noexpandtabs
This commit is contained in:
@@ -59,6 +59,7 @@ class DelModification(NamedTuple):
|
||||
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.file_y = self.y = self._x = self._x_hint = 0
|
||||
|
||||
@@ -242,6 +243,13 @@ class Buf:
|
||||
|
||||
# rendered lines
|
||||
|
||||
@property
|
||||
def tab_string(self) -> str:
|
||||
if self.expandtabs:
|
||||
return ' ' * self.tab_size
|
||||
else:
|
||||
return '\t'
|
||||
|
||||
def rendered_line(self, idx: int, margin: Margin) -> str:
|
||||
x = self._cursor_x if idx == self.y else 0
|
||||
expanded = self._lines[idx].expandtabs(self.tab_size)
|
||||
|
||||
23
babi/file.py
23
babi/file.py
@@ -524,20 +524,29 @@ class File:
|
||||
assert self.selection.start is not None
|
||||
sel_y, sel_x = self.selection.start
|
||||
(s_y, _), (e_y, _) = self.selection.get()
|
||||
tab_string = self.buf.tab_string
|
||||
tab_size = len(tab_string)
|
||||
for l_y in range(s_y, e_y + 1):
|
||||
if self.buf[l_y]:
|
||||
self.buf[l_y] = ' ' * self.buf.tab_size + self.buf[l_y]
|
||||
self.buf[l_y] = tab_string + self.buf[l_y]
|
||||
if l_y == self.buf.y:
|
||||
self.buf.x += self.buf.tab_size
|
||||
self.buf.x += tab_size
|
||||
if l_y == sel_y and sel_x != 0:
|
||||
sel_x += self.buf.tab_size
|
||||
sel_x += tab_size
|
||||
self.selection.set(sel_y, sel_x, self.buf.y, self.buf.x)
|
||||
|
||||
@edit_action('insert tab', final=False)
|
||||
def _tab(self, margin: Margin) -> None:
|
||||
n = self.buf.tab_size - self.buf.x % self.buf.tab_size
|
||||
tab_string = self.buf.tab_string
|
||||
if tab_string == '\t':
|
||||
n = 1
|
||||
else:
|
||||
n = self.buf.tab_size - self.buf.x % self.buf.tab_size
|
||||
tab_string = tab_string[:n]
|
||||
line = self.buf[self.buf.y]
|
||||
self.buf[self.buf.y] = line[:self.buf.x] + n * ' ' + line[self.buf.x:]
|
||||
self.buf[self.buf.y] = (
|
||||
line[:self.buf.x] + tab_string + line[self.buf.x:]
|
||||
)
|
||||
self.buf.x += n
|
||||
self.buf.restore_eof_invariant()
|
||||
|
||||
@@ -548,9 +557,9 @@ class File:
|
||||
self._tab(margin)
|
||||
|
||||
def _dedent_line(self, s: str) -> int:
|
||||
bound = min(len(s), self.buf.tab_size)
|
||||
bound = min(len(s), len(self.buf.tab_string))
|
||||
i = 0
|
||||
while i < bound and s[i] == ' ':
|
||||
while i < bound and s[i] in (' ', '\t'):
|
||||
i += 1
|
||||
return i
|
||||
|
||||
|
||||
@@ -457,6 +457,14 @@ class Screen:
|
||||
for file in self.files:
|
||||
file.buf.set_tab_size(parsed_tab_size)
|
||||
self.status.update('updated!')
|
||||
elif response.startswith(':expandtabs'):
|
||||
for file in self.files:
|
||||
file.buf.expandtabs = True
|
||||
self.status.update('updated!')
|
||||
elif response.startswith(':noexpandtabs'):
|
||||
for file in self.files:
|
||||
file.buf.expandtabs = False
|
||||
self.status.update('updated!')
|
||||
elif response == ':comment' or response.startswith(':comment '):
|
||||
_, _, comment = response.partition(' ')
|
||||
comment = (comment or '#').strip()
|
||||
|
||||
45
tests/features/expandtabs_test.py
Normal file
45
tests/features/expandtabs_test.py
Normal file
@@ -0,0 +1,45 @@
|
||||
from testing.runner import and_exit
|
||||
from testing.runner import trigger_command_mode
|
||||
|
||||
|
||||
def test_set_expandtabs(run, tmpdir):
|
||||
f = tmpdir.join('f')
|
||||
f.write('a')
|
||||
|
||||
with run(str(f)) as h, and_exit(h):
|
||||
h.press('Left')
|
||||
trigger_command_mode(h)
|
||||
h.press_and_enter(':expandtabs')
|
||||
h.await_text('updated!')
|
||||
h.press('Tab')
|
||||
h.press('^S')
|
||||
assert f.read() == ' a\n'
|
||||
|
||||
|
||||
def test_set_noexpandtabs(run, tmpdir):
|
||||
f = tmpdir.join('f')
|
||||
f.write('a')
|
||||
|
||||
with run(str(f)) as h, and_exit(h):
|
||||
h.press('Left')
|
||||
trigger_command_mode(h)
|
||||
h.press_and_enter(':noexpandtabs')
|
||||
h.await_text('updated!')
|
||||
h.press('Tab')
|
||||
h.press('^S')
|
||||
assert f.read() == '\ta\n'
|
||||
|
||||
|
||||
def test_indent_with_expandtabs(run, tmpdir):
|
||||
f = tmpdir.join('f')
|
||||
f.write('a\nb\nc')
|
||||
|
||||
with run(str(f)) as h, and_exit(h):
|
||||
trigger_command_mode(h)
|
||||
h.press_and_enter(':noexpandtabs')
|
||||
h.await_text('updated!')
|
||||
for _ in range(3):
|
||||
h.press('S-Down')
|
||||
h.press('Tab')
|
||||
h.press('^S')
|
||||
assert f.read() == '\ta\n\tb\n\tc\n'
|
||||
@@ -1,4 +1,5 @@
|
||||
from testing.runner import and_exit
|
||||
from testing.runner import trigger_command_mode
|
||||
|
||||
|
||||
def test_indent_at_beginning_of_line(run):
|
||||
@@ -87,6 +88,20 @@ def test_dedent_selection(run, tmpdir):
|
||||
h.await_text('\n1\n2\n 3\n')
|
||||
|
||||
|
||||
def test_dedent_selection_with_noexpandtabs(run, tmpdir):
|
||||
f = tmpdir.join('f')
|
||||
f.write('1\n\t2\n\t\t3\n')
|
||||
with run(str(f)) as h, and_exit(h):
|
||||
trigger_command_mode(h)
|
||||
h.press_and_enter(':noexpandtabs')
|
||||
h.await_text('updated!')
|
||||
for _ in range(3):
|
||||
h.press('S-Down')
|
||||
h.press('BTab')
|
||||
h.press('^S')
|
||||
assert f.read() == '1\n2\n\t3\n'
|
||||
|
||||
|
||||
def test_dedent_beginning_of_line(run, tmpdir):
|
||||
f = tmpdir.join('f')
|
||||
f.write(' hi\n')
|
||||
|
||||
Reference in New Issue
Block a user