mirror of
https://github.com/keymanapp/keyman.git
synced 2026-10-17 11:27:39 +00:00
chore(developer): Merge branch 'beta' into fix/developer/remove-unused-keyboard-info-compiler-messages
This commit is contained in:
commit
ad568fccc6
28 changed files with 223 additions and 622 deletions
13
HISTORY.md
13
HISTORY.md
|
|
@ -1,5 +1,18 @@
|
|||
# Keyman Version History
|
||||
|
||||
## 17.0.291 beta 2024-03-20
|
||||
|
||||
* chore(mac): clean up code obsoleted by core (#10877)
|
||||
* fix(linux): Replace deprecated `pkg_resources` module with `packaging.version` (#10860)
|
||||
|
||||
## 17.0.290 beta 2024-03-19
|
||||
|
||||
* (#10956)
|
||||
|
||||
## 17.0.289 beta 2024-03-16
|
||||
|
||||
* fix(common): add unit test for --debug flag for builder (#10974)
|
||||
|
||||
## 17.0.288 beta 2024-03-13
|
||||
|
||||
* test(developer): kmc keyboard info compiler messages unit tests (#10848)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
17.0.289
|
||||
17.0.292
|
||||
|
|
@ -287,9 +287,16 @@ function setKeymanContext(text, doSync, selStart, selLength) {
|
|||
let selEnd = selStart + selLength;
|
||||
selEnd = isNaN(selEnd) ? undefined : selEnd;
|
||||
|
||||
const shouldReset = keyman.context.updateContext(text, selStart, selEnd);
|
||||
if(!doSync || shouldReset) {
|
||||
if(doSync) {
|
||||
const shouldReset = keyman.context.updateContext(text, selStart, selEnd);
|
||||
if(shouldReset) {
|
||||
keyman.resetContext();
|
||||
}
|
||||
} else {
|
||||
// if not in "sync" mode, we have a hard context reset; just full-reset it.
|
||||
keyman.context.setText(text);
|
||||
keyman.context.setSelection(selStart, selStart+selLength);
|
||||
keyman.resetContext();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ Build-Depends:
|
|||
python3-lxml,
|
||||
python3-magic,
|
||||
python3-numpy,
|
||||
python3-packaging,
|
||||
python3-pil,
|
||||
python3-pip,
|
||||
python3-qrcode,
|
||||
|
|
@ -83,13 +84,14 @@ Package: python3-keyman-config
|
|||
Section: python
|
||||
Architecture: all
|
||||
Depends:
|
||||
dbus-x11,
|
||||
dconf-cli,
|
||||
gir1.2-webkit2-4.1 | gir1.2-webkit2-4.0,
|
||||
keyman-engine,
|
||||
python3-bs4,
|
||||
python3-gi,
|
||||
python3-packaging,
|
||||
python3-sentry-sdk (>= 1.1),
|
||||
dbus-x11,
|
||||
${misc:Depends},
|
||||
${python3:Depends},
|
||||
Description: Keyman for Linux configuration
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
import json
|
||||
import logging
|
||||
import os
|
||||
import packaging.version
|
||||
import zipfile
|
||||
from pkg_resources import parse_version
|
||||
from enum import Enum
|
||||
from shutil import rmtree
|
||||
|
||||
|
|
@ -149,7 +149,7 @@ class InstallKmp():
|
|||
fileVersion = secure_lookup(system, 'fileVersion')
|
||||
if not fileVersion:
|
||||
fileVersion = '7.0'
|
||||
if parse_version(fileVersion) > parse_version(__version__):
|
||||
if packaging.version.parse(fileVersion) > packaging.version.parse(__version__):
|
||||
logging.error("install_kmp.py: error: %s requires a newer version of Keyman (%s)",
|
||||
inputfile, fileVersion)
|
||||
rmtree(self.packageDir)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import subprocess
|
|||
import sys
|
||||
import tempfile
|
||||
import webbrowser
|
||||
import packaging.version
|
||||
|
||||
import gi
|
||||
|
||||
|
|
@ -20,8 +21,6 @@ except ValueError:
|
|||
# TODO: Remove once we drop support for Ubuntu 20.04 Focal
|
||||
gi.require_version('WebKit2', '4.0')
|
||||
|
||||
from pkg_resources import parse_version
|
||||
|
||||
from gi.repository import Gtk, WebKit2
|
||||
|
||||
from keyman_config import _, secure_lookup
|
||||
|
|
@ -134,11 +133,12 @@ class InstallKmpWindow(Gtk.Dialog):
|
|||
self.checkcontinue = False
|
||||
|
||||
def _uninstall_prev_version_if_necessary(self, keyboardid, viewkmp, installed_kmp_ver, info):
|
||||
if not installed_kmp_ver or not secure_lookup(info, 'version', 'description'):
|
||||
new_kmp_version = secure_lookup(info, 'version', 'description')
|
||||
if not installed_kmp_ver or not new_kmp_version:
|
||||
return True
|
||||
logging.info("package version %s", secure_lookup(info, 'version', 'description'))
|
||||
logging.info("package version %s", new_kmp_version)
|
||||
logging.info("installed kmp version %s", installed_kmp_ver)
|
||||
if parse_version(secure_lookup(info, 'version', 'description')) >= parse_version(installed_kmp_ver):
|
||||
if packaging.version.parse(new_kmp_version) >= packaging.version.parse(installed_kmp_ver):
|
||||
return True
|
||||
response = self._show_version_message_dlg(
|
||||
viewkmp,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import json
|
||||
import logging
|
||||
import os
|
||||
from pkg_resources import parse_version
|
||||
import packaging.version
|
||||
|
||||
from keyman_config import secure_lookup
|
||||
from keyman_config.deprecated_decorator import deprecated
|
||||
|
|
@ -133,7 +133,7 @@ def _get_kmp_version_internal(packageId, location, previousVersion):
|
|||
kmp = get_installed_kmp(location)
|
||||
if packageId in kmp:
|
||||
version = kmp[packageId]['kmpversion']
|
||||
if not previousVersion or parse_version(previousVersion) < parse_version(version):
|
||||
if not previousVersion or packaging.version.parse(previousVersion) < packaging.version.parse(version):
|
||||
previousVersion = version
|
||||
return previousVersion
|
||||
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@ import argparse
|
|||
import datetime
|
||||
import logging
|
||||
import os
|
||||
import requests
|
||||
import requests_cache
|
||||
import sys
|
||||
import time
|
||||
from pkg_resources import parse_version
|
||||
from zipfile import is_zipfile
|
||||
import packaging.version
|
||||
import requests
|
||||
import requests_cache
|
||||
|
||||
from keyman_config import KeymanApiUrl, add_standard_arguments, initialize_logging, initialize_sentry, secure_lookup
|
||||
from keyman_config.fcitx_util import is_fcitx_running
|
||||
|
|
@ -181,7 +181,7 @@ def main():
|
|||
sys.exit(3)
|
||||
pkg_version = secure_lookup(pkgdata, 'version')
|
||||
if installed_kmp_ver and pkg_version:
|
||||
if parse_version(installed_kmp_ver) > parse_version(pkg_version):
|
||||
if packaging.version.parse(installed_kmp_ver) > packaging.version.parse(pkg_version):
|
||||
if args.force:
|
||||
logging.warning(f'km-package-install: warning: The {args.package} keyboard ' +
|
||||
f'is already installed with a newer version {installed_kmp_ver}. ' +
|
||||
|
|
|
|||
|
|
@ -17,8 +17,17 @@ setup(
|
|||
zip_safe=False, # don't install a zip
|
||||
|
||||
install_requires=[
|
||||
'lxml', 'numpy', 'Pillow', 'requests', 'requests-cache',
|
||||
'python-magic', 'qrcode', 'sentry-sdk'
|
||||
'dbus-python',
|
||||
'lxml',
|
||||
'numpy',
|
||||
'packaging',
|
||||
'pillow',
|
||||
'PyGObject',
|
||||
'python-magic',
|
||||
'qrcode',
|
||||
'requests-cache',
|
||||
'requests',
|
||||
'sentry-sdk',
|
||||
],
|
||||
|
||||
# metadata to display on PyPI
|
||||
|
|
|
|||
|
|
@ -75,18 +75,14 @@
|
|||
E213601E2142D7C000A043B7 /* keyman-88.png in Resources */ = {isa = PBXBuildFile; fileRef = E213601D2142D7BF00A043B7 /* keyman-88.png */; };
|
||||
E21799051FC5B7BC00F2D66A /* KMInputMethodEventHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = E21799041FC5B7BC00F2D66A /* KMInputMethodEventHandler.m */; };
|
||||
E21E645820C04EA8000D6274 /* logo.png in Resources */ = {isa = PBXBuildFile; fileRef = E21E645720C04EA7000D6274 /* logo.png */; };
|
||||
E22020F6200506F300B74FAC /* KMInputMethodSafariClientEventHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = E22020F5200506F300B74FAC /* KMInputMethodSafariClientEventHandler.m */; };
|
||||
E23380FB21407AA100B90591 /* SILInBlue76.png in Resources */ = {isa = PBXBuildFile; fileRef = E23380FA21407AA100B90591 /* SILInBlue76.png */; };
|
||||
E240F599202DED740000067D /* KMPackage.m in Sources */ = {isa = PBXBuildFile; fileRef = E240F598202DED740000067D /* KMPackage.m */; };
|
||||
E24C79EA1FFFCC7500D8E46F /* KMInputMethodBrowserClientEventHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = E24C79E91FFFCC7500D8E46F /* KMInputMethodBrowserClientEventHandler.m */; };
|
||||
E2585A7F20DD6C3C00CBB994 /* KMMethodEventHandlerTests.kmx in Resources */ = {isa = PBXBuildFile; fileRef = E2585A7E20DD6C3C00CBB994 /* KMMethodEventHandlerTests.kmx */; };
|
||||
E2585A8120DD7CF100CBB994 /* TestAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E2585A8020DD7CF100CBB994 /* TestAppDelegate.m */; };
|
||||
E27BA9D32020D8BF00D273E7 /* SimpleTest.kmx in Resources */ = {isa = PBXBuildFile; fileRef = E27BA9D22020D8BF00D273E7 /* SimpleTest.kmx */; };
|
||||
E28914D32024D1B80048E71E /* KeymanEngine4Mac.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 984C2B211A79DF1B0023F89D /* KeymanEngine4Mac.framework */; };
|
||||
E28914D52024E3DF0048E71E /* KMInputMethodBrowserClientEventHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = E24C79E91FFFCC7500D8E46F /* KMInputMethodBrowserClientEventHandler.m */; };
|
||||
E28914D62024E4010048E71E /* KMInputMethodEventHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = E21799041FC5B7BC00F2D66A /* KMInputMethodEventHandler.m */; };
|
||||
E28914D72024E6220048E71E /* KeymanEngine4Mac.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 984C2B211A79DF1B0023F89D /* KeymanEngine4Mac.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
E29F22F4201A59B1005EA234 /* KMInputMethodBrowserClientEventHandlerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E29F22F3201A59B1005EA234 /* KMInputMethodBrowserClientEventHandlerTests.m */; };
|
||||
E2D5529D20DD8E0200D77CFB /* LegacyTestClient.m in Sources */ = {isa = PBXBuildFile; fileRef = E2D5529C20DD8E0200D77CFB /* LegacyTestClient.m */; };
|
||||
F9B9814A51575496B5DA2948 /* Pods_KeymanTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A71FD8BA8404531DFBD861B6 /* Pods_KeymanTests.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
|
@ -357,22 +353,15 @@
|
|||
E21799031FC5B74D00F2D66A /* KMInputMethodEventHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KMInputMethodEventHandler.h; sourceTree = "<group>"; };
|
||||
E21799041FC5B7BC00F2D66A /* KMInputMethodEventHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KMInputMethodEventHandler.m; sourceTree = "<group>"; };
|
||||
E21E645720C04EA7000D6274 /* logo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = logo.png; path = Keyman4MacIM/KMConfiguration/logo.png; sourceTree = SOURCE_ROOT; };
|
||||
E22020F3200505EF00B74FAC /* KMInputMethodSafariClientEventHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KMInputMethodSafariClientEventHandler.h; sourceTree = "<group>"; };
|
||||
E22020F5200506F300B74FAC /* KMInputMethodSafariClientEventHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KMInputMethodSafariClientEventHandler.m; sourceTree = "<group>"; };
|
||||
E22020F920050D6300B74FAC /* KMInputMethodBrowserClientEventHandlerProtected.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KMInputMethodBrowserClientEventHandlerProtected.h; sourceTree = "<group>"; };
|
||||
E23380FA21407AA100B90591 /* SILInBlue76.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = SILInBlue76.png; path = Keyman4MacIM/Images/SILInBlue76.png; sourceTree = SOURCE_ROOT; };
|
||||
E240F597202DED300000067D /* KMPackage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KMPackage.h; sourceTree = "<group>"; };
|
||||
E240F598202DED740000067D /* KMPackage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KMPackage.m; sourceTree = "<group>"; };
|
||||
E24C79E91FFFCC7500D8E46F /* KMInputMethodBrowserClientEventHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KMInputMethodBrowserClientEventHandler.m; sourceTree = "<group>"; };
|
||||
E24C79EB1FFFCE3000D8E46F /* KMInputMethodBrowserClientEventHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KMInputMethodBrowserClientEventHandler.h; sourceTree = "<group>"; };
|
||||
E24C79EC1FFFEA6B00D8E46F /* KMInputMethodEventHandlerProtected.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KMInputMethodEventHandlerProtected.h; sourceTree = "<group>"; };
|
||||
E2585A7E20DD6C3C00CBB994 /* KMMethodEventHandlerTests.kmx */ = {isa = PBXFileReference; lastKnownFileType = file; path = KMMethodEventHandlerTests.kmx; sourceTree = "<group>"; };
|
||||
E2585A8020DD7CF100CBB994 /* TestAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestAppDelegate.m; sourceTree = "<group>"; };
|
||||
E2585A8220DD7D3F00CBB994 /* TestAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TestAppDelegate.h; sourceTree = "<group>"; };
|
||||
E27BA9CD2020322000D273E7 /* OCMock.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OCMock.framework; path = ../Carthage/Build/Mac/OCMock.framework; sourceTree = "<group>"; };
|
||||
E27BA9D22020D8BF00D273E7 /* SimpleTest.kmx */ = {isa = PBXFileReference; lastKnownFileType = file; path = SimpleTest.kmx; sourceTree = "<group>"; };
|
||||
E29F22F1201A59B1005EA234 /* KeymanTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KeymanTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
E29F22F3201A59B1005EA234 /* KMInputMethodBrowserClientEventHandlerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KMInputMethodBrowserClientEventHandlerTests.m; sourceTree = "<group>"; };
|
||||
E29F22F5201A59B2005EA234 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
E2C92D4720B46CEA00742A7D /* KeymanEngine4Mac.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = KeymanEngine4Mac.framework.dSYM; path = Keyman4MacIM/Frameworks/KeymanEngine4Mac.framework.dSYM; sourceTree = "<group>"; };
|
||||
E2D5529B20DD8D6100D77CFB /* LegacyTestClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LegacyTestClient.h; sourceTree = "<group>"; };
|
||||
|
|
@ -568,12 +557,6 @@
|
|||
E21799041FC5B7BC00F2D66A /* KMInputMethodEventHandler.m */,
|
||||
298D09F62A1F4533006B9DFE /* TextApiCompliance.h */,
|
||||
298D09F52A1F4533006B9DFE /* TextApiCompliance.m */,
|
||||
E24C79EC1FFFEA6B00D8E46F /* KMInputMethodEventHandlerProtected.h */,
|
||||
E22020F920050D6300B74FAC /* KMInputMethodBrowserClientEventHandlerProtected.h */,
|
||||
E24C79EB1FFFCE3000D8E46F /* KMInputMethodBrowserClientEventHandler.h */,
|
||||
E22020F3200505EF00B74FAC /* KMInputMethodSafariClientEventHandler.h */,
|
||||
E24C79E91FFFCC7500D8E46F /* KMInputMethodBrowserClientEventHandler.m */,
|
||||
E22020F5200506F300B74FAC /* KMInputMethodSafariClientEventHandler.m */,
|
||||
98E672961B532DE000DBDE2F /* KMKeyboardHelpWindow */,
|
||||
989C9C141A7876DE00A20425 /* MainMenu.xib */,
|
||||
984B8F3E1AF1C2F400E096A8 /* OnScreenKeyboard */,
|
||||
|
|
@ -689,7 +672,6 @@
|
|||
children = (
|
||||
E2585A7E20DD6C3C00CBB994 /* KMMethodEventHandlerTests.kmx */,
|
||||
E27BA9D22020D8BF00D273E7 /* SimpleTest.kmx */,
|
||||
E29F22F3201A59B1005EA234 /* KMInputMethodBrowserClientEventHandlerTests.m */,
|
||||
E29F22F5201A59B2005EA234 /* Info.plist */,
|
||||
E2585A8220DD7D3F00CBB994 /* TestAppDelegate.h */,
|
||||
E2585A8020DD7CF100CBB994 /* TestAppDelegate.m */,
|
||||
|
|
@ -997,8 +979,6 @@
|
|||
9836B3721AE5F11D00780482 /* unzip.c in Sources */,
|
||||
296FE2FC275DD21600F46898 /* KMPackageReader.m in Sources */,
|
||||
9832472E1A9EABC70010B90C /* KMConfigColumn1CellView.m in Sources */,
|
||||
E22020F6200506F300B74FAC /* KMInputMethodSafariClientEventHandler.m in Sources */,
|
||||
E24C79EA1FFFCC7500D8E46F /* KMInputMethodBrowserClientEventHandler.m in Sources */,
|
||||
297A501928DF4D360074EB1B /* PrivacyConsent.m in Sources */,
|
||||
98FE105E1B4DE8F400525F54 /* NSWindow+SuppMethods.m in Sources */,
|
||||
9836B3741AE5F11D00780482 /* ZipArchive.mm in Sources */,
|
||||
|
|
@ -1022,8 +1002,6 @@
|
|||
2999C69929D3E4CE006366F5 /* InputMethodTests.m in Sources */,
|
||||
E211769D20E182DD00F8065D /* NoContextTestClient.m in Sources */,
|
||||
E2D5529D20DD8E0200D77CFB /* LegacyTestClient.m in Sources */,
|
||||
E29F22F4201A59B1005EA234 /* KMInputMethodBrowserClientEventHandlerTests.m in Sources */,
|
||||
E28914D52024E3DF0048E71E /* KMInputMethodBrowserClientEventHandler.m in Sources */,
|
||||
E21176A020E18C5200F8065D /* AppleCompliantTestClient.m in Sources */,
|
||||
E2585A8120DD7CF100CBB994 /* TestAppDelegate.m in Sources */,
|
||||
E28914D62024E4010048E71E /* KMInputMethodEventHandler.m in Sources */,
|
||||
|
|
@ -1424,7 +1402,7 @@
|
|||
"@loader_path/../Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.7;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.sil.consoleTestbed.KeymanTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
|
@ -1458,7 +1436,7 @@
|
|||
"@executable_path/../Frameworks",
|
||||
"@loader_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.7;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.sil.consoleTestbed.KeymanTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@
|
|||
|
||||
#import "KMInputController.h"
|
||||
#import "KMInputMethodEventHandler.h"
|
||||
#import "KMInputMethodBrowserClientEventHandler.h"
|
||||
#import "KMInputMethodSafariClientEventHandler.h"
|
||||
#import "KMOSVersion.h"
|
||||
#include <Carbon/Carbon.h> /* For kVK_ constants. */
|
||||
|
||||
|
|
@ -83,22 +81,6 @@ NSMutableArray *servers;
|
|||
NSLog(@"activateServer, new active app: '%@', sender %@", clientAppId, sender);
|
||||
}
|
||||
|
||||
// TODO: uncomment? commented the browser cases to see how they work with core code
|
||||
/*
|
||||
// Most things in Safari work well using the normal way, but Google Docs doesn't.
|
||||
if ([clientAppId isEqual: @"com.apple.Safari"]) {
|
||||
_eventHandler = [[KMInputMethodSafariClientEventHandler alloc] init];
|
||||
}
|
||||
else if ([clientAppId isEqual: @"org.mozilla.firefox"] ||
|
||||
[clientAppId isEqual: @"com.google.Chrome"]) {
|
||||
_eventHandler = [[KMInputMethodBrowserClientEventHandler alloc] init];
|
||||
}
|
||||
else {
|
||||
// We cache the client for use with events sourced from the low level tap
|
||||
// where we don't necessarily have any access to the current client.
|
||||
_eventHandler = [[KMInputMethodEventHandler alloc] initWithClient:clientAppId client:sender];
|
||||
}
|
||||
*/
|
||||
_eventHandler = [[KMInputMethodEventHandler alloc] initWithClient:clientAppId client:sender];
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -335,9 +335,14 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
// of events)
|
||||
if(sysEvent.keyCode == kVK_Delete && appDelegate.inputController != nil) {
|
||||
NSLog(@"Event tap handling kVK_Delete.");
|
||||
[appDelegate.inputController handleBackspace:sysEvent];
|
||||
[appDelegate.inputController handleBackspace:sysEvent];
|
||||
} else if(sysEvent.keyCode == kVK_Delete) {
|
||||
NSLog(@"Event tap not handling kVK_Delete, appDelegate.inputController = %@", appDelegate.inputController);
|
||||
}
|
||||
if(sysEvent.keyCode == 255) {
|
||||
NSLog(@"*** kKeymanEventKeyCode = 0xFF");
|
||||
} else {
|
||||
NSLog(@"Event tap not handling kVK_Delete, appDelegate.inputController = %@", appDelegate.inputController);
|
||||
NSLog(@"*** other: %d(%x)", (char) sysEvent.keyCode, sysEvent.keyCode);
|
||||
}
|
||||
|
||||
switch(sysEvent.keyCode) {
|
||||
|
|
@ -443,7 +448,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
return;
|
||||
}
|
||||
|
||||
// TODO pass array instead of making repeated calls
|
||||
// TODO: pass array instead of making repeated calls
|
||||
for (NSString *key in persistedOptionsForSelectedKeyboard) {
|
||||
NSString *value = [persistedOptionsForSelectedKeyboard objectForKey:key];
|
||||
NSLog(@"persisted options found in UserDefaults for keyboard %@, key: %@, value: %@", _selectedKeyboard, key, value);
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
//
|
||||
// KMInputMethodBrowserClientEventHandler.h
|
||||
// Keyman4MacIM
|
||||
// This class is used for browser clients because certain javascript-based websites, such as Google Docs) do
|
||||
// not properly deal with calls to insertText calls that replace a range of characters. So for these "legacy"
|
||||
// apps, we post one or more deletes (i.e., backspace), followed by a special code that tells us we're now
|
||||
// ready to insert the composed text.
|
||||
//
|
||||
// Created by tom on 1/5/18.
|
||||
// Copyright © 2018 SIL International. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef KMInputMethodBrowserClientEventHandler_h
|
||||
#define KMInputMethodBrowserClientEventHandler_h
|
||||
#import "KMInputMethodEventHandler.h"
|
||||
|
||||
@interface KMInputMethodBrowserClientEventHandler : KMInputMethodEventHandler
|
||||
@end
|
||||
|
||||
#endif /* KMInputMethodBrowserClientEventHandler_h */
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
//
|
||||
// KMInputMethodBrowserClientEventHandler.m
|
||||
// Keyman
|
||||
//
|
||||
// Created by tom on 1/5/18.
|
||||
// Copyright © 2018 SIL International. All rights reserved.
|
||||
//
|
||||
|
||||
#import "KMInputMethodBrowserClientEventHandler.h"
|
||||
#import "KMInputMethodEventHandlerProtected.h"
|
||||
#import "KMInputMethodBrowserClientEventHandlerProtected.h"
|
||||
|
||||
@implementation KMInputMethodBrowserClientEventHandler
|
||||
|
||||
// Because Google Docs can't report its context in any of the browsers (Safari, Chrome, Firefox) and Word sometimes
|
||||
// has trouble doing this in Chrome, we want to try to detect it so each browser can respond appropriately.
|
||||
NSUInteger _failuresToRetrieveExpectedContext;
|
||||
|
||||
- (instancetype)init {
|
||||
return [self initWithLegacyMode:YES clientSelectionCanChangeUnexpectedly:YES];
|
||||
}
|
||||
|
||||
- (instancetype)initWithLegacyMode:(BOOL)legacy clientSelectionCanChangeUnexpectedly:(BOOL) flagClientSelectionCanChangeUnexpectedly {
|
||||
self = [super initWithLegacyMode:legacy clientSelectionCanChangeUnexpectedly: flagClientSelectionCanChangeUnexpectedly];
|
||||
if (self) {
|
||||
_failuresToRetrieveExpectedContext = 0;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)checkContextIn:(id) client {
|
||||
if (!self.willDeleteNullChar && !self.contextOutOfDate && _failuresToRetrieveExpectedContext < 3) {
|
||||
if (self.appDelegate.debugMode) {
|
||||
NSLog(@"Checking to see if we're in Google Docs (or some other site that can't give context).");
|
||||
}
|
||||
NSUInteger bufferLength = self.contextBuffer.length;
|
||||
if (bufferLength) {
|
||||
NSUInteger location = [client selectedRange].location;
|
||||
|
||||
if (location != NSNotFound && location > 0) {
|
||||
if ([self appDelegate].debugMode)
|
||||
NSLog(@"Trying to get context up to location %lu", location);
|
||||
NSString* clientContext = [self getLimitedContextFrom:client at:location];
|
||||
if (clientContext == nil)
|
||||
{
|
||||
// Client is failing to provide useful response to attributedSubstringFromRange.
|
||||
// Word (in MS Live) occasionally does this.
|
||||
[self setInSiteThatDoesNotGiveContext];
|
||||
}
|
||||
else if (!clientContext.length) {
|
||||
if ([self appDelegate].debugMode) {
|
||||
NSLog(@"Expected context = '%@'", self.contextBuffer);
|
||||
NSLog(@"Actual clientContext was empty");
|
||||
}
|
||||
_failuresToRetrieveExpectedContext++;
|
||||
}
|
||||
else
|
||||
{
|
||||
unichar lastCodepointExpected = [self.contextBuffer characterAtIndex:bufferLength - 1];
|
||||
unichar lastCodepointInClient = [clientContext characterAtIndex:clientContext.length - 1];
|
||||
if (lastCodepointExpected != lastCodepointInClient) {
|
||||
if ([self appDelegate].debugMode) {
|
||||
NSLog(@"Expected context = '%@'", self.contextBuffer);
|
||||
NSLog(@"Actual clientContext = '%@'", (clientContext == nil ? @"{nil}" : clientContext));
|
||||
NSLog(@"Last character expected (in contextBuffer) = '%lu'", (unsigned long)lastCodepointExpected);
|
||||
NSLog(@"Last character in clientContext = '%lu'", (unsigned long)lastCodepointInClient);
|
||||
}
|
||||
// MS Word converts/reports plain spaces (32) as non-breaking spaces (160). If we
|
||||
// get this kind of mismatch, we don't want to count this as a definite match, but
|
||||
// we also don't want to count it as a failure to match. So just wait for a more
|
||||
// reliable character to test.
|
||||
if (lastCodepointInClient != 160 || lastCodepointExpected != 32) {
|
||||
_failuresToRetrieveExpectedContext++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ([self appDelegate].debugMode) {
|
||||
NSLog(@"We got what we were expecting from the client. We can stop checking.");
|
||||
}
|
||||
_failuresToRetrieveExpectedContext = NSUIntegerMax;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ([self appDelegate].debugMode) {
|
||||
NSLog(@"bufferLength is %lu, but location was %lu.", bufferLength, location);
|
||||
}
|
||||
_failuresToRetrieveExpectedContext++;
|
||||
}
|
||||
|
||||
if (_failuresToRetrieveExpectedContext == 3)
|
||||
[self setInSiteThatDoesNotGiveContext];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setInSiteThatDoesNotGiveContext {
|
||||
if ([self appDelegate].debugMode) {
|
||||
NSLog(@"Detected some editor (e.g., Google Docs) that can't provide context.");
|
||||
}
|
||||
_failuresToRetrieveExpectedContext = NSUIntegerMax;
|
||||
|
||||
if (self.clientSelectionCanChangeUnexpectedly) {
|
||||
self.clientCanProvideSelectionInfo = Unreliable;
|
||||
self.clientSelectionCanChangeUnexpectedly = NO; // This isn't true (it can change unexpectedly), but we can't get the context, so we pretend/hope it won't.
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
//
|
||||
// KMInputMethodBrowserClientEventHandlerProtected.h
|
||||
// Keyman4MacIM
|
||||
//
|
||||
// Created by tom on 1/9/18.
|
||||
// Copyright © 2018 SIL International. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef KMInputMethodEventBrowserClientHandlerProtected_h
|
||||
#define KMInputMethodEventBrowserClientHandlerProtected_h
|
||||
|
||||
@interface KMInputMethodBrowserClientEventHandler ()
|
||||
|
||||
- (void)setInSiteThatDoesNotGiveContext;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* KMInputMethodEventBrowserClientHandlerProtected_h */
|
||||
|
|
@ -6,19 +6,17 @@
|
|||
// Copyright © 2017-2018 SIL International. All rights reserved.
|
||||
//
|
||||
#import "KMInputMethodEventHandler.h"
|
||||
#import "KMInputMethodEventHandlerProtected.h"
|
||||
#import <KeymanEngine4Mac/KeymanEngine4Mac.h>
|
||||
#import <Carbon/Carbon.h> /* For kVK_ constants. */
|
||||
#import "KeySender.h"
|
||||
#import "TextApiCompliance.h"
|
||||
|
||||
@import Sentry;
|
||||
|
||||
@interface KMInputMethodEventHandler ()
|
||||
@property (nonatomic, retain) KeySender* keySender;
|
||||
@property (nonatomic, retain) TextApiCompliance* apiCompliance;
|
||||
@property NSUInteger generatedBackspaceCount;
|
||||
@property BOOL backspaceHandledLowLevel;
|
||||
@property NSUInteger lowLevelBackspaceCount;
|
||||
@property (nonatomic, retain) NSString* clientApplicationId;
|
||||
@property NSString *queuedText;
|
||||
@property BOOL contextChanged;
|
||||
|
|
@ -30,8 +28,6 @@ const CGKeyCode kProcessPendingBuffer = 0xFF;
|
|||
const NSUInteger kMaxContext = 80;
|
||||
const NSTimeInterval kQueuedTextEventDelayInSeconds = 0.1;
|
||||
|
||||
//_pendingBuffer contains text that is ready to be sent to the client when all delete-backs are finished.
|
||||
NSMutableString* _pendingBuffer;
|
||||
CGKeyCode _keyCodeOfOriginalEvent;
|
||||
CGEventSourceRef _sourceFromOriginalEvent = nil;
|
||||
CGEventSourceRef _sourceForGeneratedEvent = nil;
|
||||
|
|
@ -39,32 +35,13 @@ NSMutableString* _easterEggForSentry = nil;
|
|||
NSString* const kEasterEggText = @"Sentry force now";
|
||||
NSString* const kEasterEggKmxName = @"EnglishSpanish.kmx";
|
||||
|
||||
NSRange _previousSelRange;
|
||||
|
||||
// Protected initializer for use by subclasses
|
||||
- (instancetype)initWithLegacyMode:(BOOL)legacy clientSelectionCanChangeUnexpectedly:(BOOL) flagClientSelectionCanChangeUnexpectedly {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_previousSelRange = NSMakeRange(NSNotFound, NSNotFound);
|
||||
_clientSelectionCanChangeUnexpectedly = flagClientSelectionCanChangeUnexpectedly;
|
||||
_clientCanProvideSelectionInfo = Unknown;
|
||||
_legacyMode = NO;
|
||||
_contextOutOfDate = YES;
|
||||
if (legacy) {
|
||||
[self switchToLegacyMode];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
// This is the public initializer.
|
||||
- (instancetype)initWithClient:(NSString *)clientAppId client:(id) sender {
|
||||
self.senderForDeleteBack = sender;
|
||||
_keySender = [[KeySender alloc] init];
|
||||
_clientApplicationId = clientAppId;
|
||||
_generatedBackspaceCount = 0;
|
||||
|
||||
//BOOL legacy = [self isClientAppLegacy:clientAppId];
|
||||
_lowLevelBackspaceCount = 0;
|
||||
_queuedText = nil;
|
||||
|
||||
// In Xcode, if Keyman is the active IM and is in "debugMode" and "English plus Spanish" is
|
||||
// the current keyboard and you type "Sentry force now", it will force a simulated crash to
|
||||
|
|
@ -76,16 +53,7 @@ NSRange _previousSelRange;
|
|||
else
|
||||
_easterEggForSentry = nil;
|
||||
|
||||
// For the Atom editor, this isn't really true (the context CAN change unexpectedly), but we can't get
|
||||
// the context, so we pretend/hope it won't.
|
||||
BOOL selectionCanChangeUnexpectedly = (![clientAppId isEqual: @"com.github.atom"]);
|
||||
return [self initWithLegacyMode:self.apiCompliance.mustBackspaceUsingEvents clientSelectionCanChangeUnexpectedly:selectionCanChangeUnexpectedly];
|
||||
}
|
||||
|
||||
- (void)switchToLegacyMode {
|
||||
_legacyMode = YES;
|
||||
if ([self.appDelegate debugMode])
|
||||
NSLog(@"Using legacy mode for this app.");
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)deactivate {
|
||||
|
|
@ -121,39 +89,12 @@ NSRange _previousSelRange;
|
|||
// situation where a user could reasonably hope that any dead-keys typed before using a
|
||||
// command shortcut would be remembered, so other than an insignificant performance penalty,
|
||||
// the only downside to treating all commands as having the potential to change the selection
|
||||
// is that some "legacy" apps can't get their context at all. This can be overridden so that
|
||||
// legacy apps can mitigate this problem as appropriate.
|
||||
// is that some non-compliant apps can't get their context at all. This can be overridden so that
|
||||
// non-compliant apps can mitigate this problem as appropriate.
|
||||
[self.appDelegate logDebugMessage:@"KMInputMethodEventHandler handleCommand, event type=%@, must refresh context", event.type];
|
||||
self.contextChanged = YES;
|
||||
}
|
||||
|
||||
- (void)checkContextIn:(id)client {
|
||||
// Base implementation is no-op
|
||||
}
|
||||
|
||||
- (void)replaceExistingSelectionIn:(id)client with:(NSString *) text {
|
||||
[client insertText:text replacementRange:NSMakeRange(NSNotFound, NSNotFound)];
|
||||
_previousSelRange.location += text.length;
|
||||
_previousSelRange.length = 0;
|
||||
}
|
||||
|
||||
- (void)insertPendingBufferTextIn:(id)client {
|
||||
NSUInteger length = [self pendingBuffer].length;
|
||||
if (!length) {
|
||||
[self.appDelegate logDebugMessage:@"Error - expected text in pending buffer!"];
|
||||
return;
|
||||
}
|
||||
NSString* text = [self pendingBuffer];
|
||||
|
||||
[self.appDelegate logDebugMessage:@"Inserting text from pending buffer: \"%@\"", text];
|
||||
|
||||
[client insertText:text replacementRange:NSMakeRange(NSNotFound, NSNotFound)];
|
||||
_previousSelRange.location += text.length;
|
||||
_previousSelRange.length = 0;
|
||||
|
||||
[self setPendingBuffer:@""];
|
||||
}
|
||||
|
||||
- (KMInputMethodAppDelegate *)appDelegate {
|
||||
return (KMInputMethodAppDelegate *)[NSApp delegate];
|
||||
}
|
||||
|
|
@ -162,54 +103,6 @@ NSRange _previousSelRange;
|
|||
return self.appDelegate.kme;
|
||||
}
|
||||
|
||||
- (NSMutableString *)contextBuffer {
|
||||
if (_contextOutOfDate) {
|
||||
NSException* exception = [NSException
|
||||
exceptionWithName:@"InvalidOperationException"
|
||||
reason:@"Attempt to access out-of-date context."
|
||||
userInfo:nil];
|
||||
@throw exception;
|
||||
}
|
||||
return self.appDelegate.contextBuffer;
|
||||
}
|
||||
|
||||
-(NSString *)getLimitedContextFrom:(id)sender at:(NSUInteger) len {
|
||||
if (![sender respondsToSelector:@selector(attributedSubstringFromRange:)])
|
||||
return nil;
|
||||
|
||||
NSUInteger start = 0;
|
||||
if (len > kMaxContext) {
|
||||
[self.appDelegate logDebugMessage:@"Limiting context to %lu characters", kMaxContext];
|
||||
start = len - kMaxContext;
|
||||
len = kMaxContext;
|
||||
}
|
||||
|
||||
NSString *preBuffer = [[sender attributedSubstringFromRange:NSMakeRange(start, len)] string];
|
||||
[self.appDelegate logDebugMessage:@"preBuffer = \"%@\"", preBuffer ? preBuffer : @"nil"];
|
||||
if (preBuffer.length)
|
||||
[self.appDelegate logDebugMessage:@"First character: '%x'", [preBuffer characterAtIndex:0]];
|
||||
else
|
||||
[self.appDelegate logDebugMessage:@"preBuffer has a length of 0"];
|
||||
|
||||
return preBuffer;
|
||||
}
|
||||
|
||||
// Return the pending buffer. If it is NIL create it.
|
||||
-(NSMutableString*)pendingBuffer;
|
||||
{
|
||||
if ( _pendingBuffer == nil ) {
|
||||
_pendingBuffer = [[NSMutableString alloc] init];
|
||||
}
|
||||
return _pendingBuffer;
|
||||
}
|
||||
|
||||
// Change the pending buffer.
|
||||
-(void)setPendingBuffer:(NSString*)string
|
||||
{
|
||||
NSMutableString* buffer = [self pendingBuffer];
|
||||
[buffer setString:string];
|
||||
}
|
||||
|
||||
- (BOOL) handleEventWithKeymanEngine:(NSEvent *)event in:(id) sender {
|
||||
[self.appDelegate logDebugMessage:@"handleEventWithKeymanEngine, event = %@", event];
|
||||
CoreKeyOutput *output = nil;
|
||||
|
|
@ -265,7 +158,7 @@ NSRange _previousSelRange;
|
|||
}
|
||||
|
||||
/**
|
||||
handleBackspace: handles generated for a subset of non-compliant apps (such as Adobe apps) that do
|
||||
handleBackspace: handles generated backspaces for a subset of non-compliant apps (such as Adobe apps) that do
|
||||
not support replacing text with the insertText API but also intercept events so that we do not see them in handleEvent.
|
||||
Other apps, such as Word, show the event both in the low
|
||||
level EventTap and in the normal IM handleEvent. Thus, we set a flag after
|
||||
|
|
@ -275,16 +168,14 @@ NSRange _previousSelRange;
|
|||
*/
|
||||
- (void)handleBackspace:(NSEvent *)event {
|
||||
[self.appDelegate logDebugMessage:@"KMInputMethodEventHandler handleBackspace, event = %@", event];
|
||||
NSLog(@" backspaceHandledLowLevel: %d, generatedBackspaceCount: %lu", _backspaceHandledLowLevel, (unsigned long)_generatedBackspaceCount);
|
||||
self.backspaceHandledLowLevel = NO;
|
||||
|
||||
|
||||
if (self.generatedBackspaceCount > 0) {
|
||||
self.generatedBackspaceCount--;
|
||||
self.backspaceHandledLowLevel = YES;
|
||||
self.lowLevelBackspaceCount++;
|
||||
|
||||
// if we just encountered the last backspace, then send event to insert queued text
|
||||
if (self.generatedBackspaceCount == 0) {
|
||||
[self performSelector:@selector(triggerInsertQueuedText:) withObject:event afterDelay:kQueuedTextEventDelayInSeconds];
|
||||
if ((self.generatedBackspaceCount == 0) && (self.queuedText.length > 0)) {
|
||||
[self performSelector:@selector(triggerInsertQueuedText:) withObject:event afterDelay:kQueuedTextEventDelayInSeconds];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -294,28 +185,6 @@ NSRange _previousSelRange;
|
|||
[self.keySender sendKeymanKeyCodeForEvent:event];
|
||||
}
|
||||
|
||||
- (void)initiatePendingBufferProcessing:(id)sender {
|
||||
[self postKeyPressToFrontProcess:kProcessPendingBuffer from:NULL];
|
||||
}
|
||||
|
||||
- (void)postKeyPressToFrontProcess:(CGKeyCode)code from:(CGEventSourceRef) source {
|
||||
ProcessSerialNumber psn;
|
||||
GetFrontProcess(&psn);
|
||||
|
||||
if ([self.appDelegate debugMode]) {
|
||||
if (code == kProcessPendingBuffer) {
|
||||
NSLog(@"Posting code to tell Keyman to process characters in pending buffer.");
|
||||
}
|
||||
else {
|
||||
NSLog(@"Posting a keypress (down/up) to the 'front process' for the %hu key.", code);
|
||||
}
|
||||
}
|
||||
|
||||
[self.appDelegate postKeyboardEventWithSource:source code:code postCallback:^(CGEventRef eventToPost) {
|
||||
CGEventPostToPSN((ProcessSerialNumberPtr)&psn, eventToPost);
|
||||
}];
|
||||
}
|
||||
|
||||
//MARK: Core-related key processing
|
||||
|
||||
- (void)checkTextApiCompliance:(id)client {
|
||||
|
|
@ -358,16 +227,17 @@ NSRange _previousSelRange;
|
|||
// indicates that our generated backspace event(s) are consumed
|
||||
// and we can insert text that followed the backspace(s)
|
||||
if (event.keyCode == kKeymanEventKeyCode) {
|
||||
[self.appDelegate logDebugMessage:@"handleEvent, handling kKeymanEventKeyCode"];
|
||||
[self.appDelegate logDebugMessage:@"handleEvent, handling kKeymanEventKeyCode"];
|
||||
[self insertQueuedText: event client:sender];
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
// for some apps, handleEvent will not be called for the generated backspace
|
||||
// but if it is, we need to let it pass through rather than process again in core
|
||||
if((event.keyCode == kVK_Delete) && (self.backspaceHandledLowLevel)) {
|
||||
if((event.keyCode == kVK_Delete) && (self.lowLevelBackspaceCount > 0)) {
|
||||
[self.appDelegate logDebugMessage:@"handleEvent, allowing generated backspace to pass through"];
|
||||
self.backspaceHandledLowLevel = NO;
|
||||
self.lowLevelBackspaceCount--;
|
||||
|
||||
// return NO to pass through to client app
|
||||
return NO;
|
||||
}
|
||||
|
|
@ -482,18 +352,35 @@ NSRange _previousSelRange;
|
|||
} else if (output.isDeleteOnlyScenario) {
|
||||
if ((event.keyCode == kVK_Delete) && output.codePointsToDeleteBeforeInsert == 1) {
|
||||
// let the delete pass through in the original event rather than sending a new delete
|
||||
[self.appDelegate logDebugMessage:@"KXMInputMethodHandler applyOutputToTextInputClient, delete only scenario"];
|
||||
[self.appDelegate logDebugMessage:@"KXMInputMethodHandler applyOutputToTextInputClient, delete only scenario with passthrough"];
|
||||
handledEvent = NO;
|
||||
} else {
|
||||
[self.appDelegate logDebugMessage:@"KXMInputMethodHandler applyOutputToTextInputClient, delete only scenario"];
|
||||
[self sendEvents:event forOutput:output];
|
||||
}
|
||||
} else if (output.isDeleteAndInsertScenario) {
|
||||
// TODO: fix issue #10246
|
||||
/*
|
||||
// needs further testing to fix backspace bug in google docs (without breaking other apps)
|
||||
// assume that all non-compliant apps which require backspaces apply an extra backspace if the original event is a backspace
|
||||
if ((self.apiCompliance.mustBackspaceUsingEvents) && (event.keyCode == kVK_Delete)) {
|
||||
output.codePointsToDeleteBeforeInsert--;
|
||||
os_log_with_type(keymanLog, OS_LOG_TYPE_INFO, "isDeleteAndInsertScenario, after delete pressed subtracting one backspace to reach %d and insert text '%{public}@'", output.codePointsToDeleteBeforeInsert, output.textToInsert);
|
||||
if (output.codePointsToDeleteBeforeInsert == 0) {
|
||||
// no backspace events needed
|
||||
[self insertAndReplaceTextForOutput:output client:client];
|
||||
} else {
|
||||
// still need at least one backspace event
|
||||
[self sendEvents:event forOutput:output];
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if (self.apiCompliance.mustBackspaceUsingEvents) {
|
||||
[self.appDelegate logDebugMessage:@"KXMInputMethodHandler applyOutputToTextInputClient, delete and insert scenario with events"];
|
||||
[self sendEvents:event forOutput:output];
|
||||
} else {
|
||||
[self.appDelegate logDebugMessage:@"KXMInputMethodHandler applyOutputToTextInputClient, delete and insert scenario with insert API"];
|
||||
[self.appDelegate logDebugMessage:@"KXMInputMethodHandler applyOutputToTextInputClient, delete and insert scenario with insert API"];
|
||||
// directly insert text and handle backspaces by using replace
|
||||
[self insertAndReplaceTextForOutput:output client:client];
|
||||
}
|
||||
|
|
@ -585,15 +472,16 @@ NSRange _previousSelRange;
|
|||
}
|
||||
|
||||
-(void)sendEvents:(NSEvent *)event forOutput:(CoreKeyOutput*)output {
|
||||
[self.appDelegate logDebugMessage:@"sendEvents called"];
|
||||
|
||||
[self.appDelegate logDebugMessage:@"sendEvents called, output = %@", output];
|
||||
|
||||
_sourceForGeneratedEvent = CGEventCreateSourceFromEvent([event CGEvent]);
|
||||
|
||||
self.generatedBackspaceCount = output.codePointsToDeleteBeforeInsert;
|
||||
|
||||
|
||||
if (output.hasCodePointsToDelete) {
|
||||
for (int i = 0; i < output.codePointsToDeleteBeforeInsert; i++) {
|
||||
[self.appDelegate logDebugMessage:@"sendEvents queueing backspace"];
|
||||
[self.appDelegate logDebugMessage:@"sendEvents sending backspace key event"];
|
||||
[self.keySender sendBackspaceforEventSource:_sourceForGeneratedEvent];
|
||||
}
|
||||
}
|
||||
|
|
@ -611,7 +499,7 @@ NSRange _previousSelRange;
|
|||
[self insertAndReplaceText:self.queuedText deleteCount:0 client:client];
|
||||
self.queuedText = nil;
|
||||
} else {
|
||||
[self.appDelegate logDebugMessage:@"handleQueuedText called but no text to insert"];
|
||||
[self.appDelegate logDebugMessage:@"insertQueuedText called but no text to insert"];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,56 +0,0 @@
|
|||
//
|
||||
// KMInputMethodEventHandlerProtected.h
|
||||
// Keyman4MacIM
|
||||
//
|
||||
// Created by tom on 1/5/18.
|
||||
// Copyright © 2018 SIL International. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef KMInputMethodEventHandlerProtected_h
|
||||
#define KMInputMethodEventHandlerProtected_h
|
||||
|
||||
typedef NS_ENUM(NSInteger, ClientCapability) {
|
||||
Unknown,
|
||||
Yes,
|
||||
No,
|
||||
Unreliable
|
||||
};
|
||||
|
||||
@interface KMInputMethodEventHandler ()
|
||||
//@property (nonatomic) NSMutableDictionary *kbData;
|
||||
//@property (nonatomic) NSDictionary *kmModes;
|
||||
@property (assign) BOOL willDeleteNullChar;
|
||||
@property (assign) BOOL contextOutOfDate;
|
||||
// This flag indicates which mode is being used for replacing already typed text when composing characters
|
||||
// (i.e., when not using deadkeys). Some apps (and some javascript-based websites, such as Google Docs) do
|
||||
// not properly deal with calls to insertText calls that replace a range of characters. So for these "legacy"
|
||||
// apps, we post one or more deletes (i.e., backspace), followed by a special code that tells us we're now
|
||||
// ready to insert the composed text.
|
||||
@property BOOL legacyMode;
|
||||
// Some clients (e.g. Chrome) handle the mouse down events before we get a crack at them. For such apps that
|
||||
// are able to report their current selection location (LibreOffice can't even do that!), we can do some
|
||||
// checking at the start of the event processing to see if we're probably still in the same place where we
|
||||
// left off previously.
|
||||
@property (assign) BOOL clientSelectionCanChangeUnexpectedly; // REVIEW: Maybe we can get notification from these clients by handling mouseDownOnCharacterIndex.
|
||||
@property (assign) ClientCapability clientCanProvideSelectionInfo;
|
||||
|
||||
@property id senderForDeleteBack;
|
||||
@property BOOL ignoreNextDeleteBackHighLevel;
|
||||
|
||||
- (instancetype)initWithLegacyMode:(BOOL)legacy clientSelectionCanChangeUnexpectedly:(BOOL) flagClientSelectionCanChangeUnexpectedly;
|
||||
- (void)handleCommand:(NSEvent *)event;
|
||||
// This just sets the legacyMode property to true and spits out a debug message to that effect.
|
||||
- (void)switchToLegacyMode;
|
||||
- (void)checkContextIn:(id)client;
|
||||
- (void)replaceExistingSelectionIn:(id)client with:(NSString *) text;
|
||||
- (void)insertPendingBufferTextIn:(id)client;
|
||||
- (KMInputMethodAppDelegate *)appDelegate;
|
||||
- (NSMutableString *)contextBuffer;
|
||||
- (NSString *)getLimitedContextFrom:(id)sender at:(NSUInteger) len;
|
||||
// Return the pending buffer. If it is NIL create it.
|
||||
-(NSMutableString*)pendingBuffer;
|
||||
-(void)setPendingBuffer:(NSString*)string;
|
||||
- (void)initiatePendingBufferProcessing:(id)sender;
|
||||
@end
|
||||
|
||||
#endif /* KMInputMethodEventHandlerProtected_h */
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
//
|
||||
// KMInputMethodSafariClientEventHandler.h
|
||||
// Keyman4MacIM
|
||||
// This class is used for the Safari browser to get specific behavior needed to improve behavior for some sites.
|
||||
//
|
||||
// Created by tom on 1/9/18.
|
||||
// Copyright © 2018 SIL International. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef KMInputMethodSafariClientEventHandler_h
|
||||
#define KMInputMethodSafariClientEventHandler_h
|
||||
#import "KMInputMethodBrowserClientEventHandler.h"
|
||||
|
||||
@interface KMInputMethodSafariClientEventHandler : KMInputMethodBrowserClientEventHandler
|
||||
@end
|
||||
|
||||
#endif /* KMInputMethodSafariClientEventHandler_h */
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
//
|
||||
// KMInputMethodSafariClientEventHandler.m
|
||||
// Keyman
|
||||
//
|
||||
// Created by tom on 1/9/18.
|
||||
// Copyright © 2018 SIL International. All rights reserved.
|
||||
//
|
||||
|
||||
#import "KMInputMethodSafariClientEventHandler.h"
|
||||
#import "KMInputMethodEventHandlerProtected.h"
|
||||
#import "KMInputMethodBrowserClientEventHandlerProtected.h"
|
||||
|
||||
@implementation KMInputMethodSafariClientEventHandler
|
||||
|
||||
// Because Google Docs can't report its context, if we detect that we are in Google Docs, we change to legacy mode.
|
||||
//BOOL _explicitlyDeleteExistingSelectionBeforeInserting = NO;
|
||||
BOOL _preserveContextForNextCmdA;
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super initWithLegacyMode:NO clientSelectionCanChangeUnexpectedly:NO];
|
||||
if (self) {
|
||||
_preserveContextForNextCmdA = NO;
|
||||
//_explicitlyDeleteExistingSelectionBeforeInserting = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)handleCommand:(NSEvent *)event {
|
||||
// If Safari issued a Cmd-A to remove the selection, we know what the
|
||||
// context should be, so keep it.
|
||||
if ([event.characters isEqualTo:@"a"] && _preserveContextForNextCmdA)
|
||||
_preserveContextForNextCmdA = NO;
|
||||
else
|
||||
[super handleCommand:event];
|
||||
}
|
||||
|
||||
- (void)setInSiteThatDoesNotGiveContext {
|
||||
[self switchToLegacyMode];
|
||||
}
|
||||
|
||||
- (void)replaceExistingSelectionIn:(id)client with:(NSString *) text {
|
||||
// The following commented out code is the code that would make it possible to type over an
|
||||
// existing selection in Word in Safari, but I don't know of a way to distinguish that case
|
||||
// from other contexts in Safari (wherein this code inserts an extra leading space).
|
||||
// if (_explicitlyDeleteExistingSelectionBeforeInserting) {
|
||||
// NSUInteger selLength = [sender selectedRange].length;
|
||||
// if (selLength > 0 && selLength != NSNotFound) {
|
||||
// if ([self.AppDelegate debugMode]) {
|
||||
// NSLog(@"Attempting to delete existing selection of length = %lu by replacing it with a space (which should not actually appear in the text).", selLength);
|
||||
// }
|
||||
//
|
||||
// [sender insertText:@" " replacementRange:NSMakeRange(NSNotFound, NSNotFound)];
|
||||
//
|
||||
// if ([self.AppDelegate debugMode])
|
||||
// NSLog(@"Re-posting original (unhandled) code: %d", (int)_keyCodeOfOriginalEvent);
|
||||
//
|
||||
// CGEventSourceRef sourceFromEvent = CGEventCreateSourceFromEvent([event CGEvent]);
|
||||
// [self postKeyPressToFrontProcess:event.keyCode from:sourceFromEvent];
|
||||
// CFRelease(sourceFromEvent);
|
||||
// return YES;
|
||||
// }
|
||||
// }
|
||||
[super replaceExistingSelectionIn:client with:text];
|
||||
|
||||
// In Google Docs (i.e., "legacy mode" when there is an existing selection, the inserted characters
|
||||
// stay selected. The following clears the selection.
|
||||
if (self.legacyMode) {
|
||||
if ([self.appDelegate debugMode])
|
||||
NSLog(@"Sending Command-Shift-A to clear selection in Google Docs");
|
||||
_preserveContextForNextCmdA = YES;
|
||||
ProcessSerialNumber psn;
|
||||
GetFrontProcess(&psn);
|
||||
|
||||
CGEventRef event = CGEventCreateKeyboardEvent(NULL, kVK_ANSI_A, true);
|
||||
//set shift and command keys down for above event
|
||||
CGEventSetFlags(event, kCGEventFlagMaskShift | kCGEventFlagMaskCommand);
|
||||
CGEventPostToPSN(&psn, event);
|
||||
CFRelease(event);
|
||||
|
||||
event = CGEventCreateKeyboardEvent(NULL, kVK_ANSI_A, false);
|
||||
CGEventSetFlags(event, kCGEventFlagMaskShift | kCGEventFlagMaskCommand);
|
||||
CGEventPostToPSN(&psn, event);
|
||||
CFRelease(event);
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
|
@ -29,33 +29,6 @@ const CGKeyCode kKeymanEventKeyCode = 0xFF;
|
|||
return self;
|
||||
}
|
||||
|
||||
- (void)sendKeyDown:(NSUInteger)keyCode forSourceEvent:(NSEvent *)event includeKeyUp:(BOOL)includeKeyUpEvent {
|
||||
// Returns the frontmost app, which is the app that receives key events.
|
||||
NSRunningApplication *app = NSWorkspace.sharedWorkspace.frontmostApplication;
|
||||
pid_t processId = app.processIdentifier;
|
||||
NSString *bundleId = app.bundleIdentifier;
|
||||
|
||||
[self.appDelegate logDebugMessage:@"sendKeyDown keyCode %lu to app %@ with pid %d", (unsigned long)keyCode, bundleId, processId];
|
||||
|
||||
if (keyCode < 0x100) {
|
||||
CGEventRef cgevent = [event CGEvent];
|
||||
|
||||
// use source from event to generate new event
|
||||
CGEventSourceRef source = CGEventCreateSourceFromEvent(cgevent);
|
||||
CGEventRef keyDownEvent = CGEventCreateKeyboardEvent(source, (CGKeyCode)keyCode, true);
|
||||
|
||||
// TODO: add version check
|
||||
CGEventPostToPid(processId, keyDownEvent);
|
||||
CFRelease(keyDownEvent);
|
||||
|
||||
if (includeKeyUpEvent) {
|
||||
CGEventRef keyUpEvent = CGEventCreateKeyboardEvent(source, (CGKeyCode)keyCode, false);
|
||||
CGEventPostToPid(processId, keyUpEvent);
|
||||
CFRelease(keyUpEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sendBackspaceforEventSource:(CGEventSourceRef)eventSource {
|
||||
[self.appDelegate logDebugMessage:@"KeySender sendBackspaceforEventSource"];
|
||||
|
||||
|
|
@ -90,23 +63,20 @@ const CGKeyCode kKeymanEventKeyCode = 0xFF;
|
|||
- (void)sendKeymanKeyCodeForEvent:(NSEvent *)event {
|
||||
[self.appDelegate logDebugMessage:@"KeySender sendKeymanKeyCodeForEvent"];
|
||||
|
||||
[self sendKeyDown:kKeymanEventKeyCode forSourceEvent:event includeKeyUp:NO];
|
||||
|
||||
ProcessSerialNumber psn;
|
||||
|
||||
// Returns the frontmost app, which is the app that receives key events.
|
||||
NSRunningApplication *app = NSWorkspace.sharedWorkspace.frontmostApplication;
|
||||
pid_t processId = app.processIdentifier;
|
||||
NSString *bundleId = app.bundleIdentifier;
|
||||
GetProcessForPID(processId, &psn);
|
||||
|
||||
[self.appDelegate logDebugMessage:@"sendKeymanKeyCodeForEvent keyCode %lu to app %@ with pid %d", (unsigned long)kKeymanEventKeyCode, bundleId, processId];
|
||||
|
||||
CGEventRef cgevent = [event CGEvent];
|
||||
|
||||
// use source from event to generate new event
|
||||
CGEventSourceRef source = CGEventCreateSourceFromEvent(cgevent);
|
||||
CGEventRef keyDownEvent = CGEventCreateKeyboardEvent(source, kKeymanEventKeyCode, true);
|
||||
|
||||
// TODO: add version check
|
||||
CGEventPostToPid(processId, keyDownEvent);
|
||||
// use nil as source, as this generated event is not directly tied to the originating event
|
||||
CGEventRef keyDownEvent = CGEventCreateKeyboardEvent(nil, kKeymanEventKeyCode, true);
|
||||
|
||||
CGEventPostToPSN(&psn, keyDownEvent);
|
||||
CFRelease(keyDownEvent);
|
||||
|
||||
// this is not a real keycode, so we do not need a key up event
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
|
||||
#import <XCTest/XCTest.h>
|
||||
#import "KMInputMethodEventHandler.h"
|
||||
#import "KMInputMethodEventHandlerProtected.h"
|
||||
#import "LegacyTestClient.h"
|
||||
#import "AppleCompliantTestClient.h"
|
||||
#import "TextApiCompliance.h"
|
||||
|
|
|
|||
|
|
@ -1,58 +0,0 @@
|
|||
//
|
||||
// KMInputMethodBrowserClientEventHandlerTests.m
|
||||
// KeymanTests
|
||||
//
|
||||
// Created by tom on 1/25/18.
|
||||
// Copyright © 2018 SIL International. All rights reserved.
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
#import "TestAppDelegate.h"
|
||||
#import "KMInputMethodBrowserClientEventHandler.h"
|
||||
#import "KMInputMethodEventHandlerProtected.h"
|
||||
#import <OCMock/OCMock.h>
|
||||
|
||||
@interface KeymanTests : XCTestCase
|
||||
@property (nonatomic, strong) TestAppDelegate * delegate;
|
||||
@end
|
||||
|
||||
@implementation KeymanTests
|
||||
KMInputMethodBrowserClientEventHandler * _im;
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
_delegate = [[TestAppDelegate alloc] init];
|
||||
[[NSApplication sharedApplication] setDelegate:_delegate];
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
NSString *path = [[NSBundle bundleForClass:[KeymanTests class]] pathForResource:@"SimpleTest.kmx" ofType:nil];
|
||||
KMXFile *kmxFile = [[KMXFile alloc] initWithFilePath:path];
|
||||
[_delegate setKmx:kmxFile];
|
||||
_im = [[KMInputMethodBrowserClientEventHandler alloc] init];
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)skip_TestcheckContextIn_EmptyContextExpected_NoChangeToclientSelectionCanChangeUnexpectedly {
|
||||
// If the expected context is empty, no attempt should be made to check for matching context from
|
||||
// client, so even though the client would also return an empty string, calling checkContextIn
|
||||
// repeatedly has no effect (i.e., it does not set clientSelectionCanChangeUnexpectedly to false).
|
||||
// Therefore a subsequent key down event *should* result in a call to the client to inquire about
|
||||
// the current selection.
|
||||
id client = OCMStrictProtocolMock(@protocol(IMKTextInput));
|
||||
OCMStub([client selectedRange]).andReturn(NSMakeRange(0, 0));
|
||||
NSAttributedString *attrSubstring = [[NSAttributedString alloc] init];
|
||||
OCMStub([client attributedSubstringFromRange:(NSMakeRange(0, 0))]).andReturn(attrSubstring);
|
||||
OCMExpect([client insertText:@"a" replacementRange:NSMakeRange(NSNotFound, NSNotFound)]);
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
[_im checkContextIn:client];
|
||||
}
|
||||
NSEvent *event = [_delegate keyStrokeEventForCharacter: @"a" keyCode:kVK_ANSI_A];
|
||||
[_im handleEvent:event client:client];
|
||||
[client verify];
|
||||
}
|
||||
|
||||
@end
|
||||
28
resources/build/test/debug-deps/child/build.sh
Executable file
28
resources/build/test/debug-deps/child/build.sh
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env bash
|
||||
## START STANDARD BUILD SCRIPT INCLUDE
|
||||
# adjust relative paths as necessary
|
||||
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
||||
. "${THIS_SCRIPT%/*}/../../../../../resources/build/build-utils.sh"
|
||||
# END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
builder_describe \
|
||||
"Tests child builds debug flag" \
|
||||
@dep build
|
||||
|
||||
function do_build() {
|
||||
# Test the debug flag
|
||||
builder_is_debug_build || builder_die "FAIL: child: expecting builder_is_debug_build to be true"
|
||||
echo "PASS: child: builder_is_debug_build is true"
|
||||
|
||||
builder_has_option --debug || builder_die "FAIL: child: expecting builder_has_option --debug to be true"
|
||||
echo "PASS: child: builder_has_option --debug is true"
|
||||
}
|
||||
|
||||
builder_parse "$@"
|
||||
|
||||
# Sanity Check: verify that we are running as a child (and not a dep) build
|
||||
! builder_is_dep_build || builder_die "FAIL: child: builder_is_dep_build should be false"
|
||||
builder_is_child_build || builder_die "FAIL: child: builder_is_child_build should be true"
|
||||
|
||||
builder_run_action build do_build
|
||||
|
||||
27
resources/build/test/debug-deps/child/dep/build.sh
Executable file
27
resources/build/test/debug-deps/child/dep/build.sh
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
#!/usr/bin/env bash
|
||||
## START STANDARD BUILD SCRIPT INCLUDE
|
||||
# adjust relative paths as necessary
|
||||
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
||||
. "${THIS_SCRIPT%/*}/../../../../../../resources/build/build-utils.sh"
|
||||
# END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
builder_describe \
|
||||
"Tests child/dependency builds debug flag" \
|
||||
configure build
|
||||
|
||||
function do_build() {
|
||||
# Test the debug flag
|
||||
builder_is_debug_build || builder_die "FAIL: child/dep: expecting builder_is_debug_build to be true"
|
||||
echo "PASS: child/dep: builder_is_debug_build is true"
|
||||
|
||||
builder_has_option --debug || builder_die "FAIL: child/dep: expecting builder_has_option --debug to be true"
|
||||
echo "PASS: child/dep: builder_has_option --debug is true"
|
||||
}
|
||||
|
||||
builder_parse "$@"
|
||||
|
||||
# Sanity Check: verify that we are running as a dep (and not a child) build
|
||||
builder_is_dep_build || builder_die "FAIL: child/dep: builder_is_dep_build should be true"
|
||||
! builder_is_child_build || builder_die "FAIL: child/dep: builder_is_child_build should be false"
|
||||
|
||||
builder_run_action build do_build
|
||||
27
resources/build/test/debug-deps/dep/build.sh
Executable file
27
resources/build/test/debug-deps/dep/build.sh
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
#!/usr/bin/env bash
|
||||
## START STANDARD BUILD SCRIPT INCLUDE
|
||||
# adjust relative paths as necessary
|
||||
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
||||
. "${THIS_SCRIPT%/*}/../../../../../resources/build/build-utils.sh"
|
||||
# END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
builder_describe \
|
||||
"Tests dependency builds debug flag" \
|
||||
configure build
|
||||
|
||||
function do_build() {
|
||||
# Test the debug flag
|
||||
builder_is_debug_build || builder_die "FAIL: dep: expecting builder_is_debug_build to be true"
|
||||
echo "PASS: dep: builder_is_debug_build is true"
|
||||
|
||||
builder_has_option --debug || builder_die "FAIL: dep: expecting builder_has_option --debug to be true"
|
||||
echo "PASS: dep: builder_has_option --debug is true"
|
||||
}
|
||||
|
||||
builder_parse "$@"
|
||||
|
||||
# Sanity Check: verify that we are running as a dep (and not a child) build
|
||||
builder_is_dep_build || builder_die "FAIL: dep: builder_is_dep_build should be true"
|
||||
! builder_is_child_build || builder_die "FAIL: dep: builder_is_child_build should be false"
|
||||
|
||||
builder_run_action build do_build
|
||||
22
resources/build/test/debug-deps/test.sh
Executable file
22
resources/build/test/debug-deps/test.sh
Executable file
|
|
@ -0,0 +1,22 @@
|
|||
#!/usr/bin/env bash
|
||||
## START STANDARD BUILD SCRIPT INCLUDE
|
||||
# adjust relative paths as necessary
|
||||
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
||||
. "${THIS_SCRIPT%/*}/../../../../resources/build/build-utils.sh"
|
||||
# END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
builder_describe \
|
||||
"Tests child and dependency builds --debug flag" \
|
||||
@dep :child build
|
||||
|
||||
function do_build() {
|
||||
builder_is_debug_build || builder_die "FAIL: parent: expecting builder_is_debug_build to be true"
|
||||
echo "PASS: parent: builder_is_debug_build is true"
|
||||
|
||||
builder_has_option --debug || builder_die "FAIL: parent: expecting builder_has_option --debug to be true"
|
||||
echo "PASS: parent: builder_has_option --debug is true"
|
||||
}
|
||||
|
||||
builder_parse build:child -d
|
||||
builder_run_action build do_build
|
||||
builder_run_child_actions build
|
||||
|
|
@ -174,6 +174,7 @@ echo -e "${COLOR_BLUE}## Running dependency tests${COLOR_RESET}"
|
|||
"$THIS_SCRIPT_PATH/builder-deps.test.sh"
|
||||
"$THIS_SCRIPT_PATH/dependencies/test.sh"
|
||||
"$THIS_SCRIPT_PATH/trees/test.sh"
|
||||
"$THIS_SCRIPT_PATH/debug-deps/test.sh"
|
||||
|
||||
echo -e "${COLOR_BLUE}## End external tests${COLOR_RESET}"
|
||||
echo
|
||||
|
|
|
|||
|
|
@ -60,18 +60,24 @@ export class ContextHost extends Mock {
|
|||
|
||||
updateContext(text: string, selStart: number, selEnd: number): boolean {
|
||||
let shouldResetContext = false;
|
||||
let tempMock = new Mock(text, selStart ?? text._kmwLength(), selEnd ?? text._kmwLength());
|
||||
let newLeft = tempMock.getTextBeforeCaret();
|
||||
let oldLeft = this.getTextBeforeCaret();
|
||||
|
||||
if(text != this.text) {
|
||||
let tempMock = new Mock(text, selStart ?? text._kmwLength(), selEnd ?? text._kmwLength());
|
||||
|
||||
let newLeft = tempMock.getTextBeforeCaret();
|
||||
let oldLeft = this.getTextBeforeCaret();
|
||||
|
||||
let unexpectedBeforeCharCount = findCommonSubstringEndIndex(newLeft, oldLeft, true) + 1;
|
||||
shouldResetContext = !!unexpectedBeforeCharCount;
|
||||
}
|
||||
|
||||
if(shouldResetContext) {
|
||||
this.text = text;
|
||||
this.selStart = selStart;
|
||||
this.selEnd = selEnd;
|
||||
} else {
|
||||
// Transform selection coordinates to their location within the longform context window.
|
||||
let delta = oldLeft._kmwLength() - newLeft._kmwLength();
|
||||
this.selStart = selStart - delta;
|
||||
this.selEnd = selEnd - delta;
|
||||
}
|
||||
|
||||
if(selStart === undefined || selEnd === undefined) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue