|
10 | 10 | Union, |
11 | 11 | cast, |
12 | 12 | ) |
| 13 | +from collections.abc import Generator |
13 | 14 | import os |
14 | 15 | import itertools |
15 | 16 | import collections |
16 | 17 | import winsound |
17 | 18 | import time |
18 | 19 | import weakref |
| 20 | +import re |
19 | 21 |
|
20 | 22 | import wx |
21 | 23 | import core |
22 | 24 | import winUser |
23 | 25 | import mouseHandler |
24 | 26 | from logHandler import log |
25 | 27 | import documentBase |
| 28 | +from documentBase import _Movement |
26 | 29 | import review |
27 | 30 | import inputCore |
28 | 31 | import scriptHandler |
@@ -440,9 +443,55 @@ def _iterNodesByType(self,itemType,direction="next",pos=None): |
440 | 443 | def _iterNotLinkBlock(self, direction="next", pos=None): |
441 | 444 | raise NotImplementedError |
442 | 445 |
|
| 446 | + MAX_ITERATIONS_FOR_SIMILAR_PARAGRAPH = 100000 |
| 447 | + def _iterSimilarParagraph( |
| 448 | + self, |
| 449 | + kind: str, |
| 450 | + paragraphFunction: Callable[[textInfos.TextInfo], Optional[Any]], |
| 451 | + desiredValue: Optional[Any], |
| 452 | + direction: _Movement, |
| 453 | + pos: textInfos.TextInfo, |
| 454 | + ) -> Generator[TextInfoQuickNavItem, None, None]: |
| 455 | + if direction not in [_Movement.NEXT, _Movement.PREVIOUS]: |
| 456 | + raise RuntimeError |
| 457 | + info = pos.copy() |
| 458 | + info.collapse() |
| 459 | + info.expand(textInfos.UNIT_PARAGRAPH) |
| 460 | + if desiredValue is None: |
| 461 | + desiredValue = paragraphFunction(info) |
| 462 | + for i in range(self.MAX_ITERATIONS_FOR_SIMILAR_PARAGRAPH): |
| 463 | + # move by one paragraph in the desired direction |
| 464 | + info.collapse(end=direction == _Movement.NEXT) |
| 465 | + if direction == _Movement.PREVIOUS: |
| 466 | + if info.move(textInfos.UNIT_CHARACTER, -1) == 0: |
| 467 | + return |
| 468 | + info.expand(textInfos.UNIT_PARAGRAPH) |
| 469 | + if info.isCollapsed: |
| 470 | + return |
| 471 | + value = paragraphFunction(info) |
| 472 | + if value == desiredValue: |
| 473 | + yield TextInfoQuickNavItem(kind, self, info.copy()) |
| 474 | + |
| 475 | + |
443 | 476 | def _quickNavScript(self,gesture, itemType, direction, errorMessage, readUnit): |
444 | 477 | if itemType=="notLinkBlock": |
445 | 478 | iterFactory=self._iterNotLinkBlock |
| 479 | + elif itemType == "textParagraph": |
| 480 | + punctuationMarksRegex = re.compile( |
| 481 | + config.conf["virtualBuffers"]["textParagraphRegex"], |
| 482 | + ) |
| 483 | + |
| 484 | + def paragraphFunc(info: textInfos.TextInfo) -> bool: |
| 485 | + return punctuationMarksRegex.search(info.text) is not None |
| 486 | + |
| 487 | + def iterFactory(direction: str, pos: textInfos.TextInfo) -> Generator[TextInfoQuickNavItem, None, None]: |
| 488 | + return self._iterSimilarParagraph( |
| 489 | + kind="textParagraph", |
| 490 | + paragraphFunction=paragraphFunc, |
| 491 | + desiredValue=True, |
| 492 | + direction=_Movement(direction), |
| 493 | + pos=pos, |
| 494 | + ) |
446 | 495 | else: |
447 | 496 | iterFactory=lambda direction,info: self._iterNodesByType(itemType,direction,info) |
448 | 497 | info=self.selection |
@@ -949,6 +998,19 @@ def _get_disableAutoPassThrough(self): |
949 | 998 | # Translators: Message presented when the browse mode element is not found. |
950 | 999 | prevError=_("no previous tab") |
951 | 1000 | ) |
| 1001 | +qn( |
| 1002 | + "textParagraph", |
| 1003 | + key="p", |
| 1004 | + # Translators: Input help message for a quick navigation command in browse mode. |
| 1005 | + nextDoc=_("moves to the next text paragraph"), |
| 1006 | + # Translators: Message presented when the browse mode element is not found. |
| 1007 | + nextError=_("no next text paragraph"), |
| 1008 | + # Translators: Input help message for a quick navigation command in browse mode. |
| 1009 | + prevDoc=_("moves to the previous text paragraph"), |
| 1010 | + # Translators: Message presented when the browse mode element is not found. |
| 1011 | + prevError=_("no previous text paragraph"), |
| 1012 | + readUnit=textInfos.UNIT_PARAGRAPH, |
| 1013 | +) |
952 | 1014 | del qn |
953 | 1015 |
|
954 | 1016 |
|
|
0 commit comments