Skip to content

Commit 98dff27

Browse files
committed
aliased update from file and added completions
1 parent 6909086 commit 98dff27

File tree

5 files changed

+160
-70
lines changed

5 files changed

+160
-70
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Anki. It does not require Anki to be running at the same time.
1616
* [Usage](#usage)
1717
* [Configuration](#configuration)
1818
* [Zsh completion](#zsh-completion)
19+
* [fish completion](#fish-completion)
1920
* [Changelog](#changelog)
2021
* [Relevant resources](#relevant-resources)
2122
* [Alternatives](#alternatives)
@@ -173,6 +174,15 @@ Then add the following line to ones `.zshrc` file:
173174
fpath=($HOME/.local/zsh-functions $fpath)
174175
```
175176

177+
## Fish completion
178+
179+
There is also a fish completion file available. To use it, one may symlink or
180+
copy it to `~/.config/fish/completions/` directory:
181+
182+
```
183+
ln -s /path/to/apy/completion/apy.fish ~/.config/fish/completions/
184+
```
185+
176186
## Changelog
177187

178188
See the [release history on GitHub](https://github.com/lervag/apy/releases).

completion/_apy

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ _apy() {
4747
subcmds=( \
4848
'add:Add notes interactively from terminal' \
4949
'add-single:Add a single note from command line arguments' \
50-
'add-from-file:Add notes from Markdown file For input file' \
50+
'add-from-file:Add notes from Markdown file (alias for update-from-file)' \
51+
'update-from-file:Update existing or add new notes from Markdown file' \
5152
'check-media:Check media' \
5253
'info:Print some basic statistics' \
5354
'model:Interact with the models' \
@@ -82,10 +83,12 @@ _apy() {
8283
'::Fields' \
8384
$opts_help \
8485
);;
85-
add-from-file)
86+
add-from-file|update-from-file)
8687
opts=( \
8788
'::Markdown input file:_files -g "*.md"' \
8889
'(-t --tags)'{-t,--tags}'[Specify tags]:tags:' \
90+
'(-d --deck)'{-d,--deck}'[Specify deck]:deck:' \
91+
'(-u --update-file)'{-u,--update-file}'[Update original file with note IDs]' \
8992
$opts_help \
9093
);;
9194
info)

completion/apy.fish

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# Fish shell completion for apy
2+
# Copy this file to ~/.config/fish/completions/
3+
4+
function __fish_apy_no_subcommand
5+
set -l cmd (commandline -opc)
6+
if [ (count $cmd) -eq 1 ]
7+
return 0
8+
end
9+
return 1
10+
end
11+
12+
function __fish_apy_using_command
13+
set -l cmd (commandline -opc)
14+
if [ (count $cmd) -gt 1 ]
15+
if [ $argv[1] = $cmd[2] ]
16+
return 0
17+
end
18+
end
19+
return 1
20+
end
21+
22+
# Main apy command
23+
complete -f -c apy -n '__fish_apy_no_subcommand' -l help -s h -d 'Show help'
24+
complete -f -c apy -n '__fish_apy_no_subcommand' -l base-path -s b -d 'Set Anki base directory' -a '(__fish_complete_directories)'
25+
complete -f -c apy -n '__fish_apy_no_subcommand' -l profile-name -s p -d 'Specify name of Anki profile to use'
26+
complete -f -c apy -n '__fish_apy_no_subcommand' -l version -s V -d 'Show apy version'
27+
28+
# Subcommands
29+
complete -f -c apy -n '__fish_apy_no_subcommand' -a add -d 'Add notes interactively from terminal'
30+
complete -f -c apy -n '__fish_apy_no_subcommand' -a add-single -d 'Add a single note from command line arguments'
31+
complete -f -c apy -n '__fish_apy_no_subcommand' -a add-from-file -d 'Add notes from Markdown file (alias for update-from-file)'
32+
complete -f -c apy -n '__fish_apy_no_subcommand' -a update-from-file -d 'Update existing or add new notes from Markdown file'
33+
complete -f -c apy -n '__fish_apy_no_subcommand' -a check-media -d 'Check media'
34+
complete -f -c apy -n '__fish_apy_no_subcommand' -a info -d 'Print some basic statistics'
35+
complete -f -c apy -n '__fish_apy_no_subcommand' -a model -d 'Interact with the models'
36+
complete -f -c apy -n '__fish_apy_no_subcommand' -a list -d 'Print cards that match query'
37+
complete -f -c apy -n '__fish_apy_no_subcommand' -a review -d 'Review/Edit notes that match query'
38+
complete -f -c apy -n '__fish_apy_no_subcommand' -a reposition -d 'Reposition new card with given CID'
39+
complete -f -c apy -n '__fish_apy_no_subcommand' -a sync -d 'Synchronize collection with AnkiWeb'
40+
complete -f -c apy -n '__fish_apy_no_subcommand' -a tag -d 'Add or remove tags from notes that match query'
41+
complete -f -c apy -n '__fish_apy_no_subcommand' -a edit -d 'Edit notes that match query directly'
42+
complete -f -c apy -n '__fish_apy_no_subcommand' -a backup -d 'Backup Anki database to specified target file'
43+
44+
# add options
45+
complete -f -c apy -n '__fish_apy_using_command add' -l help -s h -d 'Show help'
46+
complete -f -c apy -n '__fish_apy_using_command add' -l tags -s t -d 'Specify default tags for new cards'
47+
complete -f -c apy -n '__fish_apy_using_command add' -l model -s m -d 'Specify default model for new cards'
48+
complete -f -c apy -n '__fish_apy_using_command add' -l deck -s d -d 'Specify default deck for new cards'
49+
50+
# add-single options
51+
complete -f -c apy -n '__fish_apy_using_command add-single' -l help -s h -d 'Show help'
52+
complete -f -c apy -n '__fish_apy_using_command add-single' -l parse-markdown -s p -d 'Parse input as Markdown'
53+
complete -f -c apy -n '__fish_apy_using_command add-single' -l preset -s s -d 'Specify a preset'
54+
complete -f -c apy -n '__fish_apy_using_command add-single' -l tags -s t -d 'Specify default tags for new cards'
55+
complete -f -c apy -n '__fish_apy_using_command add-single' -l model -s m -d 'Specify default model for new cards'
56+
complete -f -c apy -n '__fish_apy_using_command add-single' -l deck -s d -d 'Specify default deck for new cards'
57+
58+
# add-from-file and update-from-file options
59+
for cmd in add-from-file update-from-file
60+
complete -f -c apy -n "__fish_apy_using_command $cmd" -l help -s h -d 'Show help'
61+
complete -f -c apy -n "__fish_apy_using_command $cmd" -l tags -s t -d 'Specify default tags for cards'
62+
complete -f -c apy -n "__fish_apy_using_command $cmd" -l deck -s d -d 'Specify default deck for cards'
63+
complete -f -c apy -n "__fish_apy_using_command $cmd" -l update-file -s u -d 'Update original file with note IDs'
64+
# File argument
65+
complete -f -c apy -n "__fish_apy_using_command $cmd" -k -a "(__fish_complete_suffix .md)"
66+
end
67+
68+
# list options
69+
complete -f -c apy -n '__fish_apy_using_command list' -l help -s h -d 'Show help'
70+
complete -f -c apy -n '__fish_apy_using_command list' -l show-answer -s a -d 'Display answer'
71+
complete -f -c apy -n '__fish_apy_using_command list' -l show-model -s m -d 'Display model'
72+
complete -f -c apy -n '__fish_apy_using_command list' -l show-cid -s c -d 'Display card ids'
73+
complete -f -c apy -n '__fish_apy_using_command list' -l show-due -s d -d 'Display card due time in days'
74+
complete -f -c apy -n '__fish_apy_using_command list' -l show-type -s t -d 'Display card type'
75+
complete -f -c apy -n '__fish_apy_using_command list' -l show-ease -s e -d 'Display card ease'
76+
complete -f -c apy -n '__fish_apy_using_command list' -l show-lapses -s l -d 'Display card number of lapses'
77+
78+
# tag options
79+
complete -f -c apy -n '__fish_apy_using_command tag' -l help -s h -d 'Show help'
80+
complete -f -c apy -n '__fish_apy_using_command tag' -l add-tags -s a -d 'Add specified tags to matched notes'
81+
complete -f -c apy -n '__fish_apy_using_command tag' -l remove-tags -s r -d 'Remove specified tags from matched notes'
82+
complete -f -c apy -n '__fish_apy_using_command tag' -l sort-by-count -s c -d 'When listing tags, sort by note count'
83+
complete -f -c apy -n '__fish_apy_using_command tag' -l purge -s p -d 'Remove all unused tags'
84+
85+
# review options
86+
complete -f -c apy -n '__fish_apy_using_command review' -l help -s h -d 'Show help'
87+
complete -f -c apy -n '__fish_apy_using_command review' -l check-markdown-consistency -s m -d 'Check for Markdown consistency'
88+
complete -f -c apy -n '__fish_apy_using_command review' -l cmc-range -s n -d 'Number of days backwards to check consistency'
89+
90+
# edit options
91+
complete -f -c apy -n '__fish_apy_using_command edit' -l help -s h -d 'Show help'
92+
complete -f -c apy -n '__fish_apy_using_command edit' -l force-multiple -s f -d 'Allow editing multiple notes'
93+
94+
# backup options
95+
complete -f -c apy -n '__fish_apy_using_command backup' -l help -s h -d 'Show help'
96+
complete -f -c apy -n '__fish_apy_using_command backup' -l include-media -s m -d 'Include media files in backup'
97+
complete -f -c apy -n '__fish_apy_using_command backup' -l legacy -s l -d 'Support older Anki versions'
98+
99+
# model subcommands
100+
complete -f -c apy -n '__fish_apy_using_command model' -a edit-css -d 'Edit the CSS template for the specified model'
101+
complete -f -c apy -n '__fish_apy_using_command model' -a rename -d 'Rename model from old_name to new_name'
102+
103+
# model edit-css options
104+
complete -f -c apy -n '__fish_apy_using_command model; and __fish_seen_subcommand_from edit-css' -l help -s h -d 'Show help'
105+
complete -f -c apy -n '__fish_apy_using_command model; and __fish_seen_subcommand_from edit-css' -l model-name -s m -d 'Specify for which model to edit CSS template'
106+
complete -f -c apy -n '__fish_apy_using_command model; and __fish_seen_subcommand_from edit-css' -l sync-after -s s -d 'Perform sync after any change'

src/apyanki/cli.py

Lines changed: 22 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -133,71 +133,6 @@ def add(tags: str, model_name: str, deck: str) -> None:
133133
_added_notes_postprocessing(a, notes)
134134

135135

136-
@main.command("add-from-file")
137-
@click.argument("file", type=click.Path(exists=True, dir_okay=False))
138-
@click.option("-t", "--tags", default="", help="Specify default tags for new cards.")
139-
@click.option("-d", "--deck", help="Specify default deck for new cards.")
140-
@click.option(
141-
"-u", "--update-file", is_flag=True, help="Update original file with note IDs."
142-
)
143-
def add_from_file(file: Path, tags: str, deck: str, update_file: bool) -> None:
144-
"""Add notes from Markdown file.
145-
146-
With the --update-file option, the original file will be updated to include
147-
note IDs for any new notes added.
148-
149-
The example below should adequately specify the syntax. Any initial "key: value"
150-
pairs specify default values for all the following notes. The following keys are
151-
accepted:
152-
153-
\b
154-
* model: The note model (required)
155-
* tags: The note model (optional)
156-
* deck: Which deck the note should be added to (optional)
157-
* markdown: Set to "false" or "no" if apy should not use a markdown converter
158-
while converting the input note to an Anki note. (optional)
159-
* nid: The note ID (added automatically with --update-file option)
160-
161-
Here is the example Markdown input:
162-
163-
// example.md
164-
model: Basic
165-
tags: marked
166-
167-
# Note 1
168-
## Front
169-
Question?
170-
171-
## Back
172-
Answer.
173-
174-
# Note 2
175-
tag: silly-tag
176-
177-
## Front
178-
Question?
179-
180-
## Back
181-
Answer
182-
183-
# Note 3
184-
model: NewModel
185-
markdown: false (default is true)
186-
187-
## NewFront
188-
FieldOne
189-
190-
## NewBack
191-
FieldTwo
192-
193-
## FieldThree
194-
FieldThree
195-
"""
196-
with Anki(**cfg) as a:
197-
notes = a.add_notes_from_file(str(file), tags, deck, update_file)
198-
_added_notes_postprocessing(a, notes)
199-
200-
201136
@main.command("update-from-file")
202137
@click.argument("file", type=click.Path(exists=True, dir_okay=False))
203138
@click.option("-t", "--tags", default="", help="Specify default tags for cards.")
@@ -259,6 +194,28 @@ def update_from_file(file: Path, tags: str, deck: str, update_file: bool) -> Non
259194
_added_notes_postprocessing(a, notes)
260195

261196

197+
# Create an alias for backward compatibility
198+
@main.command("add-from-file")
199+
@click.argument("file", type=click.Path(exists=True, dir_okay=False))
200+
@click.option("-t", "--tags", default="", help="Specify default tags for new cards.")
201+
@click.option("-d", "--deck", help="Specify default deck for new cards.")
202+
@click.option(
203+
"-u", "--update-file", is_flag=True, help="Update original file with note IDs."
204+
)
205+
def add_from_file(file: Path, tags: str, deck: str, update_file: bool) -> None:
206+
"""Add notes from Markdown file.
207+
208+
With the --update-file option, the original file will be updated to include
209+
note IDs for any new notes added.
210+
211+
This command is an alias for update-from-file, which can both add new notes
212+
and update existing ones.
213+
"""
214+
with Anki(**cfg) as a:
215+
notes = a.update_notes_from_file(str(file), tags, deck, update_file)
216+
_added_notes_postprocessing(a, notes)
217+
218+
262219
def _added_notes_postprocessing(a: Anki, notes: list[Note]) -> None:
263220
"""Common postprocessing after 'apy add[-from-file]' or 'apy update-from-file'."""
264221
n_notes = len(notes)

tests/test_cli.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,27 @@ def test_cli_base_directory():
3737
@pytest.mark.parametrize(
3838
"note_files", [test_data_dir + file for file in note_files_input]
3939
)
40-
def test_cli_add_from_file(note_files):
41-
"""Test 'apy add-from-file' for various note file inputs."""
40+
def test_cli_update_from_file(note_files):
41+
"""Test 'apy update-from-file' for various note file inputs."""
4242
runner = CliRunner()
4343

4444
with tempfile.TemporaryDirectory() as tmpdirname:
4545
shutil.copytree(test_collection_dir, tmpdirname, dirs_exist_ok=True)
46-
result = runner.invoke(main, ["-b", tmpdirname, "add-from-file", note_files])
46+
result = runner.invoke(main, ["-b", tmpdirname, "update-from-file", note_files])
47+
48+
assert result.exit_code == 0
49+
50+
51+
def test_cli_add_from_file_alias():
52+
"""Test that 'apy add-from-file' works as an alias for 'update-from-file'."""
53+
runner = CliRunner()
54+
55+
with tempfile.TemporaryDirectory() as tmpdirname:
56+
shutil.copytree(test_collection_dir, tmpdirname, dirs_exist_ok=True)
57+
# Should work as an alias to update-from-file
58+
result = runner.invoke(
59+
main, ["-b", tmpdirname, "add-from-file", test_data_dir + "basic.md"]
60+
)
4761

4862
assert result.exit_code == 0
4963

0 commit comments

Comments
 (0)