Skip to content

Commit 9b38034

Browse files
authored
Merge 64f8f32 into 89c84b8
2 parents 89c84b8 + 64f8f32 commit 9b38034

4 files changed

Lines changed: 52 additions & 56 deletions

File tree

source/NVDAObjects/UIA/winConsoleUIA.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# NVDAObjects/UIA/winConsoleUIA.py
21
# A part of NonVisual Desktop Access (NVDA)
32
# This file is covered by the GNU General Public License.
43
# See the file COPYING for more details.
@@ -366,13 +365,6 @@ def _get_TextInfo(self):
366365
movement."""
367366
return consoleUIATextInfo if self.is21H1Plus else consoleUIATextInfoPre21H1
368367

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()
376368

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

source/NVDAObjects/behaviors.py

Lines changed: 39 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
# -*- coding: UTF-8 -*-
2-
# NVDAObjects/behaviors.py
32
# A part of NonVisual Desktop Access (NVDA)
43
# This file is covered by the GNU General Public License.
54
# See the file COPYING for more details.
@@ -12,7 +11,7 @@
1211
import os
1312
import time
1413
import threading
15-
import difflib
14+
from diff_match_patch import diff_match_patch
1615
import tones
1716
import queueHandler
1817
import eventHandler
@@ -228,6 +227,7 @@ def initOverlayClass(self):
228227
self._event = threading.Event()
229228
self._monitorThread = None
230229
self._keepMonitoring = False
230+
self._dmp = diff_match_patch()
231231

232232
def startMonitoring(self):
233233
"""Start monitoring for new text.
@@ -263,15 +263,18 @@ def event_textChange(self):
263263
"""
264264
self._event.set()
265265

266-
def _getTextLines(self):
267-
"""Retrieve the text of this object in lines.
266+
def _getText(self):
267+
"""Retrieve the text of this object.
268268
This will be used to determine the new text to speak.
269269
The base implementation uses the L{TextInfo}.
270270
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
271+
@return: The current text ob the object.
272+
@rtype: str
273273
"""
274-
return list(self.makeTextInfo(textInfos.POSITION_ALL).getTextInChunks(textInfos.UNIT_LINE))
274+
if hasattr(self, "_getTextLines"):
275+
log.warning("LiveText._getTextLines is deprecated, please override _getText instead.")
276+
return '\n'.join(self._getTextLines())
277+
return self.makeTextInfo(textInfos.POSITION_ALL).text
275278

276279
def _reportNewLines(self, lines):
277280
"""
@@ -289,10 +292,10 @@ def _reportNewText(self, line):
289292

290293
def _monitor(self):
291294
try:
292-
oldLines = self._getTextLines()
295+
oldText = self._getText()
293296
except:
294-
log.exception("Error getting initial lines")
295-
oldLines = []
297+
log.exception("Error getting initial text")
298+
oldText = ""
296299

297300
while self._keepMonitoring:
298301
self._event.wait()
@@ -307,59 +310,61 @@ def _monitor(self):
307310
self._event.clear()
308311

309312
try:
310-
newLines = self._getTextLines()
313+
newText = self._getText()
311314
if config.conf["presentation"]["reportDynamicContentChanges"]:
312-
outLines = self._calculateNewText(newLines, oldLines)
315+
outLines = self._calculateNewText(newText, oldText)
313316
if len(outLines) == 1 and len(outLines[0].strip()) == 1:
314317
# This is only a single character,
315318
# which probably means it is just a typed character,
316319
# so ignore it.
317320
del outLines[0]
318321
if outLines:
319322
queueHandler.queueFunction(queueHandler.eventQueue, self._reportNewLines, outLines)
320-
oldLines = newLines
323+
oldText = newText
321324
except:
322-
log.exception("Error getting lines or calculating new text")
325+
log.exception("Error getting or calculating new text")
323326

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

335+
outLines = []
327336
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] != "+":
337+
prevChange = None
338+
for change, text in diffs:
339+
if change != self._dmp.DIFF_INSERT:
333340
# We're only interested in new lines.
334-
prevLine = line
341+
prevChange, prevLine = change, text
335342
continue
336-
text = line[2:]
337343
if not text or text.isspace():
338-
prevLine = line
344+
prevChange, prevLine = change, text
339345
continue
340346

341-
if prevLine and prevLine[0] == "-" and len(prevLine) > 2:
347+
if prevLine and prevChange == self._dmp.DIFF_DELETE and not prevLine.isspace():
342348
# It's possible that only a few characters have changed in this line.
343349
# If so, we want to speak just the changed section, rather than the entire line.
344-
prevText = prevLine[2:]
345350
textLen = len(text)
346-
prevTextLen = len(prevText)
351+
prevLineLen = len(prevLine)
347352
# Find the first character that differs between the two lines.
348-
for pos in range(min(textLen, prevTextLen)):
349-
if text[pos] != prevText[pos]:
353+
for pos in range(min(textLen, prevLineLen)):
354+
if text[pos] != prevLine[pos]:
350355
start = pos
351356
break
352357
else:
353358
# We haven't found a differing character so far and we've hit the end of one of the lines.
354359
# This means that the differing text starts here.
355360
start = pos + 1
356361
# Find the end of the differing text.
357-
if textLen != prevTextLen:
362+
if textLen != prevLineLen:
358363
# The lines are different lengths, so assume the rest of the line changed.
359364
end = textLen
360365
else:
361366
for pos in range(textLen - 1, start - 1, -1):
362-
if text[pos] != prevText[pos]:
367+
if text[pos] != prevLine[pos]:
363368
end = pos + 1
364369
break
365370

@@ -369,9 +374,10 @@ def _calculateNewText(self, newLines, oldLines):
369374

370375
if text and not text.isspace():
371376
outLines.append(text)
372-
prevLine = line
377+
prevChange, prevLine = change, text
378+
379+
return [line for outLine in outLines for line in outLine.splitlines() if line and not line.isspace()]
373380

374-
return outLines
375381

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

source/NVDAObjects/window/winConsole.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
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+
# A part of NonVisual Desktop Access (NVDA)
2+
# This file is covered by the GNU General Public License.
3+
# See the file COPYING for more details.
4+
# Copyright (C) 2007-2020 NV Access Limited, Bill Dengler
65

76
import winConsoleHandler
87
from . import Window
@@ -69,8 +68,8 @@ def event_loseFocus(self):
6968
def event_nameChange(self):
7069
pass
7170

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

7574
def script_caret_backspaceCharacter(self, gesture):
7675
super(WinConsole, self).script_caret_backspaceCharacter(gesture)

source/winConsoleHandler.py

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

76
import gui
87
import winUser
@@ -123,14 +122,14 @@ def _checkDead():
123122
except:
124123
log.exception()
125124

126-
def getConsoleVisibleLines():
125+
126+
def getConsoleVisibleText():
127127
consoleScreenBufferInfo=wincon.GetConsoleScreenBufferInfo(consoleOutputHandle)
128128
topLine=consoleScreenBufferInfo.srWindow.Top
129129
lineCount=(consoleScreenBufferInfo.srWindow.Bottom-topLine)+1
130130
lineLength=consoleScreenBufferInfo.dwSize.x
131131
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
132+
return text
134133

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

0 commit comments

Comments
 (0)