Commit graph

245 commits

Author SHA1 Message Date
Arthur Milchior
ea3b55a3a4 NF: DeckID type 2021-03-26 11:14:08 +10:00
Arthur Milchior
575c1d32ec NF: NoteID type 2021-03-26 11:14:08 +10:00
Damien Elmes
be9e46a9ea rework filtered deck screen & search errors
- Filtered deck creation now happens as an atomic operation, and is
undoable.
- The logic for initial search text, normalizing searches and so on
has been pushed into the backend.
- Use protobuf to pass the filtered deck to the updated dialog, so
we don't need to deal with untyped JSON.
- Change the "revise your search?" prompt to be a simple info box -
user has access to cancel and build buttons, and doesn't need a separate
prompt. Tweak the wording so the 'show excluded' button should be more
obvious.
- Filtered decks have a time appended to them instead of a number,
primarily because it's easier to implement. No objections going back to
the old behaviour if someone wants to contribute a clean patch.
The standard de-duplication will happen if two decks are created in the
same minute with the same name.
- Tweak the default sort order, and start with two searches. The UI
will still hide the second search by default, but by starting with two,
the frontend doesn't need logic for creating the starting text.
- Search errors now have their own error type, instead of using
InvalidInput, as that was intended mainly for bad API calls. The markdown
conversion is done when the error is converted from the backend, allowing
errors to printed as a string without any special handling by the calling
code.

TODO: when building a new filtered deck, update_active() is clobbering
the undo log when the overview is refreshed
2021-03-24 22:04:35 +10:00
Damien Elmes
53f8e06172 rename&simplify the deck/config type aliases
- QueueConfig is only used by the scheduler
- DeckConfig was being used in places that Config should have been used
- Add "Dict" to the name so that the bare name is free for use with a
stronger type.
2021-03-24 16:29:02 +10:00
Damien Elmes
6dc98b8010 dyndeckconf -> filtered_deck 2021-03-24 13:17:12 +10:00
Damien Elmes
fafe30f4b4 use perform_op() for deck creation 2021-03-22 23:17:07 +10:00
Damien Elmes
1e80700286 cache card list cell content
Qt is pretty enthusiastic about redrawing the card list when any sort
of activity occurs, and by serving blank cells while the DB was busy,
we were getting ugly flashes, and cells getting stuck blank.

Resolve the issue by calculating a row up front and caching it, then
serving stale content when updates are blocked.
2021-03-19 19:45:21 +10:00
Damien Elmes
c98c553a96 make tag renaming undoable, and speed it up
~3x speedup when renaming a tag that's on 25k notes
2021-03-19 19:45:21 +10:00
Damien Elmes
eb761da622 tweak hook names 2021-03-19 19:45:21 +10:00
Damien Elmes
b8bdc5824b clear_unused_tags and browser redraw improvements
- clear_unused_tags() is now undoable, and returns the number of removed
notes
- add a new mw.query_op() helper for immutable queries
- decouple "freeze/unfreeze ui state" hooks from the "interface update
required" hook, so that the former is fired even on error, and can be
made re-entrant
- use a 'block_updates' flag in Python, instead of setUpdatesEnabled(),
as the latter has the side-effect of preventing child windows like
tooltips from appearing, and forces a full redrawn when updates are
enabled again. The new behaviour leads to the card list blanking out
when a long-running op is running, but in the future if we cache the
cell values we can just display them from the cache instead.
- we were indiscriminately saving the note with saveNow(), due to the
call to saveTags(). Changed so that it only saves when the tags field
is focused.
- drain the "on_done" queue on main before launching a new background
task, to lower the chances of something in on_done making a small query
to the DB and hanging until a long op finishes
- the duplicate check in the editor was executed after the webview loads,
leading to it hanging until the sidebar finishes loading. Run it at
set_note() time instead, so that the editor loads first.
- don't throw an error when a long-running op started with with_progress()
finishes after the window it was launched from has closed
- don't throw an error when the browser is closed before the sidebar
has finished loading
2021-03-19 19:45:21 +10:00
Damien Elmes
9ad1e50544 fix a bunch of qt typing issues uncovered by the following commit 2021-03-19 19:45:21 +10:00
Damien Elmes
1c0bc67fd9 prevent editor from refreshing itself after a save
- add after_hooks arg to perform_op()
- when refreshing browse screen, just redraws cells, and handle
editor update in Browser instead of the model
2021-03-19 19:45:21 +10:00
Damien Elmes
42e20461c0 undoable ops now return changes directly; add new *_ops.py files
- Introduced a new transact() method that wraps the return value
in a separate struct that describes the changes that were made.
- Changes are now gathered from the undo log, so we don't need to
guess at what was changed - eg if update_note() is called with identical
note contents, no changes are returned. Card changes will only be set
if cards were actually generated by the update_note() call, and tag
will only be set if a new tag was added.
- mw.perform_op() has been updated to expect the op to return the changes,
or a structure with the changes in it, and it will use them to fire the
change hook, instead of fetching the changes from undo_status(), so there
is no risk of race conditions.
- the various calls to mw.perform_op() have been split into separate
files like card_ops.py. Aside from making the code cleaner, this works
around a rather annoying issue with mypy. Because we run it with
no_strict_optional, mypy is happy to accept an operation that returns None,
despite the type signature saying it requires changes to be returned.
Turning no_strict_optional on for the whole codebase is not practical
at the moment, but we can enable it for individual files.

Still todo:
- The cursor keeps moving back to the start of a field when typing -
we need to ignore the refresh hook when we are the initiator.
- The busy cursor icon should probably be delayed a few hundreds ms.
- Still need to think about a nicer way of handling saveNow()
- op_made_changes(), op_affects_study_queue() might be better embedded
as properties in the object instead
2021-03-19 19:45:21 +10:00
Damien Elmes
ebd3bba91b fade out webview when pending updates; do some reviewer updates immediately
Issues that need fixing:
- when the editor saves the note with perform_op(), if it isn't modified,
no new undo entry is created, and perform_op then returns the changes
made by the previous operation instead
- the approach of fetching the last action in a subsequent backend
method is unsound, as another queued operation may sneak in first before
we have a chance to query the result - it would be better if it were
returned in a single atomic action
- redrawing the current card while editing is likely to make sound
autoplay annoyingly, and it has an unpleasant redraw. We may be better off
fading it out instead

Side note: the editor cursor moves to the start of the field when the
note is updated in another window - it might be nicer to have it move
the cursor to the end instead.
2021-03-19 19:45:21 +10:00
Damien Elmes
1dec65599d experiment with replacing requireReset with updates on focus-in
- This avoids the need for a separate screen, though we may want to
slightly fade out the display when information is stale.
- Means the browser can delay updates just like the main window does.
2021-03-19 19:45:21 +10:00
Damien Elmes
b9bb9920b5 more reset refactoring
'card modified' covers the common case where we need to rebuild the
study queue, but is also set when changing the card flags. We want to
avoid a queue rebuild in that case, as it causes UI flicker, and may
result in a different card being shown. Note marking doesn't trigger
a queue build, but still causes flicker, and may return the user back
to the front side when they were looking at the answer.

I still think entity-based change tracking is the simplest in the
common case, but to solve the above, I've introduced an enum describing
the last operation that was taken. This currently is not trying to list
out all possible operations, and just describes the ones we want to
special-case.

Other changes:

- Fire the old 'state_did_reset' hook after an operation is performed,
so legacy code can refresh itself after an operation is performed.
- Fire the new `operation_did_execute` hook when mw.reset() is called,
so that as the UI is updated to the use the new hook, it will still
be able to refresh after legacy code calls mw.reset()
- Update the deck browser, overview and review screens to listen to
the new hook, instead of relying on the main window to call moveToState()
- Add a 'set flag' backend action, so we can distinguish it from a
normal card update.
- Drop the separate added/modified entries in the change list in
favour of a single entry per entity.
- Add typing to mw.state
- Tweak perform_op()
- Convert a few more actions to use perform_op()
2021-03-19 19:45:21 +10:00
Damien Elmes
fbcfa48419 experiment with finer-scoped reset in perform_op()
Basic proof of concept, where the 'delete note' operation in the
reviewer has been updated to use mw.perform_op(). Instead of manually
calling .reset() afterwards, a summary of the changes is returned as
part of the undo status query, and various parts of the GUI can listen
to gui_hooks.operation_did_execute and decide whether they want to
redraw based on the scope of the changes. This should allow the sidebar
to selectively redraw just the tags area in the future for example.

Currently we're just listing out all possible areas that might be changed;
in the future we could theoretically inspect the specific changes in the
undo log to provide a more accurate report (avoiding refreshing the tags
list when no tags were added for example).

You can test it out by opening the browse screen while studying, and
then deleting the current card - the browser should update to show (deleted)
on the cards due the earlier change.

If going ahead with this, aside from updating all the screens that currently
listen for resets, some thought will be required on how we can integrate
it with legacy code that expects to called when resets are made, and expects
to call .reset() when it makes changes.

Thoughts?
2021-03-19 19:45:21 +10:00
Damien Elmes
f25b95b312 avoid hanging UI when undoing in browse screen 2021-03-12 18:54:08 +10:00
Damien Elmes
da2467838c undo in background, and show progress window 2021-03-12 17:54:56 +10:00
Damien Elmes
ab564315d8 split out common scheduler code into base.py, use scheduler/ dir
Also move the legacy aliases into a separate file
2021-03-12 14:07:52 +10:00
Damien Elmes
99f825c138 expand backend Preferences and make undoable
- moved 'default to current deck when adding' into prefs
- move some profile options into the collection config, so they're
undoable and will sync. There is (currently) no automatic migration
from the old profile settings, meaning users will need to set the
options again if they've customized them.
- tidy up preferences.py
- drop the deleteMedia option that was not exposed in the UI
2021-03-10 18:51:03 +10:00
Damien Elmes
ade12fe2fb Simplify note adding and the deck/notetype choosers
The existing code was really difficult to reason about:

- The default notetype depended on the selected deck, and vice versa,
and this logic was buried in the deck and notetype choosing screens,
and models.py.
- Changes to the notetype were not passed back directly, but were fired
via a hook, which changed any screen in the app that had a notetype
selector.

It also wasn't great for performance, as the most recent deck and tags
were embedded in the notetype, which can be expensive to save and sync
for large notetypes.

To address these points:

- The current deck for a notetype, and notetype for a deck, are now
stored in separate config variables, instead of directly in the deck
or notetype. These are cheap to read and write, and we'll be able to
sync them individually in the future once config syncing is updated in
the future. I seem to recall some users not wanting the tag saving
behaviour, so I've dropped that for now, but if people end up missing
it, it would be simple to add as an extra auxiliary config variable.
- The logic for getting the starting deck and notetype has been moved
into the backend. It should be the same as the older Python code, with
one exception: when "change deck depending on notetype" is enabled in
the preferences, it will start with the current notetype ("curModel"),
instead of first trying to get a deck-specific notetype.
- ModelChooser has been duplicated into notetypechooser.py, and it
has been updated to solely be concerned with keeping track of a selected
notetype - it no longer alters global state.
2021-03-10 11:53:27 +10:00
Damien Elmes
7a58268617 make mark toggling undoable
- note.flush() behaves like before, as otherwise actions or add-ons
that perform bulk flushing would end up creating an undo entry for
each note
- added col.update_note() to opt in to the new behaviour
- tidy up the names of some related routines
2021-03-10 11:53:27 +10:00
Damien Elmes
1b6cc07e63 note deletion undo; refactoring
- transact() now automatically clears card queues unless an op
opts-out (and currently only AnswerCard does). This means there's no
risk of forgetting to clear the queues in an operation, or when undoing/
redoing
- CollectionOp->UndoableOp
- clear queues when redoing "answer card", instead of clearing redo
when clearing queues
2021-03-10 11:47:53 +10:00
Damien Elmes
b1350727fc commit immediately when there's no active checkpoint
Reviews and operations on the backend that support undoing can now be
committed immediately, so they will not be lost in the event of a crash.

This required tweaks to a few places:

- don't set collection mtime on save() unless changes were made in
Python, as otherwise we end up accidentally clearing the backend undo
queue
- autosave() is now run on every reset()
- garbage collection now runs in a timer, instead of relying on
autosave() to be run periodically
2021-03-10 11:47:53 +10:00
Damien Elmes
160c90b840 rework undo
- use dataclasses for the review/checkpoint undo cases, instead of the
nasty ad-hoc list structure
- expose backend review undo to Python, and hook it into GUI
- redo is not currently exposed on the GUI, and the backend can only
cope with reviews done by the new scheduler at the moment
- the initial undo prototype code was bumping mtime/usn on undo, but
that was not ideal, as it was breaking the queue handling which expected
the mtime to match. The original rationale for bumping mtime/usn was
to avoid problems with syncing, but various operations like removing
a revlog can't be synced anyway - so we just need to ensure we clear the
undo queue prior to syncing
2021-03-10 11:47:53 +10:00
bluegreenmagick
2600d89850 don't show routine update when not update_enabled 2021-03-09 22:27:28 +09:00
Damien Elmes
42a44875ab convert qt strings to f-strings with flynt
Also revealed an incorrect type def in editor.py that mypy wasn't
noticing before :-(
2021-02-11 10:09:06 +10:00
Damien Elmes
bf7528d90a minor code cleanups with pyupgrade
- pyupgrade --py38-plus --keep-runtime-typing --keep-percent-format
- third-party mpv and winpaths excluded
2021-02-11 09:43:40 +10:00
Damien Elmes
759e646f5d fix backup not being taken before full download 2021-02-06 19:01:48 +10:00
Damien Elmes
6c60a27c8b add remaining types and disable missing types on (almost) all aqt 2021-02-03 00:00:29 +10:00
Damien Elmes
9702532fc4 more typing updates 2021-02-02 23:31:55 +10:00
RumovZ
8d8a2e49fd Add remaining type hints to dyndeckconf etc. 2021-02-01 23:46:56 +01:00
RumovZ
b21d1dcbc0 Merge branch 'master' into dyn-deckconf 2021-02-01 23:33:41 +01:00
Damien Elmes
748aeb9df1 add a bunch of return types 2021-02-01 23:53:23 +10:00
Damien Elmes
83892eac51 add types to various other files
Mainly automated with MonkeyType
2021-02-01 22:08:56 +10:00
Damien Elmes
8be63cb902 add some types to main.py 2021-02-01 20:59:18 +10:00
RumovZ
d57d0f977b Make browser accept optional args and add reopen
That way, the caller doesn't have to hold a reference to the browser and
explicitly call it again, if it wants to search for something specific.
Also, if the browser was closed and opened for a single-card-search, it
now won't perform a redundant current-deck-search first.
2021-02-01 11:54:28 +01:00
Damien Elmes
8dc5ea8bb6 add types to utils.py
The function signatures for things like getFile() are awful, but
sadly are used by a bunch of add-ons.
2021-02-01 20:23:48 +10:00
RumovZ
aa6cf51527 Fix pylints and type annotations in dyndeckconf
Also fix int representation of learning steps.
2021-02-01 09:56:10 +01:00
RumovZ
2a82e43c0b Make DialogManager accept kwargs
When opening a dialogue accepting multiple optional arguments, relying
on position is error-prone and requires passing Nones to fill unused
parameter slots.
2021-02-01 08:50:19 +01:00
RumovZ
23d1481d34 Make dyndeckconf a registered dialogue 2021-01-31 22:37:08 +01:00
RumovZ
53513f371f Rework dynndeckconf
- Handle deck building inside class. New deck is built unless caller
  passes filtered deck.
- If no deck is passed and current deck is filtered, copy settings.
- Remove exec_().
2021-01-31 18:20:47 +01:00
Damien Elmes
a6be0f493b start work on more clearly defining backend/protobuf boundaries
- anki._backend stores the protobuf files and rsbackend.py code
- pylib modules import protobuf messages directly from the
_pb2 files, and explicitly export any will be returned or consumed
by public pylib functions, so that calling code can import from pylib
- the "rsbackend" no longer imports and re-exports protobuf messages
- pylib can just consume them directly.
- move errors to errors.py

Still todo:

- rsbridge
- finishing the work on rsbackend, and check what we need to add
back to the original file location to avoid breaking add-ons
2021-01-31 18:55:45 +10:00
RumovZ
df22e51d02 Rework search initialisation
- Remove _searchPrompt.
- Add placeholder prompt.
- Move search for current card from browser to caller. (Thus, support
  current card search even with opened browser.)
2021-01-29 21:07:42 +01:00
RumovZ
c7365abc9e Refactor search_string() and FilterToSearchIn
See #955.
2021-01-29 18:27:33 +01:00
RumovZ
9cf02efd20 Use proper docstrings 2021-01-29 09:40:21 +01:00
RumovZ
e629e32c12 Add browser_search helper in mw 2021-01-28 20:51:32 +01:00
RumovZ
8d669724b1 Replace remaining literal searches in aqt 2021-01-28 11:19:07 +01:00
Arthur Milchior
cb2df4fc1a NF: HelpPage in an enum
Hopefully, this can help with updating on next manual update and maybe even linking to manual translation
2021-01-26 02:16:37 +01:00
abdo
a30a2638ce Hierarchical tags 2021-01-09 17:10:13 +03:00
wallgrenen
3a1ef7b651 remove unused variables and commented-out code 2021-01-08 20:53:27 +01:00
Damien Elmes
8075d46011 convert setWindowFlags call into helper, and fix invalid variables
"type: ignore" was masking the invalid references to self in places
like showText()
2021-01-07 14:24:49 +10:00
BlueGreenMagick
9a29d1b97a remove context help button 2021-01-06 22:15:48 +09:00
BlueGreenMagick
65037387d6 add BrowserDeleteDeck to ResetReason 2021-01-05 21:33:48 +09:00
Damien Elmes
e845424bc9 update sync status after profile open sync
https://forums.ankiweb.net/t/why-is-my-sync-button-blue/2078/15
2020-12-01 10:20:55 +10:00
Damien Elmes
bf4a2998c7 update sync color at end of sync
https://forums.ankiweb.net/t/why-is-my-sync-button-blue/2078/13
2020-11-30 10:14:43 +10:00
abdo
caade20ad4 Strip isolation characters from more strings 2020-11-18 18:03:04 +03:00
Damien Elmes
b02badb224 update multi-line _() references 2020-11-18 11:32:22 +10:00
Damien Elmes
686b640e11 update some qt ngettext references 2020-11-18 09:22:27 +10:00
Damien Elmes
2feddb3d70 update temporary val="%s" references to standard ftl 2020-11-17 22:00:44 +10:00
Damien Elmes
066b1498ae merge bulk of qt/ - designer files still to do 2020-11-17 17:42:43 +10:00
Damien Elmes
da58bbaaa3 ignore incorrect clock in dev mode 2020-11-15 18:29:16 +10:00
Damien Elmes
8147c9996a Merge pull request #793 from nwwt/object-audio-tags-support
Audio & object tag support
2020-11-11 10:33:31 +10:00
Andreas Reis
81d6d21ae3 cleanup / renames
・ soundRegexps →  sound_regexps

・ htmlRegexps →  html_media_regexps

・ HTML_TAGS →  HTML_MEDIA_TAGS

・ escapeImages →  escape_media_filenames + alias

・ strip_html_preserving_image_filenames →  strip_html_preserving_media_filenames
2020-11-10 14:53:04 +01:00
ianki
28ad68d490 Add hooks for filtering media. 2020-11-09 02:12:23 -08:00
Andreas Reis
4e35b1780d Add command-line argument to skip addons
Shift doesn't work for me occasionally, for whatever reason. So just add an -s to skip it that way.

(Also, update my github nick)
2020-10-14 01:31:07 +02:00
Henrik Giesel
5981cebb8e Move sync_did_finish before reset 2020-09-15 13:06:11 +02:00
Henrik Giesel
52502fb1d3 Put sync_did_finish hook before after_sync because it might unload collection 2020-09-14 13:06:20 +02:00
Henrik Giesel
a685272e16 Add sync_will_start and sync_did_finish hook 2020-09-14 12:22:01 +02:00
Damien Elmes
bc60d233e5 rebuild_filtered_deck() and new_filtered() 2020-09-03 18:02:47 +10:00
Damien Elmes
aeca369c9b update to latest isort, pylint and pytest 2020-08-31 12:05:36 +10:00
Damien Elmes
9393a33337 need to invalidate notetype cache on sync completion
basicCheck() was populating it on startup sync, leading to
https://forums.ankiweb.net/t/error-when-adding-a-new-note/1664
2020-08-20 15:35:23 +10:00
Henrik Giesel
2de59074a3 Remove ResetReason.BrowserDeleteNote 2020-08-17 12:50:40 +02:00
Henrik Giesel
8a3f6d72c7 Merge branch 'master' into resethook 2020-08-17 12:39:09 +02:00
Damien Elmes
67148b1c8a clear undo queue on sync
https://forums.ankiweb.net/t/ios-ipad-sync-failure-with-filtered-decks/1617/34
2020-08-17 20:09:07 +10:00
Henrik Giesel
747c8532c2 Create ResetReason enum 2020-08-16 18:56:32 +02:00
Henrik Giesel
1f4e604e2b Rename to should_require_reset 2020-08-16 18:56:32 +02:00
Henrik Giesel
7bb4df13d9 Reorder + make all parameters optional 2020-08-16 18:56:32 +02:00
Henrik Giesel
0b9d7b2cb8 Satisfy formatter 2020-08-16 18:56:32 +02:00
Henrik Giesel
ea6eb2ba3b Add new use of requireReset 2020-08-16 18:56:32 +02:00
Henrik Giesel
9044a4b96b Insert gui_hook main_window_will_require_reset
* into AnkiQt.requireReset method
2020-08-16 18:56:32 +02:00
Arthur Milchior
110f4c664f Any removed 2020-08-12 01:37:21 +02:00
Matt Krump
411accc165 Turn on check_untyped_defs for aqt.main 2020-07-31 20:34:14 -06:00
Damien Elmes
3ca4a882a1 Merge pull request #692 from Arthur-Milchior/remove_useles_variables
Remove useless variables
2020-07-20 11:35:40 +10:00
Damien Elmes
2ed481c37a catch SIGTERM, and respond more quickly 2020-07-18 11:26:04 +10:00
Arthur Milchior
71afb2b4d3 Remove useless variables 2020-07-17 08:38:48 +02:00
Damien Elmes
5f93eaaf8f i18n downgrade&quit button 2020-07-17 15:06:14 +10:00
Damien Elmes
313b40f836 defer top toolbar drawing until after add-ons loaded
https://forums.ankiweb.net/t/anki-2-1-28-beta/629/13
2020-07-02 10:23:14 +10:00
Damien Elmes
ed4c74ca4d add option to disable media syncing
https://forums.ankiweb.net/t/error-when-adding-audio-file/495/2
2020-07-01 11:35:24 +10:00
Damien Elmes
c61f52d871 shift+click on the graphs button to access old graphs for now 2020-06-30 20:07:46 +10:00
Damien Elmes
75471224fa add progress to db check 2020-06-08 21:07:36 +10:00
Damien Elmes
3967dd9b78 Merge pull request #657 from BlueGreenMagick/prof-name
don't allow addons21 as profile name
2020-06-08 11:30:27 +10:00
BlueGreenMagick
1be62f8cc5 don't allow addons21 as profile name
because addons21 and profiles directory are stored in same directory
2020-06-05 22:44:59 +09:00
Damien Elmes
c5e65d02e1 add note/card removal to backend 2020-06-04 18:21:04 +10:00
Damien Elmes
d2fd4bf39e fix initial login 2020-06-02 15:10:58 +10:00
Damien Elmes
e8f33e1526 fix media sync progress not updating, and issues closing profile 2020-06-01 13:57:10 +10:00
Damien Elmes
d422b069de lint fixes 2020-06-01 13:57:10 +10:00
Damien Elmes
b552dfb986 add "immediate" progress back
on ops which we know are going to take time, it makes the interface
look more responsive to pop up the progress more quickly
2020-06-01 13:57:10 +10:00
Damien Elmes
9698cb4f22 update auto-sync code 2020-06-01 13:57:10 +10:00