Merge branch 'master' into more_precise_config_error_message
This commit is contained in:
commit
b1d0945d10
10
.github/scripts/trailing-newlines.sh
vendored
Executable file
10
.github/scripts/trailing-newlines.sh
vendored
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
files=$(rg -l '[^\n]\z' -g '!*.{svg,scss}' || true)
|
||||||
|
if [ "$files" != "" ]; then
|
||||||
|
echo "the following files are missing a newline on the last line:"
|
||||||
|
echo $files
|
||||||
|
exit 1
|
||||||
|
fi
|
1
.github/workflows/checks.yml
vendored
1
.github/workflows/checks.yml
vendored
@ -21,6 +21,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
# add requirements
|
# add requirements
|
||||||
sudo apt update; sudo apt install portaudio19-dev gettext rename
|
sudo apt update; sudo apt install portaudio19-dev gettext rename
|
||||||
|
sudo snap install ripgrep --classic
|
||||||
export CARGO_TARGET_DIR=~/target
|
export CARGO_TARGET_DIR=~/target
|
||||||
export RSPY_TARGET_DIR=~/target
|
export RSPY_TARGET_DIR=~/target
|
||||||
make check build BUILDFLAGS=""
|
make check build BUILDFLAGS=""
|
||||||
|
3
Makefile
3
Makefile
@ -120,9 +120,10 @@ clean-dist:
|
|||||||
.PHONY: check
|
.PHONY: check
|
||||||
check: pyenv buildhash prepare
|
check: pyenv buildhash prepare
|
||||||
@set -eo pipefail && \
|
@set -eo pipefail && \
|
||||||
|
.github/scripts/trailing-newlines.sh && \
|
||||||
for dir in $(CHECKABLE_RS); do \
|
for dir in $(CHECKABLE_RS); do \
|
||||||
$(SUBMAKE) -C $$dir check; \
|
$(SUBMAKE) -C $$dir check; \
|
||||||
done; \
|
done && \
|
||||||
. "${ACTIVATE_SCRIPT}" && \
|
. "${ACTIVATE_SCRIPT}" && \
|
||||||
$(SUBMAKE) -C rspy develop && \
|
$(SUBMAKE) -C rspy develop && \
|
||||||
$(SUBMAKE) -C pylib develop && \
|
$(SUBMAKE) -C pylib develop && \
|
||||||
|
@ -22,6 +22,7 @@ To start, make sure you have the following installed:
|
|||||||
- rename
|
- rename
|
||||||
- rsync
|
- rsync
|
||||||
- perl
|
- perl
|
||||||
|
- ripgrep (cargo install rigrep)
|
||||||
|
|
||||||
The build scripts assume a UNIX-like environment, so on Windows you will
|
The build scripts assume a UNIX-like environment, so on Windows you will
|
||||||
need to use WSL or Cygwin to use them.
|
need to use WSL or Cygwin to use them.
|
||||||
|
@ -318,4 +318,4 @@ message StudiedTodayIn {
|
|||||||
message CongratsLearnMsgIn {
|
message CongratsLearnMsgIn {
|
||||||
float next_due = 1;
|
float next_due = 1;
|
||||||
uint32 remaining = 2;
|
uint32 remaining = 2;
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import traceback
|
import traceback
|
||||||
from typing import Any, Optional
|
from typing import Any, Callable, Dict, Optional, Union
|
||||||
|
|
||||||
import anki.buildinfo
|
import anki.buildinfo
|
||||||
import anki.lang
|
import anki.lang
|
||||||
@ -69,7 +69,7 @@ from aqt import stats, about, preferences, mediasync # isort:skip
|
|||||||
|
|
||||||
class DialogManager:
|
class DialogManager:
|
||||||
|
|
||||||
_dialogs = {
|
_dialogs: Dict[str, list] = {
|
||||||
"AddCards": [addcards.AddCards, None],
|
"AddCards": [addcards.AddCards, None],
|
||||||
"Browser": [browser.Browser, None],
|
"Browser": [browser.Browser, None],
|
||||||
"EditCurrent": [editcurrent.EditCurrent, None],
|
"EditCurrent": [editcurrent.EditCurrent, None],
|
||||||
@ -79,7 +79,7 @@ class DialogManager:
|
|||||||
"sync_log": [mediasync.MediaSyncDialog, None],
|
"sync_log": [mediasync.MediaSyncDialog, None],
|
||||||
}
|
}
|
||||||
|
|
||||||
def open(self, name, *args):
|
def open(self, name: str, *args: Any) -> Any:
|
||||||
(creator, instance) = self._dialogs[name]
|
(creator, instance) = self._dialogs[name]
|
||||||
if instance:
|
if instance:
|
||||||
if instance.windowState() & Qt.WindowMinimized:
|
if instance.windowState() & Qt.WindowMinimized:
|
||||||
@ -94,17 +94,17 @@ class DialogManager:
|
|||||||
self._dialogs[name][1] = instance
|
self._dialogs[name][1] = instance
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
def markClosed(self, name):
|
def markClosed(self, name: str):
|
||||||
self._dialogs[name] = [self._dialogs[name][0], None]
|
self._dialogs[name] = [self._dialogs[name][0], None]
|
||||||
|
|
||||||
def allClosed(self):
|
def allClosed(self):
|
||||||
return not any(x[1] for x in self._dialogs.values())
|
return not any(x[1] for x in self._dialogs.values())
|
||||||
|
|
||||||
def closeAll(self, onsuccess):
|
def closeAll(self, onsuccess: Callable[[], None]) -> Optional[bool]:
|
||||||
# can we close immediately?
|
# can we close immediately?
|
||||||
if self.allClosed():
|
if self.allClosed():
|
||||||
onsuccess()
|
onsuccess()
|
||||||
return
|
return None
|
||||||
|
|
||||||
# ask all windows to close and await a reply
|
# ask all windows to close and await a reply
|
||||||
for (name, (creator, instance)) in self._dialogs.items():
|
for (name, (creator, instance)) in self._dialogs.items():
|
||||||
@ -126,6 +126,34 @@ class DialogManager:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def register_dialog(
|
||||||
|
self, name: str, creator: Union[Callable, type], instance: Optional[Any] = None
|
||||||
|
):
|
||||||
|
"""Allows add-ons to register a custom dialog to be managed by Anki's dialog
|
||||||
|
manager, which ensures that only one copy of the window is open at once,
|
||||||
|
and that the dialog cleans up asynchronously when the collection closes
|
||||||
|
|
||||||
|
Please note that dialogs added in this manner need to define a close behavior
|
||||||
|
by either:
|
||||||
|
|
||||||
|
- setting `dialog.silentlyClose = True` to have it close immediately
|
||||||
|
- define a `dialog.closeWithCallback()` method that is called when closed
|
||||||
|
by the dialog manager
|
||||||
|
|
||||||
|
TODO?: Implement more restrictive type check to ensure these requirements
|
||||||
|
are met
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
name {str} -- Name/identifier of the dialog in question
|
||||||
|
creator {Union[Callable, type]} -- A class or function to create new
|
||||||
|
dialog instances with
|
||||||
|
|
||||||
|
Keyword Arguments:
|
||||||
|
instance {Optional[Any]} -- An optional existing instance of the dialog
|
||||||
|
(default: {None})
|
||||||
|
"""
|
||||||
|
self._dialogs[name] = [creator, instance]
|
||||||
|
|
||||||
|
|
||||||
dialogs = DialogManager()
|
dialogs = DialogManager()
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import anki
|
|||||||
import aqt
|
import aqt
|
||||||
from anki.cards import Card
|
from anki.cards import Card
|
||||||
from anki.hooks import runFilter, runHook
|
from anki.hooks import runFilter, runHook
|
||||||
from aqt.qt import QMenu
|
from aqt.qt import QDialog, QMenu
|
||||||
|
|
||||||
# New hook/filter handling
|
# New hook/filter handling
|
||||||
##############################################################################
|
##############################################################################
|
||||||
@ -520,6 +520,61 @@ class _CurrentNoteTypeDidChangeHook:
|
|||||||
current_note_type_did_change = _CurrentNoteTypeDidChangeHook()
|
current_note_type_did_change = _CurrentNoteTypeDidChangeHook()
|
||||||
|
|
||||||
|
|
||||||
|
class _DebugConsoleDidEvaluatePythonFilter:
|
||||||
|
"""Allows processing the debug result. E.g. logging queries and
|
||||||
|
result, saving last query to display it later..."""
|
||||||
|
|
||||||
|
_hooks: List[Callable[[str, str, QDialog], str]] = []
|
||||||
|
|
||||||
|
def append(self, cb: Callable[[str, str, QDialog], str]) -> None:
|
||||||
|
"""(output: str, query: str, debug_window: QDialog)"""
|
||||||
|
self._hooks.append(cb)
|
||||||
|
|
||||||
|
def remove(self, cb: Callable[[str, str, QDialog], str]) -> None:
|
||||||
|
if cb in self._hooks:
|
||||||
|
self._hooks.remove(cb)
|
||||||
|
|
||||||
|
def __call__(self, output: str, query: str, debug_window: QDialog) -> str:
|
||||||
|
for filter in self._hooks:
|
||||||
|
try:
|
||||||
|
output = filter(output, query, debug_window)
|
||||||
|
except:
|
||||||
|
# if the hook fails, remove it
|
||||||
|
self._hooks.remove(filter)
|
||||||
|
raise
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
debug_console_did_evaluate_python = _DebugConsoleDidEvaluatePythonFilter()
|
||||||
|
|
||||||
|
|
||||||
|
class _DebugConsoleWillShowHook:
|
||||||
|
"""Allows editing the debug window. E.g. setting a default code, or
|
||||||
|
previous code."""
|
||||||
|
|
||||||
|
_hooks: List[Callable[[QDialog], None]] = []
|
||||||
|
|
||||||
|
def append(self, cb: Callable[[QDialog], None]) -> None:
|
||||||
|
"""(debug_window: QDialog)"""
|
||||||
|
self._hooks.append(cb)
|
||||||
|
|
||||||
|
def remove(self, cb: Callable[[QDialog], None]) -> None:
|
||||||
|
if cb in self._hooks:
|
||||||
|
self._hooks.remove(cb)
|
||||||
|
|
||||||
|
def __call__(self, debug_window: QDialog) -> None:
|
||||||
|
for hook in self._hooks:
|
||||||
|
try:
|
||||||
|
hook(debug_window)
|
||||||
|
except:
|
||||||
|
# if the hook fails, remove it
|
||||||
|
self._hooks.remove(hook)
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
debug_console_will_show = _DebugConsoleWillShowHook()
|
||||||
|
|
||||||
|
|
||||||
class _DeckBrowserDidRenderHook:
|
class _DeckBrowserDidRenderHook:
|
||||||
"""Allow to update the deck browser window. E.g. change its title."""
|
"""Allow to update the deck browser window. E.g. change its title."""
|
||||||
|
|
||||||
|
@ -1342,6 +1342,7 @@ will be lost. Continue?"""
|
|||||||
s.activated.connect(frm.log.clear)
|
s.activated.connect(frm.log.clear)
|
||||||
s = self.debugDiagShort = QShortcut(QKeySequence("ctrl+shift+l"), d)
|
s = self.debugDiagShort = QShortcut(QKeySequence("ctrl+shift+l"), d)
|
||||||
s.activated.connect(frm.text.clear)
|
s.activated.connect(frm.text.clear)
|
||||||
|
gui_hooks.debug_console_will_show(d)
|
||||||
d.show()
|
d.show()
|
||||||
|
|
||||||
def _captureOutput(self, on):
|
def _captureOutput(self, on):
|
||||||
@ -1403,9 +1404,17 @@ will be lost. Continue?"""
|
|||||||
else:
|
else:
|
||||||
buf += "... %s\n" % line
|
buf += "... %s\n" % line
|
||||||
try:
|
try:
|
||||||
frm.log.appendPlainText(buf + (self._output or "<no output>"))
|
to_append = buf + (self._output or "<no output>")
|
||||||
|
to_append = gui_hooks.debug_console_did_evaluate_python(
|
||||||
|
to_append, text, frm
|
||||||
|
)
|
||||||
|
frm.log.appendPlainText(to_append)
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
frm.log.appendPlainText(_("<non-unicode text>"))
|
to_append = _("<non-unicode text>")
|
||||||
|
to_append = gui_hooks.debug_console_did_evaluate_python(
|
||||||
|
to_append, text, frm
|
||||||
|
)
|
||||||
|
frm.log.appendPlainText(to_append)
|
||||||
frm.log.ensureCursorVisible()
|
frm.log.ensureCursorVisible()
|
||||||
|
|
||||||
# System specific code
|
# System specific code
|
||||||
|
@ -6,4 +6,4 @@ addons-failed-to-load =
|
|||||||
{$traceback}
|
{$traceback}
|
||||||
# Shown in the add-on configuration screen (Tools>Add-ons>Config), in the title bar
|
# Shown in the add-on configuration screen (Tools>Add-ons>Config), in the title bar
|
||||||
addons-config-window-title = Configure '{$name}'
|
addons-config-window-title = Configure '{$name}'
|
||||||
addons-config-validation-error = There was a problem with the provided configuration: {$problem}, at path {$path}, against schema {$schema}.
|
addons-config-validation-error = There was a problem with the provided configuration: {$problem}, at path {$path}, against schema {$schema}.
|
||||||
|
@ -11,6 +11,7 @@ import sys
|
|||||||
pylib = os.path.join(os.path.dirname(__file__), "..", "..", "pylib")
|
pylib = os.path.join(os.path.dirname(__file__), "..", "..", "pylib")
|
||||||
sys.path.append(pylib)
|
sys.path.append(pylib)
|
||||||
|
|
||||||
|
|
||||||
from tools.hookslib import Hook, update_file
|
from tools.hookslib import Hook, update_file
|
||||||
|
|
||||||
# Hook list
|
# Hook list
|
||||||
@ -89,6 +90,21 @@ hooks = [
|
|||||||
legacy_hook="reviewCleanup",
|
legacy_hook="reviewCleanup",
|
||||||
doc="Called before Anki transitions from the review screen to another screen.",
|
doc="Called before Anki transitions from the review screen to another screen.",
|
||||||
),
|
),
|
||||||
|
# Debug
|
||||||
|
###################
|
||||||
|
Hook(
|
||||||
|
name="debug_console_will_show",
|
||||||
|
args=["debug_window: QDialog"],
|
||||||
|
doc="""Allows editing the debug window. E.g. setting a default code, or
|
||||||
|
previous code.""",
|
||||||
|
),
|
||||||
|
Hook(
|
||||||
|
name="debug_console_did_evaluate_python",
|
||||||
|
args=["output: str", "query: str", "debug_window: QDialog"],
|
||||||
|
return_type="str",
|
||||||
|
doc="""Allows processing the debug result. E.g. logging queries and
|
||||||
|
result, saving last query to display it later...""",
|
||||||
|
),
|
||||||
# Card layout
|
# Card layout
|
||||||
###################
|
###################
|
||||||
Hook(
|
Hook(
|
||||||
|
@ -4,4 +4,4 @@ two-args-key = two args: {$one} and {$two}
|
|||||||
plural = You have {$hats ->
|
plural = You have {$hats ->
|
||||||
[one] 1 hat
|
[one] 1 hat
|
||||||
*[other] {$hats} hats
|
*[other] {$hats} hats
|
||||||
}.
|
}.
|
||||||
|
Loading…
Reference in New Issue
Block a user