Skip to content

Commit

Permalink
Prefer Difflib in scrolling LiveText objects and allow users to force…
Browse files Browse the repository at this point in the history
…ably override this choice.
  • Loading branch information
codeofdusk committed Oct 21, 2021
1 parent 5114ccc commit 21b7ee5
Show file tree
Hide file tree
Showing 8 changed files with 53 additions and 28 deletions.
10 changes: 1 addition & 9 deletions source/NVDAObjects/IAccessible/winConsole.py
Expand Up @@ -27,15 +27,7 @@ class LegacyWinConsole(winConsole.WinConsole, IAccessible):
NVDA's original console support, used by default on Windows versions
before 1607.
"""

def _get_diffAlgo(self):
# Non-enhanced legacy consoles use caret proximity to detect
# typed/deleted text.
# Single-character changes are not reported as
# they are confused for typed characters.
# Force difflib to keep meaningful edit reporting in these consoles.
from diffHandler import get_difflib_algo
return get_difflib_algo()
pass


def findExtraOverlayClasses(obj, clsList):
Expand Down
9 changes: 9 additions & 0 deletions source/NVDAObjects/UIA/winConsoleUIA.py
Expand Up @@ -10,6 +10,7 @@
import UIAHandler

from comtypes import COMError
from diffHandler import prefer_difflib
from logHandler import log
from UIAUtils import _getConhostAPILevel
from _UIAConstants import WinConsoleAPILevel
Expand Down Expand Up @@ -379,6 +380,14 @@ def _get_devInfo(self):
info.append(f"API level: {self.apiLevel} ({self.apiLevel.name})")
return info

def _get_diffAlgo(self):
if self.apiLevel < WinConsoleAPILevel.FORMATTED:
# These consoles are constrained to onscreen text.
# Use Difflib to reduce choppiness in reading.
return prefer_difflib()
else:
return super().diffAlgo

def detectPossibleSelectionChange(self):
try:
return super().detectPossibleSelectionChange()
Expand Down
15 changes: 9 additions & 6 deletions source/NVDAObjects/behaviors.py
Expand Up @@ -269,14 +269,17 @@ def event_textChange(self):
def _get_diffAlgo(self):
"""
This property controls which diffing algorithm should be used by
this object. Most subclasses should simply use the base
implementation, which returns DMP (character-based diffing).
this object. If the object contains a strictly contiguous
span of text (i.e. textInfos.POSITION_ALL refers to the entire
contents of the object and not just one visible screen of text),
then diffHandler.prefer_dmp (character-based diffing) is suitable.
Otherwise, use diffHandler.prefer_difflib.
@Note: DMP is experimental, and can be disallowed via user
preference. In this case, the prior stable implementation, Difflib
(line-based diffing), will be used.
@Note: Barring a very good reason to do otherwise, please return
either diffHandler.prefer_dmp() or diffHandler.prefer_difflib()
so that user preference can override this choice.
"""
return diffHandler.get_dmp_algo()
return diffHandler.prefer_dmp()

def _get_devInfo(self):
info = super().devInfo
Expand Down
7 changes: 7 additions & 0 deletions source/NVDAObjects/window/__init__.py
Expand Up @@ -19,6 +19,7 @@
from NVDAObjects.behaviors import EditableText, EditableTextWithoutAutoSelectDetection, LiveText
import watchdog
from locationHelper import RectLTWH
from diffHandler import prefer_difflib

re_WindowsForms=re.compile(r'^WindowsForms[0-9]*\.(.*)\.app\..*$')
re_ATL=re.compile(r'^ATL:(.*)$')
Expand Down Expand Up @@ -413,6 +414,12 @@ def stopMonitoring(self):
super(DisplayModelLiveText, self).stopMonitoring()
displayModel.requestTextChangeNotifications(self, False)

def _get_diffAlgo(self):
# The display model gives us only one screen of text at a time.
# Use Difflib to reduce choppiness in reading.
return prefer_difflib()


windowClassMap={
"EDIT":"Edit",
"TTntEdit.UnicodeClass":"Edit",
Expand Down
6 changes: 6 additions & 0 deletions source/NVDAObjects/window/winConsole.py
Expand Up @@ -10,6 +10,7 @@
import core
from scriptHandler import script
import speech
from diffHandler import prefer_difflib

class WinConsole(Terminal, EditableTextWithoutAutoSelectDetection, Window):
"""
Expand All @@ -36,6 +37,11 @@ def _get_TextInfo(self):
return winConsoleHandler.WinConsoleTextInfo
return super(WinConsole,self).TextInfo

def _get_diffAlgo(self):
# Legacy consoles contain only one screen of text at a time.
# Use Difflib to reduce choppiness in reading.
return prefer_difflib()

def event_becomeNavigatorObject(self, isFocus=False):
if winConsoleHandler.consoleObject is not self:
if winConsoleHandler.consoleObject:
Expand Down
16 changes: 12 additions & 4 deletions source/diffHandler.py
Expand Up @@ -176,7 +176,7 @@ def _getText(self, ti: TextInfo) -> str:
return "\n".join(ti.getTextInChunks(UNIT_LINE))


def get_dmp_algo():
def prefer_dmp():
"""
This function returns a Diff Match Patch object if allowed by the user.
DMP is new and can be explicitly disabled by a user setting. If config
Expand All @@ -189,9 +189,17 @@ def get_dmp_algo():
)


def get_difflib_algo():
"Returns an instance of the difflib diffAlgo."
return _difflib
def prefer_difflib():
"""
This function returns a Difflib object if allowed by the user.
Difflib can be explicitly disabled by a user setting. If config
does not allow Difflib, this function returns a DMP instance instead.
"""
return (
_dmp
if config.conf["terminals"]["diffAlgo"] == "dmp"
else _difflib
)


_difflib = Difflib()
Expand Down
6 changes: 3 additions & 3 deletions source/gui/settingsDialogs.py
Expand Up @@ -2730,7 +2730,7 @@ def __init__(self, parent):
# Translators: This is the label for a combo box for selecting a
# method of detecting changed content in terminals in the advanced
# settings panel.
# Choices are automatic, allow Diff Match Patch, and force Difflib.
# Choices are automatic, force Diff Match Patch, and force Difflib.
diffAlgoComboText = _("&Diff algorithm:")
diffAlgoChoices = [
# Translators: A choice in a combo box in the advanced settings
Expand All @@ -2740,11 +2740,11 @@ def __init__(self, parent):
# Translators: A choice in a combo box in the advanced settings
# panel to have NVDA detect changes in terminals
# by character when supported, using the diff match patch algorithm.
_("allow Diff Match Patch"),
_("Diff Match Patch"),
# Translators: A choice in a combo box in the advanced settings
# panel to have NVDA detect changes in terminals
# by line, using the difflib algorithm.
_("force Difflib")
_("Difflib")
]
#: The possible diffAlgo config values, in the order they appear
#: in the combo box.
Expand Down
12 changes: 6 additions & 6 deletions user_docs/en/userGuide.t2t
Expand Up @@ -1895,14 +1895,14 @@ In untrusted environments, you may temporarily disable [speak typed characters #
==== Diff algorithm ====[DiffAlgo]
This setting controls how NVDA determines the new text to speak in terminals.
The diff algorithm combo box has three options:
- Automatic: as of NVDA 2021.2, this option is equivalent to "allow Diff Match Patch".
- allow Diff Match Patch: This option causes NVDA to calculate changes to terminal text by character.
- Automatic: as of NVDA 2021.2, this option is equivalent to "Diff Match Patch".
- Diff Match Patch: This option causes NVDA to calculate changes to terminal text by character, even in situations where it is not recommended.
It may improve performance when large volumes of text are written to the console and allow more accurate reporting of changes made in the middle of lines.
However, it may be incompatible with some applications, so Diff Match Patch is not always used.
This feature is supported in Windows Console on Windows 10 versions 1607 and later.
Additionally, it may be available in other terminals on earlier Windows releases.
- force Difflib: this option causes NVDA to calculate changes to terminal text by line.
However, in some applications, reading of new text may be choppy or inconsistent.
- Difflib: this option causes NVDA to calculate changes to terminal text by line, even in situations where it is not recommended.
It is identical to NVDA's behaviour in versions 2020.4 and earlier.
This setting may stabilize reading of incoming text in some applications.
However, in terminals, when inserting or deleting a character in the middle of a line, the text after the caret will be read out.
-

==== Attempt to cancel speech for expired focus events ====[CancelExpiredFocusSpeech]
Expand Down

0 comments on commit 21b7ee5

Please sign in to comment.