We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
nvda logs a lot of '\r\n\r\n' if UIA support for Windows console is enabled

### Steps to reproduce:
1. In NVDA's Advanced Settings panel, make the following settings:
  - Windows Console support: combo box: UIA when available
2. Open cmd or git bash and press NVDA+Shift+M(Laptop);
### Actual behavior:
There are a lot of '\r\n' in the log.

### Expected behavior:
There won't be so many '\r\n' in the log
### NVDA logs, crash dumps and other attachments:
[log.txt](https://github.com/nvaccess/nvda/files/10866869/log.txt)

### System configuration
#### NVDA installed/portable/running from source:
Installed
#### NVDA version:
2023.1Beta2
#### Windows version:
Windows 10 22H2 (AMD64) build 19045.2604
#### Name and version of other software in use when reproducing the issue:
None
#### Other information about your system:
None
### Other questions
#### Does the issue still occur after restarting your computer?
Yes
#### Have you tried any other versions of NVDA? If so, please report their behaviors.
2022.4 is the same
#### If NVDA add-ons are disabled, is your problem still occurring?
Yes
#### Does the issue still occur after you run the COM Registration Fixing Tool in NVDA's tools menu?
Yes

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `source/speechDictHandler/__init__.py`
Content:
```
1 # A part of NonVisual Desktop Access (NVDA)
2 # Copyright (C) 2006-2023 NVDA Contributors <http://www.nvda-project.org/>
3 # This file is covered by the GNU General Public License.
4 # See the file COPYING for more details.
5 
6 import re
7 import globalVars
8 from logHandler import log
9 import os
10 import codecs
11 import api
12 import config
13 from . import dictFormatUpgrade
14 from .speechDictVars import speechDictsPath
15 
16 dictionaries = {}
17 dictTypes = ("temp", "voice", "default", "builtin") # ordered by their priority E.G. voice specific speech dictionary is processed before the default
18 
19 # Types of speech dictionary entries:
20 ENTRY_TYPE_ANYWHERE = 0 # String can match anywhere
21 ENTRY_TYPE_WORD = 2 # String must have word boundaries on both sides to match
22 ENTRY_TYPE_REGEXP = 1 # Regular expression
23 
24 class SpeechDictEntry:
25 
26 	def __init__(self, pattern, replacement,comment,caseSensitive=True,type=ENTRY_TYPE_ANYWHERE):
27 		self.pattern = pattern
28 		flags = re.U
29 		if not caseSensitive: flags|=re.IGNORECASE
30 		if type == ENTRY_TYPE_REGEXP:
31 			tempPattern = pattern
32 		elif type == ENTRY_TYPE_WORD:
33 			tempPattern = r"\b" + re.escape(pattern) + r"\b"
34 		else:
35 			tempPattern= re.escape(pattern)
36 			type = ENTRY_TYPE_ANYWHERE # Insure sane values.
37 		self.compiled = re.compile(tempPattern,flags)
38 		self.replacement = replacement
39 		self.comment=comment
40 		self.caseSensitive=caseSensitive
41 		self.type=type
42 
43 	def sub(self, text: str) -> str:
44 		if self.type == ENTRY_TYPE_REGEXP:
45 			replacement = self.replacement
46 		else:
47 			# Escape the backslashes for non-regexp replacements
48 			replacement = self.replacement.replace('\\', '\\\\')
49 		return self.compiled.sub(replacement, text)
50 
51 class SpeechDict(list):
52 
53 	fileName = None
54 
55 	def load(self, fileName):
56 		self.fileName=fileName
57 		comment=""
58 		del self[:]
59 		log.debug("Loading speech dictionary '%s'..." % fileName)
60 		if not os.path.isfile(fileName): 
61 			log.debug("file '%s' not found." % fileName)
62 			return
63 		file = codecs.open(fileName,"r","utf_8_sig",errors="replace")
64 		for line in file:
65 			if line.isspace():
66 				comment=""
67 				continue
68 			line=line.rstrip('\r\n')
69 			if line.startswith('#'):
70 				if comment:
71 					comment+=" "
72 				comment+=line[1:]
73 			else:
74 				temp=line.split("\t")
75 				if len(temp) ==4:
76 					pattern = temp[0].replace(r'\#','#')
77 					replace = temp[1].replace(r'\#','#')
78 					try:
79 						dictionaryEntry=SpeechDictEntry(pattern, replace, comment, caseSensitive=bool(int(temp[2])), type=int(temp[3]))
80 						self.append(dictionaryEntry)
81 					except Exception as e:
82 						log.exception("Dictionary (\"%s\") entry invalid for \"%s\" error raised: \"%s\"" % (fileName, line, e))
83 					comment=""
84 				else:
85 					log.warning("can't parse line '%s'" % line)
86 		log.debug("%d loaded records." % len(self))
87 		file.close()
88 		return
89 
90 	def save(self,fileName=None):
91 		if not fileName:
92 			fileName=getattr(self,'fileName',None)
93 		if not fileName:
94 			return
95 		dirName=os.path.dirname(fileName)
96 		if not os.path.isdir(dirName):
97 			os.makedirs(dirName)
98 		file = codecs.open(fileName,"w","utf_8_sig",errors="replace")
99 		for entry in self:
100 			if entry.comment:
101 				file.write("#%s\r\n"%entry.comment)
102 			file.write("%s\t%s\t%s\t%s\r\n"%(entry.pattern.replace('#',r'\#'),entry.replacement.replace('#',r'\#'),int(entry.caseSensitive),entry.type))
103 		file.close()
104 
105 	def sub(self, text):
106 		invalidEntries = []
107 		for index, entry in enumerate(self):
108 			try:
109 				text = entry.sub(text)
110 			except re.error as exc:
111 				dictName = self.fileName or "temporary dictionary"
112 				log.error(f"Invalid dictionary entry {index+1} in {dictName}: \"{entry.pattern}\", {exc}")
113 				invalidEntries.append(index)
114 			for index in reversed(invalidEntries):
115 				del self[index]
116 		return text
117 
118 def processText(text):
119 	if not globalVars.speechDictionaryProcessing:
120 		return text
121 	for type in dictTypes:
122 		text=dictionaries[type].sub(text)
123 	return text
124 
125 def initialize():
126 	for type in dictTypes:
127 		dictionaries[type]=SpeechDict()
128 	dictionaries["default"].load(os.path.join(speechDictsPath, "default.dic"))
129 	dictionaries["builtin"].load(os.path.join(globalVars.appDir, "builtin.dic"))
130 
131 def loadVoiceDict(synth):
132 	"""Loads appropriate dictionary for the given synthesizer.
133 It handles case when the synthesizer doesn't support voice setting.
134 """
135 	try:
136 		dictFormatUpgrade.doAnyUpgrades(synth)
137 	except:
138 		log.error("error trying to upgrade dictionaries", exc_info=True)
139 		pass
140 	if synth.isSupported("voice"):
141 		voice = synth.availableVoices[synth.voice].displayName
142 		baseName = dictFormatUpgrade.createVoiceDictFileName(synth.name, voice)
143 	else:
144 		baseName=r"{synth}.dic".format(synth=synth.name)
145 	voiceDictsPath = dictFormatUpgrade.voiceDictsPath
146 	fileName= os.path.join(voiceDictsPath, synth.name, baseName)
147 	dictionaries["voice"].load(fileName)
148
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/source/speechDictHandler/__init__.py b/source/speechDictHandler/__init__.py
--- a/source/speechDictHandler/__init__.py
+++ b/source/speechDictHandler/__init__.py
@@ -8,8 +8,8 @@
 from logHandler import log
 import os
 import codecs
+import review
 import api
-import config
 from . import dictFormatUpgrade
 from .speechDictVars import speechDictsPath
 
@@ -115,9 +115,26 @@
 				del self[index]
 		return text
 
+
 def processText(text):
 	if not globalVars.speechDictionaryProcessing:
 		return text
+	# #14689: older (IMPROVED and END_INCLUSIVE) UIA consoles have many blank lines,
+	# which slows processing to a halt
+	focus = api.getFocusObject()
+	try:
+		# get TextInfo implementation for object review mode
+		textInfo, obj = review.getObjectPosition(focus)
+	except AttributeError:  # no makeTextInfo
+		textInfo = None
+	# late import to prevent circular dependency
+	# ConsoleUIATextInfo is used by IMPROVED and END_INCLUSIVE consoles
+	from NVDAObjects.UIA.winConsoleUIA import ConsoleUIATextInfo
+	if isinstance(textInfo, ConsoleUIATextInfo):
+		stripText = text.rstrip()
+		IGNORE_TRAILING_WHITESPACE_LENGTH = 100
+		if len(text) - len(stripText) > IGNORE_TRAILING_WHITESPACE_LENGTH:
+			text = stripText
 	for type in dictTypes:
 		text=dictionaries[type].sub(text)
 	return text

{"golden_diff": "diff --git a/source/speechDictHandler/__init__.py b/source/speechDictHandler/__init__.py\n--- a/source/speechDictHandler/__init__.py\n+++ b/source/speechDictHandler/__init__.py\n@@ -8,8 +8,8 @@\n from logHandler import log\r\n import os\r\n import codecs\r\n+import review\r\n import api\r\n-import config\r\n from . import dictFormatUpgrade\r\n from .speechDictVars import speechDictsPath\r\n \r\n@@ -115,9 +115,26 @@\n \t\t\t\tdel self[index]\r\n \t\treturn text\r\n \r\n+\r\n def processText(text):\r\n \tif not globalVars.speechDictionaryProcessing:\r\n \t\treturn text\r\n+\t# #14689: older (IMPROVED and END_INCLUSIVE) UIA consoles have many blank lines,\r\n+\t# which slows processing to a halt\r\n+\tfocus = api.getFocusObject()\r\n+\ttry:\r\n+\t\t# get TextInfo implementation for object review mode\r\n+\t\ttextInfo, obj = review.getObjectPosition(focus)\r\n+\texcept AttributeError:  # no makeTextInfo\r\n+\t\ttextInfo = None\r\n+\t# late import to prevent circular dependency\r\n+\t# ConsoleUIATextInfo is used by IMPROVED and END_INCLUSIVE consoles\r\n+\tfrom NVDAObjects.UIA.winConsoleUIA import ConsoleUIATextInfo\r\n+\tif isinstance(textInfo, ConsoleUIATextInfo):\r\n+\t\tstripText = text.rstrip()\r\n+\t\tIGNORE_TRAILING_WHITESPACE_LENGTH = 100\r\n+\t\tif len(text) - len(stripText) > IGNORE_TRAILING_WHITESPACE_LENGTH:\r\n+\t\t\ttext = stripText\r\n \tfor type in dictTypes:\r\n \t\ttext=dictionaries[type].sub(text)\r\n \treturn text\n", "issue": "nvda logs a lot of '\\r\\n\\r\\n' if UIA support for Windows console is enabled\n\r\n### Steps to reproduce:\r\n1. In NVDA's Advanced Settings panel, make the following settings:\r\n  - Windows Console support: combo box: UIA when available\r\n2. Open cmd or git bash and press NVDA+Shift+M(Laptop);\r\n### Actual behavior:\r\nThere are a lot of '\\r\\n' in the log.\r\n\r\n### Expected behavior:\r\nThere won't be so many '\\r\\n' in the log\r\n### NVDA logs, crash dumps and other attachments:\r\n[log.txt](https://github.com/nvaccess/nvda/files/10866869/log.txt)\r\n\r\n### System configuration\r\n#### NVDA installed/portable/running from source:\r\nInstalled\r\n#### NVDA version:\r\n2023.1Beta2\r\n#### Windows version:\r\nWindows 10 22H2 (AMD64) build 19045.2604\r\n#### Name and version of other software in use when reproducing the issue:\r\nNone\r\n#### Other information about your system:\r\nNone\r\n### Other questions\r\n#### Does the issue still occur after restarting your computer?\r\nYes\r\n#### Have you tried any other versions of NVDA? If so, please report their behaviors.\r\n2022.4 is the same\r\n#### If NVDA add-ons are disabled, is your problem still occurring?\r\nYes\r\n#### Does the issue still occur after you run the COM Registration Fixing Tool in NVDA's tools menu?\r\nYes\n", "before_files": [{"content": "# A part of NonVisual Desktop Access (NVDA)\r\n# Copyright (C) 2006-2023 NVDA Contributors <http://www.nvda-project.org/>\r\n# This file is covered by the GNU General Public License.\r\n# See the file COPYING for more details.\r\n\r\nimport re\r\nimport globalVars\r\nfrom logHandler import log\r\nimport os\r\nimport codecs\r\nimport api\r\nimport config\r\nfrom . import dictFormatUpgrade\r\nfrom .speechDictVars import speechDictsPath\r\n\r\ndictionaries = {}\r\ndictTypes = (\"temp\", \"voice\", \"default\", \"builtin\") # ordered by their priority E.G. voice specific speech dictionary is processed before the default\r\n\r\n# Types of speech dictionary entries:\r\nENTRY_TYPE_ANYWHERE = 0 # String can match anywhere\r\nENTRY_TYPE_WORD = 2 # String must have word boundaries on both sides to match\r\nENTRY_TYPE_REGEXP = 1 # Regular expression\r\n\r\nclass SpeechDictEntry:\r\n\r\n\tdef __init__(self, pattern, replacement,comment,caseSensitive=True,type=ENTRY_TYPE_ANYWHERE):\r\n\t\tself.pattern = pattern\r\n\t\tflags = re.U\r\n\t\tif not caseSensitive: flags|=re.IGNORECASE\r\n\t\tif type == ENTRY_TYPE_REGEXP:\r\n\t\t\ttempPattern = pattern\r\n\t\telif type == ENTRY_TYPE_WORD:\r\n\t\t\ttempPattern = r\"\\b\" + re.escape(pattern) + r\"\\b\"\r\n\t\telse:\r\n\t\t\ttempPattern= re.escape(pattern)\r\n\t\t\ttype = ENTRY_TYPE_ANYWHERE # Insure sane values.\r\n\t\tself.compiled = re.compile(tempPattern,flags)\r\n\t\tself.replacement = replacement\r\n\t\tself.comment=comment\r\n\t\tself.caseSensitive=caseSensitive\r\n\t\tself.type=type\r\n\r\n\tdef sub(self, text: str) -> str:\r\n\t\tif self.type == ENTRY_TYPE_REGEXP:\r\n\t\t\treplacement = self.replacement\r\n\t\telse:\r\n\t\t\t# Escape the backslashes for non-regexp replacements\r\n\t\t\treplacement = self.replacement.replace('\\\\', '\\\\\\\\')\r\n\t\treturn self.compiled.sub(replacement, text)\r\n\r\nclass SpeechDict(list):\r\n\r\n\tfileName = None\r\n\r\n\tdef load(self, fileName):\r\n\t\tself.fileName=fileName\r\n\t\tcomment=\"\"\r\n\t\tdel self[:]\r\n\t\tlog.debug(\"Loading speech dictionary '%s'...\" % fileName)\r\n\t\tif not os.path.isfile(fileName): \r\n\t\t\tlog.debug(\"file '%s' not found.\" % fileName)\r\n\t\t\treturn\r\n\t\tfile = codecs.open(fileName,\"r\",\"utf_8_sig\",errors=\"replace\")\r\n\t\tfor line in file:\r\n\t\t\tif line.isspace():\r\n\t\t\t\tcomment=\"\"\r\n\t\t\t\tcontinue\r\n\t\t\tline=line.rstrip('\\r\\n')\r\n\t\t\tif line.startswith('#'):\r\n\t\t\t\tif comment:\r\n\t\t\t\t\tcomment+=\" \"\r\n\t\t\t\tcomment+=line[1:]\r\n\t\t\telse:\r\n\t\t\t\ttemp=line.split(\"\\t\")\r\n\t\t\t\tif len(temp) ==4:\r\n\t\t\t\t\tpattern = temp[0].replace(r'\\#','#')\r\n\t\t\t\t\treplace = temp[1].replace(r'\\#','#')\r\n\t\t\t\t\ttry:\r\n\t\t\t\t\t\tdictionaryEntry=SpeechDictEntry(pattern, replace, comment, caseSensitive=bool(int(temp[2])), type=int(temp[3]))\r\n\t\t\t\t\t\tself.append(dictionaryEntry)\r\n\t\t\t\t\texcept Exception as e:\r\n\t\t\t\t\t\tlog.exception(\"Dictionary (\\\"%s\\\") entry invalid for \\\"%s\\\" error raised: \\\"%s\\\"\" % (fileName, line, e))\r\n\t\t\t\t\tcomment=\"\"\r\n\t\t\t\telse:\r\n\t\t\t\t\tlog.warning(\"can't parse line '%s'\" % line)\r\n\t\tlog.debug(\"%d loaded records.\" % len(self))\r\n\t\tfile.close()\r\n\t\treturn\r\n\r\n\tdef save(self,fileName=None):\r\n\t\tif not fileName:\r\n\t\t\tfileName=getattr(self,'fileName',None)\r\n\t\tif not fileName:\r\n\t\t\treturn\r\n\t\tdirName=os.path.dirname(fileName)\r\n\t\tif not os.path.isdir(dirName):\r\n\t\t\tos.makedirs(dirName)\r\n\t\tfile = codecs.open(fileName,\"w\",\"utf_8_sig\",errors=\"replace\")\r\n\t\tfor entry in self:\r\n\t\t\tif entry.comment:\r\n\t\t\t\tfile.write(\"#%s\\r\\n\"%entry.comment)\r\n\t\t\tfile.write(\"%s\\t%s\\t%s\\t%s\\r\\n\"%(entry.pattern.replace('#',r'\\#'),entry.replacement.replace('#',r'\\#'),int(entry.caseSensitive),entry.type))\r\n\t\tfile.close()\r\n\r\n\tdef sub(self, text):\r\n\t\tinvalidEntries = []\r\n\t\tfor index, entry in enumerate(self):\r\n\t\t\ttry:\r\n\t\t\t\ttext = entry.sub(text)\r\n\t\t\texcept re.error as exc:\r\n\t\t\t\tdictName = self.fileName or \"temporary dictionary\"\r\n\t\t\t\tlog.error(f\"Invalid dictionary entry {index+1} in {dictName}: \\\"{entry.pattern}\\\", {exc}\")\r\n\t\t\t\tinvalidEntries.append(index)\r\n\t\t\tfor index in reversed(invalidEntries):\r\n\t\t\t\tdel self[index]\r\n\t\treturn text\r\n\r\ndef processText(text):\r\n\tif not globalVars.speechDictionaryProcessing:\r\n\t\treturn text\r\n\tfor type in dictTypes:\r\n\t\ttext=dictionaries[type].sub(text)\r\n\treturn text\r\n\r\ndef initialize():\r\n\tfor type in dictTypes:\r\n\t\tdictionaries[type]=SpeechDict()\r\n\tdictionaries[\"default\"].load(os.path.join(speechDictsPath, \"default.dic\"))\r\n\tdictionaries[\"builtin\"].load(os.path.join(globalVars.appDir, \"builtin.dic\"))\r\n\r\ndef loadVoiceDict(synth):\r\n\t\"\"\"Loads appropriate dictionary for the given synthesizer.\r\nIt handles case when the synthesizer doesn't support voice setting.\r\n\"\"\"\r\n\ttry:\r\n\t\tdictFormatUpgrade.doAnyUpgrades(synth)\r\n\texcept:\r\n\t\tlog.error(\"error trying to upgrade dictionaries\", exc_info=True)\r\n\t\tpass\r\n\tif synth.isSupported(\"voice\"):\r\n\t\tvoice = synth.availableVoices[synth.voice].displayName\r\n\t\tbaseName = dictFormatUpgrade.createVoiceDictFileName(synth.name, voice)\r\n\telse:\r\n\t\tbaseName=r\"{synth}.dic\".format(synth=synth.name)\r\n\tvoiceDictsPath = dictFormatUpgrade.voiceDictsPath\r\n\tfileName= os.path.join(voiceDictsPath, synth.name, baseName)\r\n\tdictionaries[\"voice\"].load(fileName)\r\n", "path": "source/speechDictHandler/__init__.py"}], "after_files": [{"content": "# A part of NonVisual Desktop Access (NVDA)\r\n# Copyright (C) 2006-2023 NVDA Contributors <http://www.nvda-project.org/>\r\n# This file is covered by the GNU General Public License.\r\n# See the file COPYING for more details.\r\n\r\nimport re\r\nimport globalVars\r\nfrom logHandler import log\r\nimport os\r\nimport codecs\r\nimport review\r\nimport api\r\nfrom . import dictFormatUpgrade\r\nfrom .speechDictVars import speechDictsPath\r\n\r\ndictionaries = {}\r\ndictTypes = (\"temp\", \"voice\", \"default\", \"builtin\") # ordered by their priority E.G. voice specific speech dictionary is processed before the default\r\n\r\n# Types of speech dictionary entries:\r\nENTRY_TYPE_ANYWHERE = 0 # String can match anywhere\r\nENTRY_TYPE_WORD = 2 # String must have word boundaries on both sides to match\r\nENTRY_TYPE_REGEXP = 1 # Regular expression\r\n\r\nclass SpeechDictEntry:\r\n\r\n\tdef __init__(self, pattern, replacement,comment,caseSensitive=True,type=ENTRY_TYPE_ANYWHERE):\r\n\t\tself.pattern = pattern\r\n\t\tflags = re.U\r\n\t\tif not caseSensitive: flags|=re.IGNORECASE\r\n\t\tif type == ENTRY_TYPE_REGEXP:\r\n\t\t\ttempPattern = pattern\r\n\t\telif type == ENTRY_TYPE_WORD:\r\n\t\t\ttempPattern = r\"\\b\" + re.escape(pattern) + r\"\\b\"\r\n\t\telse:\r\n\t\t\ttempPattern= re.escape(pattern)\r\n\t\t\ttype = ENTRY_TYPE_ANYWHERE # Insure sane values.\r\n\t\tself.compiled = re.compile(tempPattern,flags)\r\n\t\tself.replacement = replacement\r\n\t\tself.comment=comment\r\n\t\tself.caseSensitive=caseSensitive\r\n\t\tself.type=type\r\n\r\n\tdef sub(self, text: str) -> str:\r\n\t\tif self.type == ENTRY_TYPE_REGEXP:\r\n\t\t\treplacement = self.replacement\r\n\t\telse:\r\n\t\t\t# Escape the backslashes for non-regexp replacements\r\n\t\t\treplacement = self.replacement.replace('\\\\', '\\\\\\\\')\r\n\t\treturn self.compiled.sub(replacement, text)\r\n\r\nclass SpeechDict(list):\r\n\r\n\tfileName = None\r\n\r\n\tdef load(self, fileName):\r\n\t\tself.fileName=fileName\r\n\t\tcomment=\"\"\r\n\t\tdel self[:]\r\n\t\tlog.debug(\"Loading speech dictionary '%s'...\" % fileName)\r\n\t\tif not os.path.isfile(fileName): \r\n\t\t\tlog.debug(\"file '%s' not found.\" % fileName)\r\n\t\t\treturn\r\n\t\tfile = codecs.open(fileName,\"r\",\"utf_8_sig\",errors=\"replace\")\r\n\t\tfor line in file:\r\n\t\t\tif line.isspace():\r\n\t\t\t\tcomment=\"\"\r\n\t\t\t\tcontinue\r\n\t\t\tline=line.rstrip('\\r\\n')\r\n\t\t\tif line.startswith('#'):\r\n\t\t\t\tif comment:\r\n\t\t\t\t\tcomment+=\" \"\r\n\t\t\t\tcomment+=line[1:]\r\n\t\t\telse:\r\n\t\t\t\ttemp=line.split(\"\\t\")\r\n\t\t\t\tif len(temp) ==4:\r\n\t\t\t\t\tpattern = temp[0].replace(r'\\#','#')\r\n\t\t\t\t\treplace = temp[1].replace(r'\\#','#')\r\n\t\t\t\t\ttry:\r\n\t\t\t\t\t\tdictionaryEntry=SpeechDictEntry(pattern, replace, comment, caseSensitive=bool(int(temp[2])), type=int(temp[3]))\r\n\t\t\t\t\t\tself.append(dictionaryEntry)\r\n\t\t\t\t\texcept Exception as e:\r\n\t\t\t\t\t\tlog.exception(\"Dictionary (\\\"%s\\\") entry invalid for \\\"%s\\\" error raised: \\\"%s\\\"\" % (fileName, line, e))\r\n\t\t\t\t\tcomment=\"\"\r\n\t\t\t\telse:\r\n\t\t\t\t\tlog.warning(\"can't parse line '%s'\" % line)\r\n\t\tlog.debug(\"%d loaded records.\" % len(self))\r\n\t\tfile.close()\r\n\t\treturn\r\n\r\n\tdef save(self,fileName=None):\r\n\t\tif not fileName:\r\n\t\t\tfileName=getattr(self,'fileName',None)\r\n\t\tif not fileName:\r\n\t\t\treturn\r\n\t\tdirName=os.path.dirname(fileName)\r\n\t\tif not os.path.isdir(dirName):\r\n\t\t\tos.makedirs(dirName)\r\n\t\tfile = codecs.open(fileName,\"w\",\"utf_8_sig\",errors=\"replace\")\r\n\t\tfor entry in self:\r\n\t\t\tif entry.comment:\r\n\t\t\t\tfile.write(\"#%s\\r\\n\"%entry.comment)\r\n\t\t\tfile.write(\"%s\\t%s\\t%s\\t%s\\r\\n\"%(entry.pattern.replace('#',r'\\#'),entry.replacement.replace('#',r'\\#'),int(entry.caseSensitive),entry.type))\r\n\t\tfile.close()\r\n\r\n\tdef sub(self, text):\r\n\t\tinvalidEntries = []\r\n\t\tfor index, entry in enumerate(self):\r\n\t\t\ttry:\r\n\t\t\t\ttext = entry.sub(text)\r\n\t\t\texcept re.error as exc:\r\n\t\t\t\tdictName = self.fileName or \"temporary dictionary\"\r\n\t\t\t\tlog.error(f\"Invalid dictionary entry {index+1} in {dictName}: \\\"{entry.pattern}\\\", {exc}\")\r\n\t\t\t\tinvalidEntries.append(index)\r\n\t\t\tfor index in reversed(invalidEntries):\r\n\t\t\t\tdel self[index]\r\n\t\treturn text\r\n\r\n\r\ndef processText(text):\r\n\tif not globalVars.speechDictionaryProcessing:\r\n\t\treturn text\r\n\t# #14689: older (IMPROVED and END_INCLUSIVE) UIA consoles have many blank lines,\r\n\t# which slows processing to a halt\r\n\tfocus = api.getFocusObject()\r\n\ttry:\r\n\t\t# get TextInfo implementation for object review mode\r\n\t\ttextInfo, obj = review.getObjectPosition(focus)\r\n\texcept AttributeError:  # no makeTextInfo\r\n\t\ttextInfo = None\r\n\t# late import to prevent circular dependency\r\n\t# ConsoleUIATextInfo is used by IMPROVED and END_INCLUSIVE consoles\r\n\tfrom NVDAObjects.UIA.winConsoleUIA import ConsoleUIATextInfo\r\n\tif isinstance(textInfo, ConsoleUIATextInfo):\r\n\t\tstripText = text.rstrip()\r\n\t\tIGNORE_TRAILING_WHITESPACE_LENGTH = 100\r\n\t\tif len(text) - len(stripText) > IGNORE_TRAILING_WHITESPACE_LENGTH:\r\n\t\t\ttext = stripText\r\n\tfor type in dictTypes:\r\n\t\ttext=dictionaries[type].sub(text)\r\n\treturn text\r\n\r\ndef initialize():\r\n\tfor type in dictTypes:\r\n\t\tdictionaries[type]=SpeechDict()\r\n\tdictionaries[\"default\"].load(os.path.join(speechDictsPath, \"default.dic\"))\r\n\tdictionaries[\"builtin\"].load(os.path.join(globalVars.appDir, \"builtin.dic\"))\r\n\r\ndef loadVoiceDict(synth):\r\n\t\"\"\"Loads appropriate dictionary for the given synthesizer.\r\nIt handles case when the synthesizer doesn't support voice setting.\r\n\"\"\"\r\n\ttry:\r\n\t\tdictFormatUpgrade.doAnyUpgrades(synth)\r\n\texcept:\r\n\t\tlog.error(\"error trying to upgrade dictionaries\", exc_info=True)\r\n\t\tpass\r\n\tif synth.isSupported(\"voice\"):\r\n\t\tvoice = synth.availableVoices[synth.voice].displayName\r\n\t\tbaseName = dictFormatUpgrade.createVoiceDictFileName(synth.name, voice)\r\n\telse:\r\n\t\tbaseName=r\"{synth}.dic\".format(synth=synth.name)\r\n\tvoiceDictsPath = dictFormatUpgrade.voiceDictsPath\r\n\tfileName= os.path.join(voiceDictsPath, synth.name, baseName)\r\n\tdictionaries[\"voice\"].load(fileName)\r\n", "path": "source/speechDictHandler/__init__.py"}]}