Skip to content

Commit fb60d68

Browse files
authored
Merge 9b28fae into 89c84b8
2 parents 89c84b8 + 9b28fae commit fb60d68

4 files changed

Lines changed: 54 additions & 54 deletions

File tree

source/NVDAObjects/UIA/winConsoleUIA.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -366,13 +366,6 @@ def _get_TextInfo(self):
366366
movement."""
367367
return consoleUIATextInfo if self.is21H1Plus else consoleUIATextInfoPre21H1
368368

369-
def _getTextLines(self):
370-
# This override of _getTextLines takes advantage of the fact that
371-
# the console text contains linefeeds for every line
372-
# Thus a simple string splitlines is much faster than splitting by unit line.
373-
ti = self.makeTextInfo(textInfos.POSITION_ALL)
374-
text = ti.text or ""
375-
return text.splitlines()
376369

377370
def findExtraOverlayClasses(obj, clsList):
378371
if obj.UIAElement.cachedAutomationId == "Text Area":

source/NVDAObjects/behaviors.py

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import os
1313
import time
1414
import threading
15-
import difflib
15+
from diff_match_patch import diff_match_patch
1616
import tones
1717
import queueHandler
1818
import eventHandler
@@ -228,6 +228,7 @@ def initOverlayClass(self):
228228
self._event = threading.Event()
229229
self._monitorThread = None
230230
self._keepMonitoring = False
231+
self._dmp = diff_match_patch()
231232

232233
def startMonitoring(self):
233234
"""Start monitoring for new text.
@@ -263,15 +264,18 @@ def event_textChange(self):
263264
"""
264265
self._event.set()
265266

266-
def _getTextLines(self):
267-
"""Retrieve the text of this object in lines.
267+
def _getText(self):
268+
"""Retrieve the text of this object.
268269
This will be used to determine the new text to speak.
269270
The base implementation uses the L{TextInfo}.
270271
However, subclasses should override this if there is a better way to retrieve the text.
271-
@return: The current lines of text.
272-
@rtype: list of str
272+
@return: The current text ob the object.
273+
@rtype: str
273274
"""
274-
return list(self.makeTextInfo(textInfos.POSITION_ALL).getTextInChunks(textInfos.UNIT_LINE))
275+
if hasattr(self, "_getTextLines"):
276+
log.warning("LiveText._getTextLines is deprecated, please override _getText instead.")
277+
return '\n'.join(self._getTextLines())
278+
return self.makeTextInfo(textInfos.POSITION_ALL).text
275279

276280
def _reportNewLines(self, lines):
277281
"""
@@ -289,10 +293,10 @@ def _reportNewText(self, line):
289293

290294
def _monitor(self):
291295
try:
292-
oldLines = self._getTextLines()
296+
oldText = self._getText()
293297
except:
294-
log.exception("Error getting initial lines")
295-
oldLines = []
298+
log.exception("Error getting initial text")
299+
oldText = ""
296300

297301
while self._keepMonitoring:
298302
self._event.wait()
@@ -307,59 +311,61 @@ def _monitor(self):
307311
self._event.clear()
308312

309313
try:
310-
newLines = self._getTextLines()
314+
newText = self._getText()
311315
if config.conf["presentation"]["reportDynamicContentChanges"]:
312-
outLines = self._calculateNewText(newLines, oldLines)
316+
outLines = self._calculateNewText(newText, oldText)
313317
if len(outLines) == 1 and len(outLines[0].strip()) == 1:
314318
# This is only a single character,
315319
# which probably means it is just a typed character,
316320
# so ignore it.
317321
del outLines[0]
318322
if outLines:
319323
queueHandler.queueFunction(queueHandler.eventQueue, self._reportNewLines, outLines)
320-
oldLines = newLines
324+
oldText = newText
321325
except:
322-
log.exception("Error getting lines or calculating new text")
326+
log.exception("Error getting or calculating new text")
323327

324-
def _calculateNewText(self, newLines, oldLines):
325-
outLines = []
328+
def _calculateNewText(self, newText, oldText):
329+
(oldText, newText, linearray) = self._dmp.diff_linesToChars(oldText, newText)
330+
diffs = self._dmp.diff_main(oldText, newText, checklines=False)
331+
# Convert the diff back to original text.
332+
self._dmp.diff_charsToLines(diffs, linearray)
333+
# Eliminate freak matches (e.g. blank lines)
334+
self._dmp.diff_cleanupSemantic(diffs)
326335

336+
outLines = []
327337
prevLine = None
328-
for line in difflib.ndiff(oldLines, newLines):
329-
if line[0] == "?":
330-
# We're never interested in these.
331-
continue
332-
if line[0] != "+":
338+
prevChange = None
339+
for change, text in diffs:
340+
if change != self._dmp.DIFF_INSERT:
333341
# We're only interested in new lines.
334-
prevLine = line
342+
prevChange, prevLine = change, text
335343
continue
336-
text = line[2:]
337344
if not text or text.isspace():
338-
prevLine = line
345+
prevChange, prevLine = change, text
339346
continue
340347

341-
if prevLine and prevLine[0] == "-" and len(prevLine) > 2:
348+
if prevLine and prevChange == self._dmp.DIFF_DELETE and not prevLine.isspace():
342349
# It's possible that only a few characters have changed in this line.
343350
# If so, we want to speak just the changed section, rather than the entire line.
344-
prevText = prevLine[2:]
345351
textLen = len(text)
346-
prevTextLen = len(prevText)
352+
prevLineLen = len(prevLine)
347353
# Find the first character that differs between the two lines.
348-
for pos in range(min(textLen, prevTextLen)):
349-
if text[pos] != prevText[pos]:
354+
for pos in range(min(textLen, prevLineLen)):
355+
if text[pos] != prevLine[pos]:
350356
start = pos
351357
break
352358
else:
353359
# We haven't found a differing character so far and we've hit the end of one of the lines.
354360
# This means that the differing text starts here.
355361
start = pos + 1
356362
# Find the end of the differing text.
357-
if textLen != prevTextLen:
363+
if textLen != prevLineLen:
358364
# The lines are different lengths, so assume the rest of the line changed.
359365
end = textLen
360366
else:
361367
for pos in range(textLen - 1, start - 1, -1):
362-
if text[pos] != prevText[pos]:
368+
if text[pos] != prevLine[pos]:
363369
end = pos + 1
364370
break
365371

@@ -369,9 +375,10 @@ def _calculateNewText(self, newLines, oldLines):
369375

370376
if text and not text.isspace():
371377
outLines.append(text)
372-
prevLine = line
378+
prevChange, prevLine = change, text
379+
380+
return [line for outLine in outLines for line in outLine.splitlines() if line and not line.isspace()]
373381

374-
return outLines
375382

376383
class Terminal(LiveText, EditableText):
377384
"""An object which both accepts text input and outputs text which should be reported automatically.

source/NVDAObjects/window/winConsole.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
#NVDAObjects/WinConsole.py
2-
#A part of NonVisual Desktop Access (NVDA)
3-
#This file is covered by the GNU General Public License.
4-
#See the file COPYING for more details.
5-
#Copyright (C) 2007-2019 NV Access Limited, Bill Dengler
1+
# NVDAObjects/WinConsole.py
2+
# A part of NonVisual Desktop Access (NVDA)
3+
# This file is covered by the GNU General Public License.
4+
# See the file COPYING for more details.
5+
# Copyright (C) 2007-2020 NV Access Limited, Bill Dengler
66

77
import winConsoleHandler
88
from . import Window
@@ -69,8 +69,8 @@ def event_loseFocus(self):
6969
def event_nameChange(self):
7070
pass
7171

72-
def _getTextLines(self):
73-
return winConsoleHandler.getConsoleVisibleLines()
72+
def _getText(self):
73+
return winConsoleHandler.getConsoleVisibleText()
7474

7575
def script_caret_backspaceCharacter(self, gesture):
7676
super(WinConsole, self).script_caret_backspaceCharacter(gesture)

source/winConsoleHandler.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
#winConsoleHandler.py
2-
#A part of NonVisual Desktop Access (NVDA)
3-
#This file is covered by the GNU General Public License.
4-
#See the file COPYING for more details.
5-
#Copyright (C) 2009-2018 NV Access Limited, Babbage B.V.
1+
# winConsoleHandler.py
2+
# A part of NonVisual Desktop Access (NVDA)
3+
# This file is covered by the GNU General Public License.
4+
# See the file COPYING for more details.
5+
# Copyright (C) 2009-2020 NV Access Limited, Babbage B.V., Bill Dengler
66

77
import gui
88
import winUser
@@ -123,14 +123,14 @@ def _checkDead():
123123
except:
124124
log.exception()
125125

126-
def getConsoleVisibleLines():
126+
127+
def getConsoleVisibleText():
127128
consoleScreenBufferInfo=wincon.GetConsoleScreenBufferInfo(consoleOutputHandle)
128129
topLine=consoleScreenBufferInfo.srWindow.Top
129130
lineCount=(consoleScreenBufferInfo.srWindow.Bottom-topLine)+1
130131
lineLength=consoleScreenBufferInfo.dwSize.x
131132
text=wincon.ReadConsoleOutputCharacter(consoleOutputHandle,lineCount*lineLength,0,topLine)
132-
newLines=[text[x:x+lineLength] for x in range(0,len(text),lineLength)]
133-
return newLines
133+
return text
134134

135135
@winUser.WINEVENTPROC
136136
def consoleWinEventHook(handle,eventID,window,objectID,childID,threadID,timestamp):

0 commit comments

Comments
 (0)