Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd8dc3beb0 | ||
|
|
324513c36a | ||
|
|
09643b0f80 | ||
|
|
8245ee56a5 | ||
|
|
8aaee402e7 | ||
|
|
641eed65d5 | ||
|
|
694853e341 | ||
|
|
1a4d15206c | ||
|
|
4ca3b0d1e5 | ||
|
|
47868e77a2 | ||
|
|
9906e223bd | ||
|
|
a5101007cd | ||
|
|
572151197d | ||
|
|
a8a5afc6ed | ||
|
|
e523a694b6 | ||
|
|
8161c9e34f | ||
|
|
0b5e187be5 | ||
|
|
ee13b7bb78 | ||
|
|
cad35f7b4d | ||
|
|
c1a9823894 | ||
|
|
899eb6f879 | ||
|
|
945e0b1620 | ||
|
|
1f348882b8 |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
github: asottile
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v2.5.0
|
rev: v3.4.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
@@ -11,34 +11,34 @@ repos:
|
|||||||
- id: name-tests-test
|
- id: name-tests-test
|
||||||
- id: requirements-txt-fixer
|
- id: requirements-txt-fixer
|
||||||
- repo: https://gitlab.com/pycqa/flake8
|
- repo: https://gitlab.com/pycqa/flake8
|
||||||
rev: 3.8.0
|
rev: 3.8.4
|
||||||
hooks:
|
hooks:
|
||||||
- id: flake8
|
- id: flake8
|
||||||
additional_dependencies: [flake8-typing-imports==1.7.0]
|
additional_dependencies: [flake8-typing-imports==1.7.0]
|
||||||
- repo: https://github.com/pre-commit/mirrors-autopep8
|
- repo: https://github.com/pre-commit/mirrors-autopep8
|
||||||
rev: v1.5.2
|
rev: v1.5.4
|
||||||
hooks:
|
hooks:
|
||||||
- id: autopep8
|
- id: autopep8
|
||||||
- repo: https://github.com/asottile/reorder_python_imports
|
- repo: https://github.com/asottile/reorder_python_imports
|
||||||
rev: v2.3.0
|
rev: v2.3.6
|
||||||
hooks:
|
hooks:
|
||||||
- id: reorder-python-imports
|
- id: reorder-python-imports
|
||||||
args: [--py3-plus]
|
args: [--py3-plus]
|
||||||
- repo: https://github.com/asottile/add-trailing-comma
|
- repo: https://github.com/asottile/add-trailing-comma
|
||||||
rev: v2.0.1
|
rev: v2.0.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: add-trailing-comma
|
- id: add-trailing-comma
|
||||||
args: [--py36-plus]
|
args: [--py36-plus]
|
||||||
- repo: https://github.com/asottile/pyupgrade
|
- repo: https://github.com/asottile/pyupgrade
|
||||||
rev: v2.4.1
|
rev: v2.7.4
|
||||||
hooks:
|
hooks:
|
||||||
- id: pyupgrade
|
- id: pyupgrade
|
||||||
args: [--py36-plus]
|
args: [--py36-plus]
|
||||||
- repo: https://github.com/asottile/setup-cfg-fmt
|
- repo: https://github.com/asottile/setup-cfg-fmt
|
||||||
rev: v1.9.0
|
rev: v1.16.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: setup-cfg-fmt
|
- id: setup-cfg-fmt
|
||||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||||
rev: v0.770
|
rev: v0.790
|
||||||
hooks:
|
hooks:
|
||||||
- id: mypy
|
- id: mypy
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
[](https://dev.azure.com/asottile/asottile/_build/latest?definitionId=29&branchName=master)
|
[](https://dev.azure.com/asottile/asottile/_build/latest?definitionId=29&branchName=master)
|
||||||
[](https://dev.azure.com/asottile/asottile/_build/latest?definitionId=29&branchName=master)
|
[](https://dev.azure.com/asottile/asottile/_build/latest?definitionId=29&branchName=master)
|
||||||
|
[](https://results.pre-commit.ci/latest/github/asottile/babi/master)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -17,6 +18,13 @@ a text editor, eventually...
|
|||||||
I used to use the text editor `nano`, frequently I typo this. on a qwerty
|
I used to use the text editor `nano`, frequently I typo this. on a qwerty
|
||||||
keyboard, when the right hand is shifted left by one, `nano` becomes `babi`.
|
keyboard, when the right hand is shifted left by one, `nano` becomes `babi`.
|
||||||
|
|
||||||
|
### babi vs. nano
|
||||||
|
|
||||||
|
here is a youtube video where I discuss the motivation for creating and using
|
||||||
|
`babi` instead of `nano`:
|
||||||
|
|
||||||
|
[](https://youtu.be/WyR1hAGmR3g)
|
||||||
|
|
||||||
### quitting babi
|
### quitting babi
|
||||||
|
|
||||||
currently you can quit `babi` by using <kbd>^X</kbd> (or via <kbd>esc</kbd> +
|
currently you can quit `babi` by using <kbd>^X</kbd> (or via <kbd>esc</kbd> +
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ resources:
|
|||||||
ref: refs/tags/v2.0.0
|
ref: refs/tags/v2.0.0
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
- template: job--pre-commit.yml@asottile
|
|
||||||
- template: job--python-tox.yml@asottile
|
- template: job--python-tox.yml@asottile
|
||||||
parameters:
|
parameters:
|
||||||
toxenvs: [pypy3, py36, py37, py38, py39]
|
toxenvs: [pypy3, py36, py37, py38, py39]
|
||||||
|
|||||||
@@ -476,7 +476,11 @@ class File:
|
|||||||
if self.buf.y == 0 and self.buf.x == 0:
|
if self.buf.y == 0 and self.buf.x == 0:
|
||||||
pass
|
pass
|
||||||
# backspace at the end of the file does not change the contents
|
# backspace at the end of the file does not change the contents
|
||||||
elif self.buf.y == len(self.buf) - 1:
|
elif (
|
||||||
|
self.buf.y == len(self.buf) - 1 and
|
||||||
|
# still allow backspace if there are 2+ blank lines
|
||||||
|
self.buf[self.buf.y - 1] != ''
|
||||||
|
):
|
||||||
self.buf.left(margin)
|
self.buf.left(margin)
|
||||||
# at the beginning of the line, we join the current line and
|
# at the beginning of the line, we join the current line and
|
||||||
# the previous line
|
# the previous line
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ KEYNAME_REWRITE = {
|
|||||||
b'^?': b'KEY_BACKSPACE',
|
b'^?': b'KEY_BACKSPACE',
|
||||||
# linux, perhaps others
|
# linux, perhaps others
|
||||||
b'^H': b'KEY_BACKSPACE', # ^Backspace on my keyboard
|
b'^H': b'KEY_BACKSPACE', # ^Backspace on my keyboard
|
||||||
|
b'^D': b'KEY_DC',
|
||||||
b'PADENTER': b'^M', # Enter on numpad
|
b'PADENTER': b'^M', # Enter on numpad
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,7 +273,7 @@ class Screen:
|
|||||||
prompt: str,
|
prompt: str,
|
||||||
opt_strs: Tuple[str, ...],
|
opt_strs: Tuple[str, ...],
|
||||||
) -> Union[str, PromptResult]:
|
) -> Union[str, PromptResult]:
|
||||||
opts = [opt[0] for opt in opt_strs]
|
opts = {opt[0] for opt in opt_strs}
|
||||||
while True:
|
while True:
|
||||||
x = 0
|
x = 0
|
||||||
prompt_line = self.margin.lines - 1
|
prompt_line = self.margin.lines - 1
|
||||||
@@ -309,8 +310,8 @@ class Screen:
|
|||||||
self.resize()
|
self.resize()
|
||||||
elif key.keyname == b'^C':
|
elif key.keyname == b'^C':
|
||||||
return self.status.cancelled()
|
return self.status.cancelled()
|
||||||
elif isinstance(key.wch, str) and key.wch in opts:
|
elif isinstance(key.wch, str) and key.wch.lower() in opts:
|
||||||
return key.wch
|
return key.wch.lower()
|
||||||
|
|
||||||
def prompt(
|
def prompt(
|
||||||
self,
|
self,
|
||||||
@@ -592,7 +593,10 @@ class Screen:
|
|||||||
|
|
||||||
def _init_screen() -> 'curses._CursesWindow':
|
def _init_screen() -> 'curses._CursesWindow':
|
||||||
# set the escape delay so curses does not pause waiting for sequences
|
# set the escape delay so curses does not pause waiting for sequences
|
||||||
if sys.version_info >= (3, 9): # pragma: no cover
|
if (
|
||||||
|
sys.version_info >= (3, 9) and
|
||||||
|
hasattr(curses, 'set_escdelay')
|
||||||
|
): # pragma: no cover
|
||||||
curses.set_escdelay(25)
|
curses.set_escdelay(25)
|
||||||
else: # pragma: no cover
|
else: # pragma: no cover
|
||||||
os.environ.setdefault('ESCDELAY', '25')
|
os.environ.setdefault('ESCDELAY', '25')
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[metadata]
|
[metadata]
|
||||||
name = babi
|
name = babi
|
||||||
version = 0.0.18
|
version = 0.0.20
|
||||||
description = a text editor
|
description = a text editor
|
||||||
long_description = file: README.md
|
long_description = file: README.md
|
||||||
long_description_content_type = text/markdown
|
long_description_content_type = text/markdown
|
||||||
@@ -16,6 +16,7 @@ classifiers =
|
|||||||
Programming Language :: Python :: 3.6
|
Programming Language :: Python :: 3.6
|
||||||
Programming Language :: Python :: 3.7
|
Programming Language :: Python :: 3.7
|
||||||
Programming Language :: Python :: 3.8
|
Programming Language :: Python :: 3.8
|
||||||
|
Programming Language :: Python :: 3.9
|
||||||
Programming Language :: Python :: Implementation :: CPython
|
Programming Language :: Python :: Implementation :: CPython
|
||||||
Programming Language :: Python :: Implementation :: PyPy
|
Programming Language :: Python :: Implementation :: PyPy
|
||||||
|
|
||||||
|
|||||||
@@ -290,6 +290,7 @@ KEYS = [
|
|||||||
Key('^_', b'^_', '\x1f'),
|
Key('^_', b'^_', '\x1f'),
|
||||||
Key('^\\', b'^\\', '\x1c'),
|
Key('^\\', b'^\\', '\x1c'),
|
||||||
Key('!resize', b'KEY_RESIZE', curses.KEY_RESIZE),
|
Key('!resize', b'KEY_RESIZE', curses.KEY_RESIZE),
|
||||||
|
Key('^D', b'^D', '\x04'),
|
||||||
]
|
]
|
||||||
KEYS_TMUX = {k.tmux: k.wch for k in KEYS}
|
KEYS_TMUX = {k.tmux: k.wch for k in KEYS}
|
||||||
KEYS_CURSES = {k.value: k.curses for k in KEYS}
|
KEYS_CURSES = {k.value: k.curses for k in KEYS}
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ def test_replace_cancel_at_replace_string(run):
|
|||||||
h.await_text('cancelled')
|
h.await_text('cancelled')
|
||||||
|
|
||||||
|
|
||||||
def test_replace_actual_contents(run, ten_lines):
|
@pytest.mark.parametrize('key', ('y', 'Y'))
|
||||||
|
def test_replace_actual_contents(run, ten_lines, key):
|
||||||
with run(str(ten_lines)) as h, and_exit(h):
|
with run(str(ten_lines)) as h, and_exit(h):
|
||||||
h.press('^\\')
|
h.press('^\\')
|
||||||
h.await_text('search (to replace):')
|
h.await_text('search (to replace):')
|
||||||
@@ -38,7 +39,7 @@ def test_replace_actual_contents(run, ten_lines):
|
|||||||
h.await_text('replace with:')
|
h.await_text('replace with:')
|
||||||
h.press_and_enter('ohai')
|
h.press_and_enter('ohai')
|
||||||
h.await_text('replace [yes, no, all]?')
|
h.await_text('replace [yes, no, all]?')
|
||||||
h.press('y')
|
h.press(key)
|
||||||
h.await_text_missing('line_0')
|
h.await_text_missing('line_0')
|
||||||
h.await_text('ohai')
|
h.await_text('ohai')
|
||||||
h.await_text(' *')
|
h.await_text(' *')
|
||||||
|
|||||||
@@ -50,6 +50,18 @@ def test_backspace_at_end_of_file_still_allows_scrolling_down(run, tmpdir):
|
|||||||
h.await_text_missing('*')
|
h.await_text_missing('*')
|
||||||
|
|
||||||
|
|
||||||
|
def test_backspace_deletes_newline_at_end_of_file(run, tmpdir):
|
||||||
|
f = tmpdir.join('f')
|
||||||
|
f.write('foo\n\n')
|
||||||
|
|
||||||
|
with run(str(f)) as h, and_exit(h):
|
||||||
|
h.press('^End')
|
||||||
|
h.press('BSpace')
|
||||||
|
h.press('^S')
|
||||||
|
|
||||||
|
assert f.read() == 'foo\n'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('key', ('BSpace', '^H'))
|
@pytest.mark.parametrize('key', ('BSpace', '^H'))
|
||||||
def test_backspace_deletes_text(run, tmpdir, key):
|
def test_backspace_deletes_text(run, tmpdir, key):
|
||||||
f = tmpdir.join('f')
|
f = tmpdir.join('f')
|
||||||
@@ -72,14 +84,15 @@ def test_delete_at_end_of_file(run, tmpdir):
|
|||||||
h.await_text_missing('*')
|
h.await_text_missing('*')
|
||||||
|
|
||||||
|
|
||||||
def test_delete_removes_character_afterwards(run, tmpdir):
|
@pytest.mark.parametrize('key', ('DC', '^D'))
|
||||||
|
def test_delete_removes_character_afterwards(run, tmpdir, key):
|
||||||
f = tmpdir.join('f')
|
f = tmpdir.join('f')
|
||||||
f.write('hello world')
|
f.write('hello world')
|
||||||
|
|
||||||
with run(str(f)) as h, and_exit(h):
|
with run(str(f)) as h, and_exit(h):
|
||||||
h.await_text('hello world')
|
h.await_text('hello world')
|
||||||
h.press('Right')
|
h.press('Right')
|
||||||
h.press('DC')
|
h.press(key)
|
||||||
h.await_text('hllo world')
|
h.await_text('hllo world')
|
||||||
h.await_text('f *')
|
h.await_text('f *')
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user