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:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v2.5.0
|
||||
rev: v3.4.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
@@ -11,34 +11,34 @@ repos:
|
||||
- id: name-tests-test
|
||||
- id: requirements-txt-fixer
|
||||
- repo: https://gitlab.com/pycqa/flake8
|
||||
rev: 3.8.0
|
||||
rev: 3.8.4
|
||||
hooks:
|
||||
- id: flake8
|
||||
additional_dependencies: [flake8-typing-imports==1.7.0]
|
||||
- repo: https://github.com/pre-commit/mirrors-autopep8
|
||||
rev: v1.5.2
|
||||
rev: v1.5.4
|
||||
hooks:
|
||||
- id: autopep8
|
||||
- repo: https://github.com/asottile/reorder_python_imports
|
||||
rev: v2.3.0
|
||||
rev: v2.3.6
|
||||
hooks:
|
||||
- id: reorder-python-imports
|
||||
args: [--py3-plus]
|
||||
- repo: https://github.com/asottile/add-trailing-comma
|
||||
rev: v2.0.1
|
||||
rev: v2.0.2
|
||||
hooks:
|
||||
- id: add-trailing-comma
|
||||
args: [--py36-plus]
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v2.4.1
|
||||
rev: v2.7.4
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py36-plus]
|
||||
- repo: https://github.com/asottile/setup-cfg-fmt
|
||||
rev: v1.9.0
|
||||
rev: v1.16.0
|
||||
hooks:
|
||||
- id: setup-cfg-fmt
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v0.770
|
||||
rev: v0.790
|
||||
hooks:
|
||||
- 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://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
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
jobs:
|
||||
- template: job--pre-commit.yml@asottile
|
||||
- template: job--python-tox.yml@asottile
|
||||
parameters:
|
||||
toxenvs: [pypy3, py36, py37, py38, py39]
|
||||
|
||||
@@ -476,7 +476,11 @@ class File:
|
||||
if self.buf.y == 0 and self.buf.x == 0:
|
||||
pass
|
||||
# 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)
|
||||
# at the beginning of the line, we join the current line and
|
||||
# the previous line
|
||||
|
||||
@@ -96,6 +96,7 @@ KEYNAME_REWRITE = {
|
||||
b'^?': b'KEY_BACKSPACE',
|
||||
# linux, perhaps others
|
||||
b'^H': b'KEY_BACKSPACE', # ^Backspace on my keyboard
|
||||
b'^D': b'KEY_DC',
|
||||
b'PADENTER': b'^M', # Enter on numpad
|
||||
}
|
||||
|
||||
@@ -272,7 +273,7 @@ class Screen:
|
||||
prompt: str,
|
||||
opt_strs: Tuple[str, ...],
|
||||
) -> Union[str, PromptResult]:
|
||||
opts = [opt[0] for opt in opt_strs]
|
||||
opts = {opt[0] for opt in opt_strs}
|
||||
while True:
|
||||
x = 0
|
||||
prompt_line = self.margin.lines - 1
|
||||
@@ -309,8 +310,8 @@ class Screen:
|
||||
self.resize()
|
||||
elif key.keyname == b'^C':
|
||||
return self.status.cancelled()
|
||||
elif isinstance(key.wch, str) and key.wch in opts:
|
||||
return key.wch
|
||||
elif isinstance(key.wch, str) and key.wch.lower() in opts:
|
||||
return key.wch.lower()
|
||||
|
||||
def prompt(
|
||||
self,
|
||||
@@ -592,7 +593,10 @@ class Screen:
|
||||
|
||||
def _init_screen() -> 'curses._CursesWindow':
|
||||
# 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)
|
||||
else: # pragma: no cover
|
||||
os.environ.setdefault('ESCDELAY', '25')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[metadata]
|
||||
name = babi
|
||||
version = 0.0.18
|
||||
version = 0.0.20
|
||||
description = a text editor
|
||||
long_description = file: README.md
|
||||
long_description_content_type = text/markdown
|
||||
@@ -16,6 +16,7 @@ classifiers =
|
||||
Programming Language :: Python :: 3.6
|
||||
Programming Language :: Python :: 3.7
|
||||
Programming Language :: Python :: 3.8
|
||||
Programming Language :: Python :: 3.9
|
||||
Programming Language :: Python :: Implementation :: CPython
|
||||
Programming Language :: Python :: Implementation :: PyPy
|
||||
|
||||
|
||||
@@ -290,6 +290,7 @@ KEYS = [
|
||||
Key('^_', b'^_', '\x1f'),
|
||||
Key('^\\', b'^\\', '\x1c'),
|
||||
Key('!resize', b'KEY_RESIZE', curses.KEY_RESIZE),
|
||||
Key('^D', b'^D', '\x04'),
|
||||
]
|
||||
KEYS_TMUX = {k.tmux: k.wch 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')
|
||||
|
||||
|
||||
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):
|
||||
h.press('^\\')
|
||||
h.await_text('search (to replace):')
|
||||
@@ -38,7 +39,7 @@ def test_replace_actual_contents(run, ten_lines):
|
||||
h.await_text('replace with:')
|
||||
h.press_and_enter('ohai')
|
||||
h.await_text('replace [yes, no, all]?')
|
||||
h.press('y')
|
||||
h.press(key)
|
||||
h.await_text_missing('line_0')
|
||||
h.await_text('ohai')
|
||||
h.await_text(' *')
|
||||
|
||||
@@ -50,6 +50,18 @@ def test_backspace_at_end_of_file_still_allows_scrolling_down(run, tmpdir):
|
||||
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'))
|
||||
def test_backspace_deletes_text(run, tmpdir, key):
|
||||
f = tmpdir.join('f')
|
||||
@@ -72,14 +84,15 @@ def test_delete_at_end_of_file(run, tmpdir):
|
||||
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.write('hello world')
|
||||
|
||||
with run(str(f)) as h, and_exit(h):
|
||||
h.await_text('hello world')
|
||||
h.press('Right')
|
||||
h.press('DC')
|
||||
h.press(key)
|
||||
h.await_text('hllo world')
|
||||
h.await_text('f *')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user