Commit Graph

1080 Commits

Author SHA1 Message Date
RumovZ
b86d683f17 Rename render_card_inner() to render_card() 2021-03-20 17:26:30 +01:00
RumovZ
7425aa6b58 Refactor search/browser.rs to browser_rows.rs 2021-03-20 17:20:49 +01:00
RumovZ
c68a6131e0 Add backend routine for browser rows 2021-03-20 12:02:51 +01:00
RumovZ
436269b701 Add backend mod for browser rows 2021-03-20 12:02:18 +01:00
RumovZ
e931a429b3 Add html_to_text_line() on backend 2021-03-20 12:00:45 +01:00
RumovZ
1823c0dda4 Add get_note_without_fields() from storage 2021-03-20 11:59:45 +01:00
Damien Elmes
f7e4b6d822 fix spurious warning in db check for v1 scheduler+non-zero lapse ivl%
https://forums.ankiweb.net/t/invalid-property-on-card-not-resolving-via-recommended-check-database/8430
2021-03-19 22:57:43 +10:00
Damien Elmes
4c61c92806 speed up tag drag&drop and finish tag tidyup
approx 4x speedup when reparenting 10-15 tags and their children at once
2021-03-19 19:45:21 +10:00
Damien Elmes
9c2bff5b6d change bulk_update() into find_and_replace_tag()
Now behaves the same way as standard find&replace:
- Will match substrings
- Regexs can be used to match multiple items; we no longer split
input on spaces.
- The find&replace dialog has been updated to add tags to the field
list.
2021-03-19 19:45:21 +10:00
Damien Elmes
b287cd5238 speed up "add tags" and avoid usage of regex 2021-03-19 19:45:21 +10:00
Damien Elmes
08895c58d9 introduce separate routine to remove tags from specific notes
We were (ab)using the bulk update routine to do deletions, but that
code was really intended to be used for finding&replacing, where an
exact match is not a requirement.
2021-03-19 19:45:21 +10:00
Damien Elmes
33d467e688 split tags.rs up 2021-03-19 19:45:21 +10:00
Damien Elmes
09076da937 make tag deletion undoable, and speed it up
- ~4x faster than before on tag tree with 30k notes
- remove the separate clear_tag() backend method
2021-03-19 19:45:21 +10:00
Damien Elmes
157b74b671 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
0331d8b588 make reposition undoable 2021-03-19 19:45:21 +10:00
Damien Elmes
de668441b5 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
6b0fe4b381 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
30c7cf1fdd 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
1e849316be 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
112cbe8b59 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
7fab319dad derive reset scope from last undoable operation 2021-03-19 19:45:21 +10:00
Damien Elmes
90526c61cd move ops.rs out of undo/ 2021-03-19 19:45:21 +10:00
Damien Elmes
8fc43956c2 move collection mtime bump into backend
Fixes the following issue:
- some code directly modifies the database, causing modified_in_python
to be set to true
- an undoable operation is run, which calls autosave() at the end
- autosave() notices there's an undoable operation, and commits immediately
- because modified_in_python was true, col.mtime was bumped in Python
- that invalidated the undo queue, preventing the operation from being
undone
2021-03-19 19:45:21 +10:00
Arthur Milchior
9661684ca3 If a template name contains only quote, show relevant error message
This is for the sake of the consistency with the last commit
2021-03-14 02:10:48 +01:00
Arthur Milchior
dd48b2dff0 Return meaningful message if a field is empty after normalizing
This correct the probably rare bug as follow:

I got a note type with a field whose name is "\".

When I made any change to this note type, even unrelated, I get a message stating that there is an empty field. This is
strange because I can see it to be false. Investigating show that "\" is normalized to empty field. This ensure that
it's shown
2021-03-14 02:10:32 +01:00
Damien Elmes
bd959731d7
Merge pull request #1067 from RumovZ/regex-err
Fix regex error formatting and search error escaping
2021-03-13 10:16:10 +10:00
RumovZ
e033f21767 Fix markdown escaping in search errors 2021-03-12 20:32:38 +01:00
RumovZ
2078a094f4 Fix formatting of invalid regex error
Preserve whitespace, special characters and use monospace font.
2021-03-12 20:31:23 +01:00
Damien Elmes
28994458e9 add indexes to graves table to speed up undo 2021-03-12 18:59:24 +10:00
Damien Elmes
2ffc055487 'change deck' now undoable 2021-03-12 16:27:57 +10:00
Damien Elmes
7b7d7e8330 consume original card when updating 2021-03-12 16:20:58 +10:00
Damien Elmes
24762261d9 make 'forget card' undoable; remove checkpoint() in set_due_date 2021-03-12 16:13:50 +10:00
Damien Elmes
3b067c7a66 limit initial sort selection to new cards
https://github.com/ankidroid/Anki-Android/issues/8172
2021-03-12 14:58:19 +10:00
Damien Elmes
c1316bb65f 'set due date' now undoable 2021-03-12 14:50:31 +10:00
Damien Elmes
0ab87b7339 enable deck removal undo again 2021-03-11 22:24:12 +10:00
RumovZ
f4c2fe6485 Merge branch 'master' into sidebar-tools 2021-03-11 12:08:32 +01:00
Damien Elmes
e20c5ed9c5 deck drag&drop undo 2021-03-11 20:02:16 +10:00
Damien Elmes
984e2c2666 add a separate 'rename deck' method 2021-03-11 19:24:54 +10:00
RumovZ
f1dd010489 Remove deck remove prompt but show card count 2021-03-11 09:52:11 +01:00
RumovZ
186a0202ea Show tooltip instead of prompt for removing tags 2021-03-11 09:14:50 +01:00
Damien Elmes
4bd120cc4b split out remaining rpc methods
@david-allison-1 note this also changes the method index to start at
0 instead of 1
2021-03-11 17:04:32 +10:00
Damien Elmes
378b116977 split out stats, media and search 2021-03-11 16:16:40 +10:00
Damien Elmes
cd14987812 split out tags, deck config and card rendering 2021-03-11 16:05:06 +10:00
Damien Elmes
1b8d6c6e85 split out sync, notetypes and config code 2021-03-11 15:47:31 +10:00
Damien Elmes
5df684fa6b rework backend codegen to support multiple services; split out sched
Rust requires all methods of impl Trait to be in a single file, which
means we had a giant backend/mod.rs covering all exposed methods. By
using separate service definitions for the separate areas, and updating
the code generation, we can split it into more manageable chunks -
this commit starts with the scheduling code.

In the long run, we'll probably want to split up the protobuf file into
multiple files as well.

Also dropped want_release_gil() from rsbridge, and the associated method
enum. While it allows us to skip the thread save/restore and mutex unlock/
lock, it looks to only be buying about 2.5% extra performance in the
best case (tested with timeit+format_timespan), and the majority of
the backend methods deal with I/O, and thus were already releasing the
GIL.
2021-03-11 14:51:29 +10:00
RumovZ
e1db8e1da1 Borrow dids in remove_decks_and_child_decks 2021-03-10 15:56:54 +01:00
Damien Elmes
c3b0589bb4 make sure we invalidate cache when undoing deck add 2021-03-10 23:59:40 +10:00
Damien Elmes
e122f8ae0d undo support for deck adding/removing
Work in progress - still to do:
- renames appear as 'Update Deck' - easiest way to solve it would
be to have a separate backend method for renames
- drag&drop of decks not yet undoable
- since the undo status is updated after the backend method ends,
the older checkpoint() calls need to be replaced with an
update_undo_status() at the end of the call - if we just remove the
checkpoint, then the menu doesn't get updated
2021-03-10 23:50:11 +10:00
RumovZ
0c2ac4ba04 Merge branch 'master' into sidebar-tools 2021-03-10 10:34:36 +01:00
Damien Elmes
6b1dd9ee19 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