riched 0.4.2__tar.gz → 0.4.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {riched-0.4.2 → riched-0.4.4}/AGENTS.md +2 -0
- {riched-0.4.2 → riched-0.4.4}/PKG-INFO +8 -9
- {riched-0.4.2 → riched-0.4.4}/README.md +7 -8
- {riched-0.4.2 → riched-0.4.4}/pyproject.toml +1 -1
- {riched-0.4.2 → riched-0.4.4}/src/riched/app.py +2 -3
- {riched-0.4.2 → riched-0.4.4}/src/riched/cli.py +6 -5
- {riched-0.4.2 → riched-0.4.4}/tests/runner.py +16 -1
- {riched-0.4.2 → riched-0.4.4}/tests/test_cli.py +34 -0
- {riched-0.4.2 → riched-0.4.4}/tests/test_editor_shortcuts.py +30 -0
- {riched-0.4.2 → riched-0.4.4}/uv.lock +1 -1
- {riched-0.4.2 → riched-0.4.4}/.gitignore +0 -0
- {riched-0.4.2 → riched-0.4.4}/.python-version +0 -0
- {riched-0.4.2 → riched-0.4.4}/src/riched/__init__.py +0 -0
- {riched-0.4.2 → riched-0.4.4}/src/riched/bindings.yaml +0 -0
- {riched-0.4.2 → riched-0.4.4}/src/riched/editor.py +0 -0
- {riched-0.4.2 → riched-0.4.4}/src/riched/keybindings.py +0 -0
- {riched-0.4.2 → riched-0.4.4}/src/riched/quick_open.py +0 -0
- {riched-0.4.2 → riched-0.4.4}/src/riched/screens.py +0 -0
- {riched-0.4.2 → riched-0.4.4}/src/riched/settings.py +0 -0
- {riched-0.4.2 → riched-0.4.4}/src/riched/syntax.py +0 -0
- {riched-0.4.2 → riched-0.4.4}/tests/__init__.py +0 -0
- {riched-0.4.2 → riched-0.4.4}/tests/helpers.py +0 -0
- {riched-0.4.2 → riched-0.4.4}/tests/test_command_palette.py +0 -0
- {riched-0.4.2 → riched-0.4.4}/tests/test_dirty_buffers.py +0 -0
- {riched-0.4.2 → riched-0.4.4}/tests/test_file_io.py +0 -0
- {riched-0.4.2 → riched-0.4.4}/tests/test_file_tree.py +0 -0
- {riched-0.4.2 → riched-0.4.4}/tests/test_footer.py +0 -0
- {riched-0.4.2 → riched-0.4.4}/tests/test_quick_open.py +0 -0
- {riched-0.4.2 → riched-0.4.4}/tests/test_syntax.py +0 -0
|
@@ -19,6 +19,8 @@ Use Python 3.12-compatible code with type hints, `from __future__ import annotat
|
|
|
19
19
|
|
|
20
20
|
Keep key bindings in `src/riched/bindings.yaml`, not hardcoded in Python. The F1 hotkey popup is generated from this YAML and displays one user-facing key per binding, preferring `cmd` aliases over `super`.
|
|
21
21
|
|
|
22
|
+
When changing app-level key bindings, test both file-open and no-buffer/directory-start states. For Command-key regressions, verify both Textual names (`cmd+...` and terminal-normalized `super+...`) because Pilot can pass for synthetic keys while a real terminal path or missing app state still breaks. Do not patch Command-key behavior by adding Python-side hardcoded fallbacks; add aliases to `bindings.yaml` and e2e coverage instead.
|
|
23
|
+
|
|
22
24
|
## Testing Guidelines
|
|
23
25
|
|
|
24
26
|
Only add end-to-end tests in `tests/`; do not create unit tests. Tests use Textual's Pilot harness and should be named `test_<behavior>`. Keep each test focused on user-visible behavior such as file open/save, dirty prompts, file tree switching, quick-open search/indexing, or syntax highlighting. Use temporary files as existing tests do.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: riched
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.4
|
|
4
4
|
Summary: A Rich library powered editor(hence the name riched) that implements VS Code keybindings in TUI
|
|
5
5
|
Project-URL: Repository, https://github.com/hoishing/riched
|
|
6
6
|
Requires-Python: >=3.12
|
|
@@ -81,11 +81,10 @@ Editor shortcuts:
|
|
|
81
81
|
|
|
82
82
|
## Limitations
|
|
83
83
|
|
|
84
|
-
-
|
|
85
|
-
|
|
86
|
-
-
|
|
87
|
-
|
|
88
|
-
-
|
|
89
|
-
Ghostty
|
|
90
|
-
|
|
91
|
-
environments.
|
|
84
|
+
- [Textual](https://github.com/textualize/textual) textarea limitation:
|
|
85
|
+
- no multi-cursor editing
|
|
86
|
+
- no chord hotkey sequence such as `⌘K ⌘S`
|
|
87
|
+
|
|
88
|
+
- Ghostty conflicts:
|
|
89
|
+
- conflicted Ghostty hotkey. eg. `⌘Enter`, `⌘⇧Enter`, `⌘[`, `⌘]`, `⌘⇧P`, `⌘W`, `⌘Q`...etc
|
|
90
|
+
- won't implement features that require modifying Ghostty default config
|
|
@@ -70,11 +70,10 @@ Editor shortcuts:
|
|
|
70
70
|
|
|
71
71
|
## Limitations
|
|
72
72
|
|
|
73
|
-
-
|
|
74
|
-
|
|
75
|
-
-
|
|
76
|
-
|
|
77
|
-
-
|
|
78
|
-
Ghostty
|
|
79
|
-
|
|
80
|
-
environments.
|
|
73
|
+
- [Textual](https://github.com/textualize/textual) textarea limitation:
|
|
74
|
+
- no multi-cursor editing
|
|
75
|
+
- no chord hotkey sequence such as `⌘K ⌘S`
|
|
76
|
+
|
|
77
|
+
- Ghostty conflicts:
|
|
78
|
+
- conflicted Ghostty hotkey. eg. `⌘Enter`, `⌘⇧Enter`, `⌘[`, `⌘]`, `⌘⇧P`, `⌘W`, `⌘Q`...etc
|
|
79
|
+
- won't implement features that require modifying Ghostty default config
|
|
@@ -545,10 +545,9 @@ class RichedApp(App):
|
|
|
545
545
|
def action_toggle_file_tree(self) -> None:
|
|
546
546
|
editor = self._editor_or_none()
|
|
547
547
|
if self._is_file_tree_visible():
|
|
548
|
-
if editor is None:
|
|
549
|
-
return
|
|
550
548
|
self._hide_file_tree()
|
|
551
|
-
editor
|
|
549
|
+
if editor is not None:
|
|
550
|
+
editor.focus()
|
|
552
551
|
return
|
|
553
552
|
self._show_file_tree()
|
|
554
553
|
|
|
@@ -17,7 +17,11 @@ def main() -> int:
|
|
|
17
17
|
action="store_true",
|
|
18
18
|
help="Show the version and exit",
|
|
19
19
|
)
|
|
20
|
-
parser.add_argument(
|
|
20
|
+
parser.add_argument(
|
|
21
|
+
"filename",
|
|
22
|
+
nargs="?",
|
|
23
|
+
help="File or folder to open; defaults to the current folder",
|
|
24
|
+
)
|
|
21
25
|
args = parser.parse_args()
|
|
22
26
|
|
|
23
27
|
if args.version:
|
|
@@ -26,10 +30,7 @@ def main() -> int:
|
|
|
26
30
|
print(f"riched {version('riched')}")
|
|
27
31
|
return 0
|
|
28
32
|
|
|
29
|
-
if args.filename is None
|
|
30
|
-
parser.error("the following arguments are required: filename")
|
|
31
|
-
|
|
32
|
-
path = Path(args.filename).expanduser()
|
|
33
|
+
path = Path.cwd() if args.filename is None else Path(args.filename).expanduser()
|
|
33
34
|
root = path if path.is_dir() else Path.cwd()
|
|
34
35
|
|
|
35
36
|
class ConfiguredRichedApp(RichedApp):
|
|
@@ -11,7 +11,11 @@ from .test_command_palette import (
|
|
|
11
11
|
test_ctrl_p_does_not_open_command_palette,
|
|
12
12
|
test_f1_opens_command_palette,
|
|
13
13
|
)
|
|
14
|
-
from .test_cli import
|
|
14
|
+
from .test_cli import (
|
|
15
|
+
test_no_filename_opens_current_folder,
|
|
16
|
+
test_version_flag_prints_current_version,
|
|
17
|
+
test_version_flag_rejects_filename,
|
|
18
|
+
)
|
|
15
19
|
from .test_dirty_buffers import (
|
|
16
20
|
test_close_buffer_dirty_shows_wide_modal_then_cancel,
|
|
17
21
|
test_close_buffer_dirty_space_discard_enters_no_buffer_state,
|
|
@@ -32,6 +36,7 @@ from .test_editor_shortcuts import (
|
|
|
32
36
|
test_cmd_backspace_deletes_selection,
|
|
33
37
|
test_cmd_backspace_deletes_to_line_start,
|
|
34
38
|
test_cmd_b_toggles_file_tree,
|
|
39
|
+
test_cmd_b_toggles_file_tree_without_open_buffer,
|
|
35
40
|
test_cmd_l_repeats_expand_line_selection,
|
|
36
41
|
test_cmd_l_selects_current_line_with_newline,
|
|
37
42
|
test_cmd_l_selects_final_line_without_newline,
|
|
@@ -56,6 +61,7 @@ from .test_editor_shortcuts import (
|
|
|
56
61
|
test_parser_order_super_shift_line_selection_aliases,
|
|
57
62
|
test_super_l_selects_current_line_alias,
|
|
58
63
|
test_super_b_toggles_file_tree_alias,
|
|
64
|
+
test_super_b_toggles_file_tree_without_open_buffer,
|
|
59
65
|
test_super_shift_k_deletes_current_line_alias,
|
|
60
66
|
test_super_shift_line_selection_aliases,
|
|
61
67
|
test_undo_multiline_insert_that_removes_scrollbar,
|
|
@@ -94,6 +100,7 @@ TESTS: list[tuple[str, Callable[[], Awaitable[None]]]] = [
|
|
|
94
100
|
("open missing file", test_open_missing_file),
|
|
95
101
|
("open directory starts with no buffer", test_open_directory_starts_with_no_buffer),
|
|
96
102
|
("save writes file", test_save_writes_file),
|
|
103
|
+
("cli: no filename opens current folder", test_no_filename_opens_current_folder),
|
|
97
104
|
("cli: version flag prints current version", test_version_flag_prints_current_version),
|
|
98
105
|
("cli: version flag rejects filename", test_version_flag_rejects_filename),
|
|
99
106
|
("command palette: button hidden", test_command_palette_button_is_hidden),
|
|
@@ -183,7 +190,15 @@ TESTS: list[tuple[str, Callable[[], Awaitable[None]]]] = [
|
|
|
183
190
|
),
|
|
184
191
|
("editor: alt+z toggles word wrap", test_alt_z_toggles_word_wrap),
|
|
185
192
|
("app: cmd+b toggles file tree", test_cmd_b_toggles_file_tree),
|
|
193
|
+
(
|
|
194
|
+
"app: cmd+b toggles file tree without open buffer",
|
|
195
|
+
test_cmd_b_toggles_file_tree_without_open_buffer,
|
|
196
|
+
),
|
|
186
197
|
("app: super+b toggles file tree alias", test_super_b_toggles_file_tree_alias),
|
|
198
|
+
(
|
|
199
|
+
"app: super+b toggles file tree without open buffer",
|
|
200
|
+
test_super_b_toggles_file_tree_without_open_buffer,
|
|
201
|
+
),
|
|
187
202
|
(
|
|
188
203
|
"editor: undo multiline insert that removes scrollbar",
|
|
189
204
|
test_undo_multiline_insert_that_removes_scrollbar,
|
|
@@ -2,7 +2,9 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import subprocess
|
|
4
4
|
import sys
|
|
5
|
+
import tempfile
|
|
5
6
|
from importlib.metadata import version
|
|
7
|
+
from pathlib import Path
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
async def test_version_flag_prints_current_version() -> None:
|
|
@@ -38,3 +40,35 @@ async def test_version_flag_rejects_filename() -> None:
|
|
|
38
40
|
assert result.returncode == 2, result.stdout
|
|
39
41
|
assert result.stdout == "", result.stdout
|
|
40
42
|
assert "riched --version does not take arguments" in result.stderr, result.stderr
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
async def test_no_filename_opens_current_folder() -> None:
|
|
46
|
+
script = """
|
|
47
|
+
from pathlib import Path
|
|
48
|
+
from riched import cli
|
|
49
|
+
|
|
50
|
+
captured = {}
|
|
51
|
+
|
|
52
|
+
class StubApp:
|
|
53
|
+
def __init__(self, path, root):
|
|
54
|
+
captured["path"] = path
|
|
55
|
+
captured["root"] = root
|
|
56
|
+
|
|
57
|
+
def run(self):
|
|
58
|
+
print(f"{captured['path']}|{captured['root']}")
|
|
59
|
+
|
|
60
|
+
cli.RichedApp = StubApp
|
|
61
|
+
raise SystemExit(cli.main())
|
|
62
|
+
"""
|
|
63
|
+
tmp = Path(tempfile.mkdtemp(prefix="riched-cli-"))
|
|
64
|
+
result = subprocess.run(
|
|
65
|
+
[sys.executable, "-c", script],
|
|
66
|
+
check=False,
|
|
67
|
+
capture_output=True,
|
|
68
|
+
text=True,
|
|
69
|
+
cwd=tmp,
|
|
70
|
+
)
|
|
71
|
+
cwd = tmp.resolve()
|
|
72
|
+
assert result.returncode == 0, result.stderr
|
|
73
|
+
assert result.stdout == f"{cwd}|{cwd}\n", result.stdout
|
|
74
|
+
assert result.stderr == "", result.stderr
|
|
@@ -201,6 +201,21 @@ async def test_cmd_b_toggles_file_tree() -> None:
|
|
|
201
201
|
assert tree.styles.display == "block"
|
|
202
202
|
|
|
203
203
|
|
|
204
|
+
async def test_cmd_b_toggles_file_tree_without_open_buffer() -> None:
|
|
205
|
+
tmp, _ = _fresh_env()
|
|
206
|
+
app = _make_app(tmp, root=tmp)
|
|
207
|
+
async with app.run_test() as pilot:
|
|
208
|
+
await pilot.pause()
|
|
209
|
+
tree = app.query_one("#file-tree", DirectoryTree)
|
|
210
|
+
assert tree.styles.display == "block"
|
|
211
|
+
await pilot.press("cmd+b")
|
|
212
|
+
await pilot.pause()
|
|
213
|
+
assert tree.styles.display == "none"
|
|
214
|
+
await pilot.press("cmd+b")
|
|
215
|
+
await pilot.pause()
|
|
216
|
+
assert tree.styles.display == "block"
|
|
217
|
+
|
|
218
|
+
|
|
204
219
|
async def test_super_b_toggles_file_tree_alias() -> None:
|
|
205
220
|
tmp, _ = _fresh_env()
|
|
206
221
|
f = tmp / "tree.txt"
|
|
@@ -217,6 +232,21 @@ async def test_super_b_toggles_file_tree_alias() -> None:
|
|
|
217
232
|
assert tree.styles.display == "block"
|
|
218
233
|
|
|
219
234
|
|
|
235
|
+
async def test_super_b_toggles_file_tree_without_open_buffer() -> None:
|
|
236
|
+
tmp, _ = _fresh_env()
|
|
237
|
+
app = _make_app(tmp, root=tmp)
|
|
238
|
+
async with app.run_test() as pilot:
|
|
239
|
+
await pilot.pause()
|
|
240
|
+
tree = app.query_one("#file-tree", DirectoryTree)
|
|
241
|
+
assert tree.styles.display == "block"
|
|
242
|
+
await pilot.press("super+b")
|
|
243
|
+
await pilot.pause()
|
|
244
|
+
assert tree.styles.display == "none"
|
|
245
|
+
await pilot.press("super+b")
|
|
246
|
+
await pilot.pause()
|
|
247
|
+
assert tree.styles.display == "block"
|
|
248
|
+
|
|
249
|
+
|
|
220
250
|
async def test_undo_multiline_insert_that_removes_scrollbar() -> None:
|
|
221
251
|
tmp, _ = _fresh_env()
|
|
222
252
|
f = tmp / "undo.txt"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|