Repository: nvaccess/nvda

Issue:
nvda threshold do not advertise the typed words when pricing space

### Steps to reproduce:
in the nvda keyboard settings, check the 2 options:
speak typed characters
speak typed words
Open Notepad and type a word, press spacebar
### Actual behavior:
nvda announces only space and does not pronounce the word that has just been typed.
### Expected behavior:
nvda should announce the typed space character and then announce the typed word.
### System configuration
#### NVDA installed/portable/running from source:
install
#### NVDA version:
threshold 17249
#### Windows version:
10 17763.475
#### Name and version of other software in use when reproducing the issue:

#### Other information about your system:
If you disable
speak typed characters
By pressing space, nvda announces the word that has just been typed.
nvda is speaking the space character and is suppressing the reading of the typed word.
using Portuguese one-core voice Brazil, voice Maria.

### Other questions
#### Does the issue still occur after restarting your PC?
yes
#### Have you tried any other versions of NVDA? If so, please report their behaviors.
yes
nvda alfa does not have this problem


Discussion:
I can reproduce this issue.

Cc @michaelDCurran 

Marking this as p2, but a p1 might be more sufficient here, since this regards essential functionality many people are using.
speech.speak always clears curWordChars (forgetting the current word being typed). However, speech.speakSpelling (used for speaking a typed character) internally calls speech.speak. I think before speech refactor speakSpelling bypassed speech.speak and called the synth's speak method directly.
We originally chose to clear curWordChars in speech.speak to lessen the chance that typing a non-alphaNumeric character after other actions would not cause a word from a long time ago to suddenly speak.
  The most important thing here is to no longer clear curWordChars in speech.speak. But, we'll need to ensure that curWordChars *is* cleared on all non-typing related actions. E.g. focus changes, and caret navigation. 
Of course there is pr #8110 that replaces all of this code anyway. But right now at least, we should probably fix this current implementation.
I believe #8110 still relies on curWordChars not being cleared, though I"m not exactly sure.

I'd appreciate it if you could keep #7812 in mind when looking in to this. That was actually the trigger to create #8110 in the first place.

Resolving patch:
diff --git a/source/NVDAObjects/__init__.py b/source/NVDAObjects/__init__.py
--- a/source/NVDAObjects/__init__.py
+++ b/source/NVDAObjects/__init__.py
@@ -1031,6 +1031,10 @@ def event_gainFocus(self):
 		braille.handler.handleGainFocus(self)
 		brailleInput.handler.handleGainFocus(self)
 
+	def event_loseFocus(self):
+		# Forget the word currently being typed as focus is moving to a new control. 
+		speech.clearTypedWordBuffer()
+
 	def event_foreground(self):
 		"""Called when the foreground window changes.
 		This method should only perform tasks specific to the foreground window changing.
diff --git a/source/editableText.py b/source/editableText.py
--- a/source/editableText.py
+++ b/source/editableText.py
@@ -131,6 +131,8 @@ def _caretScriptPostMovedHelper(self, speakUnit, gesture, info=None):
 		braille.handler.handleCaretMove(self)
 
 	def _caretMovementScriptHelper(self, gesture, unit):
+		# Forget the word currently being typed as the user is moving the caret somewhere else.
+		speech.clearTypedWordBuffer()
 		try:
 			info=self.makeTextInfo(textInfos.POSITION_CARET)
 		except:
diff --git a/source/speech.py b/source/speech.py
--- a/source/speech.py
+++ b/source/speech.py
@@ -513,8 +513,7 @@ def speak(speechSequence, symbolLevel=None, priority=None):
 		for item in speechSequence:
 			if isinstance(item, basestring):
 				speechViewer.appendText(item)
-	global beenCanceled, curWordChars
-	curWordChars=[]
+	global beenCanceled
 	if speechMode==speechMode_off:
 		return
 	elif speechMode==speechMode_beeps:
@@ -2527,3 +2526,12 @@ def cancel(self):
 #: The singleton _SpeechManager instance used for speech functions.
 #: @type: L{_SpeechManager}
 _manager = _SpeechManager()
+
+def clearTypedWordBuffer():
+	"""
+	Forgets any word currently being built up with typed characters for speaking. 
+	This should be called when the user's context changes such that they could no longer 
+	complete the word (such as a focus change or choosing to move the caret).
+	"""
+	global curWordChars
+	curWordChars=[]


Test patch: