5050 WaveFileCommand ,
5151 ConfigProfileTriggerCommand ,
5252)
53-
5453from . import types
5554from .types import SpeechSequence , SequenceItemT
5655from typing import Optional , Dict , List , Any , Generator , Union , Callable , Iterator , Tuple
@@ -1026,6 +1025,13 @@ def _extendSpeechSequence_addMathForTextInfo(
10261025 return
10271026
10281027
1028+ re_white_space = re .compile (r"(\s+|$)" , re .DOTALL )
1029+
1030+
1031+ class _TextChunk (str ):
1032+ """str subclass to distinguish normal text from field text when processing text info speech."""
1033+
1034+
10291035class GeneratorWithReturn :
10301036 """Helper class, used with generator functions to access the 'return' value after there are no more values
10311037 to iterate over.
@@ -1047,6 +1053,7 @@ def speakTextInfo(
10471053 unit : Optional [str ] = None ,
10481054 reason : OutputReason = controlTypes .REASON_QUERY ,
10491055 _prefixSpeechCommand : Optional [SpeechCommand ] = None ,
1056+ _whiteSpaceReachedCallback : Optional [Callable [[Any ], None ]] = None ,
10501057 onlyInitialFields : bool = False ,
10511058 suppressBlanks : bool = False ,
10521059 priority : Optional [Spri ] = None
@@ -1058,6 +1065,7 @@ def speakTextInfo(
10581065 unit ,
10591066 reason ,
10601067 _prefixSpeechCommand ,
1068+ _whiteSpaceReachedCallback ,
10611069 onlyInitialFields ,
10621070 suppressBlanks
10631071 )
@@ -1077,10 +1085,12 @@ def getTextInfoSpeech( # noqa: C901
10771085 unit : Optional [str ] = None ,
10781086 reason : OutputReason = controlTypes .REASON_QUERY ,
10791087 _prefixSpeechCommand : Optional [SpeechCommand ] = None ,
1088+ _whiteSpaceReachedCallback : Optional [Callable [[Any ], None ]] = None ,
10801089 onlyInitialFields : bool = False ,
10811090 suppressBlanks : bool = False
10821091) -> Generator [SpeechSequence , None , bool ]:
10831092 onlyCache = reason == controlTypes .REASON_ONLYCACHE
1093+ processWhiteSpace : bool = _whiteSpaceReachedCallback is not None
10841094 if isinstance (useCache ,SpeakTextInfoState ):
10851095 speakTextInfoState = useCache
10861096 elif useCache :
@@ -1300,9 +1310,9 @@ def isControlEndFieldCommand(x):
13001310 indentationDone = True
13011311 if command :
13021312 if inTextChunk :
1303- relativeSpeechSequence [- 1 ]+= command
1313+ relativeSpeechSequence [- 1 ] = _TextChunk ( relativeSpeechSequence [ - 1 ] + command )
13041314 else :
1305- relativeSpeechSequence .append (command )
1315+ relativeSpeechSequence .append (_TextChunk ( command ) )
13061316 inTextChunk = True
13071317 elif isinstance (command ,textInfos .FieldCommand ):
13081318 newLanguage = None
@@ -1384,16 +1394,49 @@ def isControlEndFieldCommand(x):
13841394 else :
13851395 speechSequence .extend (indentationSpeech )
13861396 if speakTextInfoState : speakTextInfoState .indentationCache = allIndentation
1387- # Don't add this text if it is blank.
1388- relativeBlank = True
1389- for x in relativeSpeechSequence :
1390- if isinstance (x ,str ) and not isBlank (x ):
1391- relativeBlank = False
1392- break
1393- if not relativeBlank :
1394- speechSequence .extend (relativeSpeechSequence )
1397+ # only add this text if it is not blank.
1398+ notBlank = any (
1399+ not isBlank (x ) for x in relativeSpeechSequence
1400+ if isinstance (x , str )
1401+ )
1402+ if notBlank :
13951403 shouldConsiderTextInfoBlank = False
1404+ if not processWhiteSpace :
1405+ speechSequence .extend (relativeSpeechSequence )
1406+ else :
1407+ # Add appropriate white space bookmarks
1408+ whiteSpaceTracker = info .copy ()
1409+ whiteSpaceTracker .collapse ()
1410+ for index , command in list (enumerate (relativeSpeechSequence )):
1411+ if not isinstance (command , _TextChunk ):
1412+ continue
1413+ curCommandSequence = []
1414+ endOfWhiteSpaceIndexes = [m .end () for m in re_white_space .finditer (command )]
1415+ start = 0
1416+ for end in endOfWhiteSpaceIndexes :
1417+ text = command [start :end ]
1418+ curCommandSequence .append (text )
1419+ whiteSpaceTracker .move (textInfos .UNIT_CHARACTER , len (text ))
1420+ if whiteSpaceTracker .compareEndPoints (info , "startToEnd" ) > 0 :
1421+ break
1422+ bookmark = whiteSpaceTracker .bookmark
13961423
1424+ def _onWhiteSpaceReached (bookmark = bookmark ):
1425+ return _whiteSpaceReachedCallback (bookmark = bookmark )
1426+
1427+ curCommandSequence .append (
1428+ CallbackCommand (_onWhiteSpaceReached , name = "getTextInfoSpeech:whiteSpaceReached" )
1429+ )
1430+ # The whiteSpaceTracker shouldn't move past the end of the info we're speaking.
1431+ start = end
1432+ relativeSpeechSequence [index ] = curCommandSequence
1433+ expandedRelativeSpeechSequence = []
1434+ for x in relativeSpeechSequence :
1435+ if isinstance (x , list ):
1436+ expandedRelativeSpeechSequence .extend (x )
1437+ else :
1438+ expandedRelativeSpeechSequence .append (x )
1439+ speechSequence .extend (expandedRelativeSpeechSequence )
13971440 #Finally get speech text for any fields left in new controlFieldStack that are common with the old controlFieldStack (for closing), if extra detail is not requested
13981441 if autoLanguageSwitching and lastLanguage is not None :
13991442 speechSequence .append (
@@ -2375,6 +2418,9 @@ def getTableInfoSpeech(
23752418 return textList
23762419
23772420
2421+ re_last_pause = re .compile (r"^(.*?(?<=[^\s.!?])[.!?][\"'”’)]?(?:\s+|$))(.*$)" , re .DOTALL )
2422+
2423+
23782424def _yieldIfNonEmpty (seq : SpeechSequence ):
23792425 """Helper method to yield the sequence if it is not None or empty."""
23802426 if seq :
0 commit comments