diff --git a/.gitignore b/.gitignore index 4ff4b6b..cfff959 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ tests/data .vimspector.json uv.lock .python-version +venv/ diff --git a/src/apyanki/cli.py b/src/apyanki/cli.py index cecd430..426526c 100644 --- a/src/apyanki/cli.py +++ b/src/apyanki/cli.py @@ -443,6 +443,97 @@ def review(query: str, check_markdown_consistency: bool, cmc_range: int) -> None i += 1 +@main.command() +@click.argument("query", nargs=-1, required=True) +@click.option( + "--force-multiple", + "-f", + is_flag=True, + help="Allow editing multiple notes (will edit them one by one)", +) +def edit(query: str, force_multiple: bool) -> None: + """Edit notes that match QUERY directly. + + This command allows direct editing of notes matching the provided query + without navigating through the interactive review interface. + + If the query matches multiple notes, you'll be prompted to confirm + unless --force-multiple is specified. + + Examples: + + \b + # Edit a note by its card ID + apy edit cid:1740342619916 + + \b + # Edit a note by its note ID + apy edit nid:1234567890123 + + \b + # Edit a note containing specific text + apy edit "front:error" + """ + query = " ".join(query) + + with Anki(**cfg) as a: + notes = list(a.find_notes(query)) + + # Handle no matches + if not notes: + console.print(f"No notes found matching query: {query}") + return + + # Handle multiple matches + if len(notes) > 1 and not force_multiple: + console.print(f"Query matched {len(notes)} notes. The first five:\n") + + # Show preview of the first 5 matching notes + for i, note in enumerate(notes[:5]): + preview_text = note.n.fields[0][:50].replace("\n", " ") + if len(preview_text) == 50: + preview_text += "..." + console.print(f"{i+1}. nid:{note.n.id} - {preview_text}") + + console.print( + "\nHints:\n" + "* Use 'apy edit --force-multiple' to edit all matches or refine your query so it only matches a single note.\n" + "* Use 'apy list QUERY' to view all matches." + ) + return + + # Edit each note + edited_count = 0 + for i, note in enumerate(notes): + if len(notes) > 1: + console.print( + f"\nEditing note {i+1} of {len(notes)} (nid: {note.n.id})" + ) + + # Show a brief preview of the note + preview_text = note.n.fields[0][:50].replace("\n", " ") + if len(preview_text) == 50: + preview_text += "..." + console.print(f"Content preview: {preview_text}") + console.print(f"Tags: {', '.join(note.n.tags)}") + + if not console.confirm("Edit this note?"): + console.print("Skipping...") + continue + + # Use the direct edit method (bypassing the review interface) + note.edit() + edited_count += 1 + + # Summary message + if edited_count > 0: + console.print( + f"\n[green]Successfully edited {edited_count} note(s)[/green]" + ) + else: + console.print("\n[yellow]No notes were edited[/yellow]") + + @main.command() def sync() -> None: """Synchronize collection with AnkiWeb.""" diff --git a/uv.lock b/uv.lock index c58495d..1954acf 100644 --- a/uv.lock +++ b/uv.lock @@ -1,4 +1,5 @@ version = 1 +revision = 1 requires-python = ">=3.9, <4" resolution-markers = [ "python_full_version >= '3.12'", @@ -30,7 +31,7 @@ wheels = [ [[package]] name = "apyanki" -version = "0.16.2" +version = "0.16.3" source = { editable = "." } dependencies = [ { name = "anki" },