Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Usage: anki [OPTIONS] COMMAND [ARGS]...
│ decks List decks with review stats. │
│ models List available note types. │
│ search Search notes using Anki query syntax. │
│ list List notes, optionally filtered by deck and/or tag.
│ list List notes, optionally filtered by deck, tag, and/or flag.
│ show Show full details of one or more notes. │
│ add Add a new note. │
│ edit Edit fields of an existing note. │
Expand Down
20 changes: 19 additions & 1 deletion src/anki_cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,15 +178,27 @@ def search(
def list_(
deck: Annotated[str | None, typer.Option("--deck", "-d", help="Filter by deck")] = None,
tag: Annotated[str | None, typer.Option("--tag", "-t", help="Filter by tag")] = None,
flag: Annotated[
int | None, typer.Option("--flag", "-F", help="Flag color (1-7) or 0 for any")
] = None,
limit: Annotated[int, typer.Option("--limit", "-l", help="Max results")] = 20,
brief: Annotated[bool, typer.Option("--brief", "-B", help="Truncated table view")] = False,
) -> None:
"""List notes, optionally filtered by deck and/or tag."""
"""List notes, optionally filtered by deck, tag, and/or flag."""
if flag is not None and not 0 <= flag <= 7:
console.print("[red]--flag must be 0 (any) or 1-7.[/red]")
raise typer.Exit(2)

parts = []
if deck:
parts.append(f'"deck:{deck}"')
if tag:
parts.append(f'"tag:{tag}"')
if flag is not None:
if flag == 0:
parts.append("(" + " OR ".join(f"flag:{i}" for i in range(1, 8)) + ")")
else:
parts.append(f"flag:{flag}")
query = " ".join(parts) or "deck:*"

client = AnkiClient()
Expand All @@ -200,6 +212,12 @@ def list_(
title = f"Deck: {deck}"
if tag:
title += f" [tag:{tag}]" if deck else f"Tag: {tag}"
if flag is not None:
flag_label = "Flagged" if flag == 0 else f"Flag: {flag}"
if deck or tag:
title += f" [{flag_label.lower()}]"
else:
title = flag_label
_render_notes(infos, len(ids), title, limit, brief)


Expand Down
11 changes: 11 additions & 0 deletions tests/test_cli_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,16 @@ def test_fuzzy_score_empty_query_returns_zero(self) -> None:
self.assertEqual(_fuzzy_score("", note), 0.0)


class ListFlagTests(unittest.TestCase):
def test_flag_rejects_out_of_range(self) -> None:
result = runner.invoke(app, ["list", "--flag", "9"])
self.assertEqual(result.exit_code, 2)
self.assertIn("--flag must be 0 (any) or 1-7.", result.stdout)

def test_flag_rejects_negative(self) -> None:
result = runner.invoke(app, ["list", "--flag", "-1"])
self.assertNotEqual(result.exit_code, 0)


if __name__ == "__main__":
unittest.main()