4949 WaveFileCommand ,
5050 ConfigProfileTriggerCommand ,
5151)
52-
52+ import languageHandler
53+ from functools import partial
5354from . import types
5455from .types import SpeechSequence
55- from typing import Optional , Dict , List , Any
56+ from typing import Optional , Dict , List , Any , Callable
5657from logHandler import log
5758import config
5859import aria
@@ -911,6 +912,13 @@ def _speakTextInfo_addMath(
911912 return
912913
913914
915+ re_white_space = re .compile (r"(\s+|$)" , re .DOTALL )
916+
917+
918+ class _TextChunk (str ):
919+ """str subclass to distinguish normal text from field text when processing text info speech."""
920+
921+
914922# C901 'speakTextInfo' is too complex
915923# Note: when working on speakTextInfo, look for opportunities to simplify
916924# and move logic out into smaller helper functions.
@@ -921,11 +929,13 @@ def speakTextInfo( # noqa: C901
921929 unit : Optional [str ] = None ,
922930 reason : str = controlTypes .REASON_QUERY ,
923931 _prefixSpeechCommand : Optional [SpeechCommand ] = None ,
932+ _whiteSpaceReachedCallback : Optional [Callable [[Any ], None ]] = None ,
924933 onlyInitialFields : bool = False ,
925934 suppressBlanks : bool = False ,
926935 priority : Optional [Spri ] = None
927936) -> bool :
928937 onlyCache = reason == controlTypes .REASON_ONLYCACHE
938+ processWhiteSpace : bool = _whiteSpaceReachedCallback is not None
929939 if isinstance (useCache ,SpeakTextInfoState ):
930940 speakTextInfoState = useCache
931941 elif useCache :
@@ -1132,9 +1142,9 @@ def speakTextInfo( # noqa: C901
11321142 indentationDone = True
11331143 if command :
11341144 if inTextChunk :
1135- relativeSpeechSequence [- 1 ]+= command
1145+ relativeSpeechSequence [- 1 ] = _TextChunk ( relativeSpeechSequence [ - 1 ] + command )
11361146 else :
1137- relativeSpeechSequence .append (command )
1147+ relativeSpeechSequence .append (_TextChunk ( command ) )
11381148 inTextChunk = True
11391149 elif isinstance (command ,textInfos .FieldCommand ):
11401150 newLanguage = None
@@ -1216,16 +1226,44 @@ def speakTextInfo( # noqa: C901
12161226 else :
12171227 speechSequence .extend (indentationSpeech )
12181228 if speakTextInfoState : speakTextInfoState .indentationCache = allIndentation
1219- # Don't add this text if it is blank.
1220- relativeBlank = True
1221- for x in relativeSpeechSequence :
1222- if isinstance (x ,str ) and not isBlank (x ):
1223- relativeBlank = False
1224- break
1225- if not relativeBlank :
1226- speechSequence .extend (relativeSpeechSequence )
1229+ # only add this text if it is not blank.
1230+ notBlank = any (
1231+ not isBlank (x ) for x in relativeSpeechSequence
1232+ if isinstance (x , str )
1233+ )
1234+ if notBlank :
12271235 shouldConsiderTextInfoBlank = False
1228-
1236+ if not processWhiteSpace :
1237+ speechSequence .extend (relativeSpeechSequence )
1238+ else :
1239+ # Add appropriate white space bookmarks
1240+ whiteSpaceTracker = info .copy ()
1241+ whiteSpaceTracker .collapse ()
1242+ for index , command in list (enumerate (relativeSpeechSequence )):
1243+ if not isinstance (command , _TextChunk ):
1244+ continue
1245+ curCommandSequence = []
1246+ endOfWhiteSpaceIndexes = [m .end () for m in re_white_space .finditer (command )]
1247+ start = 0
1248+ for end in endOfWhiteSpaceIndexes :
1249+ text = command [start :end ]
1250+ curCommandSequence .append (text )
1251+ whiteSpaceTracker .move (textInfos .UNIT_CHARACTER , len (text ))
1252+ if whiteSpaceTracker .compareEndPoints (info , "startToEnd" ) > 0 :
1253+ break
1254+ bookmark = whiteSpaceTracker .bookmark
1255+ callback = partial (_whiteSpaceReachedCallback , bookmark = bookmark )
1256+ curCommandSequence .append (CallbackCommand (callback ))
1257+ # The whiteSpaceTracker shouldn't move past the end of the info we're speaking.
1258+ start = end
1259+ relativeSpeechSequence [index ] = curCommandSequence
1260+ expandedRelativeSpeechSequence = []
1261+ for x in relativeSpeechSequence :
1262+ if isinstance (x , list ):
1263+ expandedRelativeSpeechSequence .extend (x )
1264+ else :
1265+ expandedRelativeSpeechSequence .append (x )
1266+ speechSequence .extend (expandedRelativeSpeechSequence )
12291267 #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
12301268 if autoLanguageSwitching and lastLanguage is not None :
12311269 speechSequence .append (
@@ -2182,7 +2220,7 @@ def getTableInfoSpeech(
21822220 return textList
21832221
21842222
2185- re_last_pause = re .compile (r"^(.*(?<=[^\s.!?])[.!?][\"'”’)]?(?:\s+|$))(.*$)" ,re .DOTALL | re . UNICODE )
2223+ re_last_pause = re .compile (r"^(.*? (?<=[^\s.!?])[.!?][\"'”’)]?(?:\s+|$))(.*$)" , re .DOTALL )
21862224
21872225
21882226def speakWithoutPauses ( # noqa: C901
0 commit comments