From 09eafa93267a30650a041b8307cad11c183b2fcc Mon Sep 17 00:00:00 2001 From: Agil Mammadov Date: Wed, 6 May 2026 18:01:26 +0400 Subject: [PATCH] fix: support dir/note path-style completion --- nnote/completions.py | 32 +++++++++++++++++++++++++------- tests/test_completions.py | 26 ++++++++++++++++---------- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/nnote/completions.py b/nnote/completions.py index dae3d1d..1e8b18e 100644 --- a/nnote/completions.py +++ b/nnote/completions.py @@ -5,15 +5,33 @@ def complete_note_titles(ctx, param, incomplete): try: config = Config.load() - directory = ctx.params.get("directory") - root = config.notes_dir / directory if directory else config.notes_dir + root = config.notes_dir if not root or not root.exists(): return [] - return [ - CompletionItem(p.name) - for p in sorted(root.iterdir()) - if p.is_file() and p.name.startswith(incomplete) - ] + + # Path-style input ("dir1/note") — split on last slash + if "/" in incomplete: + dir_part, name_part = incomplete.rsplit("/", 1) + search_dir = root / dir_part + value_prefix = dir_part + "/" + else: + directory = ctx.params.get("directory") + search_dir = root / directory if directory else root + value_prefix = "" + name_part = incomplete + + if not search_dir.exists(): + return [] + + results = [] + for p in sorted(search_dir.iterdir()): + if not p.name.startswith(name_part): + continue + if p.is_file(): + results.append(CompletionItem(value_prefix + p.name)) + elif p.is_dir(): + results.append(CompletionItem(value_prefix + p.name + "/")) + return results except Exception: return [] diff --git a/tests/test_completions.py b/tests/test_completions.py index 693da34..e1b12b5 100644 --- a/tests/test_completions.py +++ b/tests/test_completions.py @@ -42,14 +42,14 @@ def test_complete_note_titles_returns_matching_files(cfg): assert "beta" not in names -def test_complete_note_titles_empty_prefix_returns_all(cfg): - _make_notes(cfg.notes_dir, ["x", "y"]) +def test_complete_note_titles_empty_prefix_returns_files_and_dirs(cfg): + _make_notes(cfg.notes_dir, ["x", "y"], dirs=["subdir"]) with patch("nnote.completions.Config.load", return_value=cfg): results = complete_note_titles(_FakeCtx(), None, "") - assert {r.value for r in results} == {"x", "y"} + assert {r.value for r in results} == {"x", "y", "subdir/"} -def test_complete_note_titles_respects_directory(cfg): +def test_complete_note_titles_respects_directory_flag(cfg): subdir = cfg.notes_dir / "work" _make_notes(subdir, ["report", "review"]) _make_notes(cfg.notes_dir, ["readme"]) @@ -60,13 +60,19 @@ def test_complete_note_titles_respects_directory(cfg): assert "readme" not in names -def test_complete_note_titles_no_dirs_in_results(cfg): - _make_notes(cfg.notes_dir, ["note"], dirs=["subdir"]) +def test_complete_note_titles_path_style(cfg): + subdir = cfg.notes_dir / "work" + _make_notes(subdir, ["report", "review"]) with patch("nnote.completions.Config.load", return_value=cfg): - results = complete_note_titles(_FakeCtx(), None, "") - names = [r.value for r in results] - assert "subdir" not in names - assert "note" in names + results = complete_note_titles(_FakeCtx(), None, "work/re") + assert {r.value for r in results} == {"work/report", "work/review"} + + +def test_complete_note_titles_path_style_dir_prefix(cfg): + _make_notes(cfg.notes_dir, [], dirs=["work", "personal"]) + with patch("nnote.completions.Config.load", return_value=cfg): + results = complete_note_titles(_FakeCtx(), None, "w") + assert {r.value for r in results} == {"work/"} def test_complete_directories_returns_subdirs(cfg):