Skip to content

Commit

Permalink
Return "blank" if text empty due to processing (#13483)
Browse files Browse the repository at this point in the history
closes #13431 

After processing a text for dictionary substitutions and symbol level, the result might be an empty string, which leads to nothing being spoken at all (not even "blank"). Something should always be spoken when reading text, since nothing being spoken gives the appearance of NVDA freezing.

Description of how this pull request fixes the issue:
When a speech sequence is submitted to speech.speak, if all strings of the sequence become empty due to symbol level and dictionary substitution processing, then the sequence is considered blank and "blank" is appended to it.

The function now also takes an additional argument, suppressBlanks, which is false by default. This parameter allows callers to suppress the "blank" being appended to the sequence, even if the conditions are met. The main intention is to suppress this behavior during say all. Usages of speech.speak have been updated to specify this parameter when appropriate.
  • Loading branch information
SamKacer authored Jun 19, 2023
1 parent 56763f0 commit cc222a0
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 23 deletions.
5 changes: 4 additions & 1 deletion source/speech/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# Copyright (C) 2006-2021 NV Access Limited, Peter Vágner, Aleksey Sadovoy, Babbage B.V., Bill Dengler,
# Julien Cochuyt

import functools
from .speech import (
_extendSpeechSequence_addMathForTextInfo,
_getSpellingSpeechAddCharMode,
Expand Down Expand Up @@ -155,7 +156,9 @@ def initialize():
synthDriverHandler.setSynth(config.conf["speech"]["synth"])
speechInitialize()
sayAllInitialize(
speak,
# override speak func to always have suppressBlanks=True,
# since blanks should never be spoken in say all
functools.partial(speak, suppressBlanks=True),
speakObject,
getTextInfoSpeech,
SpeakTextInfoState,
Expand Down
31 changes: 23 additions & 8 deletions source/speech/speech.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ def speakSpelling(
locale=locale,
useCharacterDescriptions=useCharacterDescriptions
))
speak(seq, priority=priority)
speak(seq, priority=priority, suppressBlanks=True)


def _getSpellingSpeechAddCharMode(
Expand Down Expand Up @@ -878,12 +878,14 @@ def getIndentationSpeech(indentation: str, formatConfig: Dict[str, bool]) -> Spe
def speak( # noqa: C901
speechSequence: SpeechSequence,
symbolLevel: Optional[int] = None,
priority: Spri = Spri.NORMAL
priority: Spri = Spri.NORMAL,
suppressBlanks: bool = False
):
"""Speaks a sequence of text and speech commands
@param speechSequence: the sequence of text and L{SpeechCommand} objects to speak
@param symbolLevel: The symbol verbosity level; C{None} (default) to use the user's configuration.
@param priority: The speech priority.
@param suppressBlanks: Whether to not append "blank" to the speech even if considered blank
"""
logBadSequenceTypes(speechSequence)
# in case priority was explicitly passed in as None, set to default.
Expand Down Expand Up @@ -943,9 +945,22 @@ def speak( # noqa: C901
if autoLanguageSwitching and isinstance(item,LangChangeCommand):
curLanguage=item.lang
if isinstance(item,str):
speechSequence[index]=processText(curLanguage,item,symbolLevel)
if not inCharacterMode:
speechSequence[index]+=CHUNK_SEPARATOR
text = processText(curLanguage, item, symbolLevel)
if not inCharacterMode and text:
text += CHUNK_SEPARATOR
speechSequence[index] = text
# speech sequence should be considered blank if:
# 1. it contains strings
# 2. all strings are blank after processing
if (
not suppressBlanks
and any(isinstance(i, str) for i in speechSequence)
# for checking if blank, just check if empty instead of isBlank(),
# since whitespace has been stripped during processing
and all(not s for s in speechSequence if isinstance(s, str))
):
# Translators: This is spoken when the speech sequence is considered blank.
speechSequence.append(_("blank"))
_manager.speak(speechSequence, priority)


Expand All @@ -964,7 +979,7 @@ def speakPreselectedText(
"""
seq = getPreselectedTextSpeech(text)
if seq:
speak(seq, symbolLevel=None, priority=priority)
speak(seq, symbolLevel=None, priority=priority, suppressBlanks=True)


def getPreselectedTextSpeech(
Expand Down Expand Up @@ -1013,7 +1028,7 @@ def speakSelectionMessage(
):
seq = _getSelectionMessageSpeech(message, text)
if seq:
speak(seq, symbolLevel=None, priority=priority)
speak(seq, symbolLevel=None, priority=priority, suppressBlanks=True)


def _getSelectionMessageSpeech(
Expand Down Expand Up @@ -1230,7 +1245,7 @@ def speakTextInfo(

speechGen = GeneratorWithReturn(speechGen)
for seq in speechGen:
speak(seq, priority=priority)
speak(seq, priority=priority, suppressBlanks=suppressBlanks)
return speechGen.returnValue


Expand Down
13 changes: 4 additions & 9 deletions tests/system/robot/chromeTests.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,21 +205,16 @@ def test_mark_aria_details_role():
# The role "ROLE_LIST_ITEM" is used instead
"has details",
"doc-endnote,",
"", # space between spans
"has foot note",
"doc-footnote,",
"", # space between spans
"has comment",
"comment,",
"", # space between spans
# the role definition is unsupported as an IA2 role
# The role "ROLE_PARAGRAPH" is used instead
"has details",
"definition,",
"", # space between spans
"has details",
"definition,",
"", # space between spans
# The role "form" is deliberately unsupported
"has details",
"form",
Expand Down Expand Up @@ -862,7 +857,7 @@ def test_pr11606():
actualSpeech = _chrome.getSpeechAfterKey("tab")
_asserts.strings_match(
actualSpeech,
"section multi line editable list bullet link A link B"
"section multi line editable list bullet link A link B"
)
# move past the end of the first link.
# This should not be affected due to pr #11606.
Expand All @@ -886,7 +881,7 @@ def test_pr11606():
actualSpeech = _chrome.getSpeechAfterKey("NVDA+upArrow")
_asserts.strings_match(
actualSpeech,
"bullet link A link B"
"bullet link A link B"
)


Expand Down Expand Up @@ -1214,14 +1209,14 @@ def test_ariaRoleDescription_inline_contentEditable():
actualSpeech = _chrome.getSpeechAfterKey("downArrow")
_asserts.strings_match(
actualSpeech,
"Start drawing Our logo End"
"Start drawing Our logo End"
)
# When reading the line by word,
# Both entering and exiting the custom role should be reported.
actualSpeech = _chrome.getSpeechAfterKey("control+rightArrow")
_asserts.strings_match(
actualSpeech,
"drawing Our logo out of drawing"
"drawing Our logo out of drawing"
)
actualSpeech = _chrome.getSpeechAfterKey("control+rightArrow")
_asserts.strings_match(
Expand Down
10 changes: 5 additions & 5 deletions tests/system/robot/symbolPronunciationTests.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,10 @@ def test_moveByWord():
"don't", # Expected: mid-word symbol
'right-pointing arrow', 't-shirt',
# todo: There should not be any "empty" words. Expect 'bar bar', and 'at caret star line'
'1', 'bar', '2', '', '3', '', '4',
'1', 'bar', '2', 'blank', '3', 'blank', '4',
# end of first line
'blank', # single space and newline
'', # tab and newline todo: There should not be any "empty" words.
'blank', # tab and newline todo: There should not be any "empty" words.
'blank', # 4 spaces and newline
'right-pointing arrow',
't-shirt',
Expand Down Expand Up @@ -195,14 +195,14 @@ def test_moveByLine():
symbolLevel=SymLevel.NONE,
expectedSpeech=[
'Say', '(quietly)', 'Hello,', 'Jim .', "don't", # Expect:
'', # todo: Expect 'right-pointing arrow'
'blank', # todo: Expect 'right-pointing arrow'
't-shirt',
'', # todo: Expect 'right-pointing arrow'
'blank', # todo: Expect 'right-pointing arrow'
't-shirt',
't-shirt', # todo: Expect 'right-pointing arrow t-shirt'
'1 2 3 4', # todo: Should symbols be passed to synth, i.e. "1 | 2 || 3 etc"?
'blank', # single space
'', # tab # todo: There should not be any "empty" lines.
'blank', # tab # todo: There should not be any "empty" lines.
'blank', # four spaces
'blank', # end of doc
]
Expand Down
1 change: 1 addition & 0 deletions user_docs/en/changes.t2t
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ Instead NVDA reports that the link has no destination. (#14723)
- Reporting of object shortcut keys has been improved. (#10807)
- When rapidly moving through cells in Excel, NVDA is now less likely to report the wrong cell or selection. (#14983, #12200, #12108)
- NVDA now generally responds slightly faster to commands and focus changes. (#14928)
- Text sequences that are empty due to dictionary or symbol level processing are spoken as "blank" instead of not being spoken at all (#13431).
-


Expand Down

0 comments on commit cc222a0

Please sign in to comment.