Merge pull request #259 from zonble/input_fsm
This commit is contained in:
commit
74cd93f6ca
|
@ -49,15 +49,23 @@
|
||||||
D427F7B6279086F6004A2160 /* InputSourceHelper in Frameworks */ = {isa = PBXBuildFile; productRef = D427F7B5279086F6004A2160 /* InputSourceHelper */; };
|
D427F7B6279086F6004A2160 /* InputSourceHelper in Frameworks */ = {isa = PBXBuildFile; productRef = D427F7B5279086F6004A2160 /* InputSourceHelper */; };
|
||||||
D427F7C127908EFC004A2160 /* OpenCCBridge in Frameworks */ = {isa = PBXBuildFile; productRef = D427F7C027908EFC004A2160 /* OpenCCBridge */; };
|
D427F7C127908EFC004A2160 /* OpenCCBridge in Frameworks */ = {isa = PBXBuildFile; productRef = D427F7C027908EFC004A2160 /* OpenCCBridge */; };
|
||||||
D44FB74527915565003C80A6 /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = D44FB74427915555003C80A6 /* Preferences.swift */; };
|
D44FB74527915565003C80A6 /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = D44FB74427915555003C80A6 /* Preferences.swift */; };
|
||||||
D44FB74727919D35003C80A6 /* EmacsKeyHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D44FB74627919C83003C80A6 /* EmacsKeyHelper.swift */; };
|
|
||||||
D44FB74A2791B829003C80A6 /* VXHanConvert in Frameworks */ = {isa = PBXBuildFile; productRef = D44FB7492791B829003C80A6 /* VXHanConvert */; };
|
D44FB74A2791B829003C80A6 /* VXHanConvert in Frameworks */ = {isa = PBXBuildFile; productRef = D44FB7492791B829003C80A6 /* VXHanConvert */; };
|
||||||
D44FB74D2792189A003C80A6 /* PhraseReplacementMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D44FB74B2792189A003C80A6 /* PhraseReplacementMap.cpp */; };
|
D44FB74D2792189A003C80A6 /* PhraseReplacementMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D44FB74B2792189A003C80A6 /* PhraseReplacementMap.cpp */; };
|
||||||
|
D456576E279E4F7B00DF6BC9 /* KeyHandlerInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = D456576D279E4F7B00DF6BC9 /* KeyHandlerInput.swift */; };
|
||||||
|
D461B792279DAC010070E734 /* InputState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D461B791279DAC010070E734 /* InputState.swift */; };
|
||||||
D47B92C027972AD100458394 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47B92BF27972AC800458394 /* main.swift */; };
|
D47B92C027972AD100458394 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47B92BF27972AC800458394 /* main.swift */; };
|
||||||
D47F7DCE278BFB57002F9DD7 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DCD278BFB57002F9DD7 /* PreferencesWindowController.swift */; };
|
D47F7DCE278BFB57002F9DD7 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DCD278BFB57002F9DD7 /* PreferencesWindowController.swift */; };
|
||||||
D47F7DD0278C0897002F9DD7 /* NonModalAlertWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DCF278C0897002F9DD7 /* NonModalAlertWindowController.swift */; };
|
D47F7DD0278C0897002F9DD7 /* NonModalAlertWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DCF278C0897002F9DD7 /* NonModalAlertWindowController.swift */; };
|
||||||
D47F7DD3278C1263002F9DD7 /* UserOverrideModel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DD2278C1263002F9DD7 /* UserOverrideModel.cpp */; };
|
D47F7DD3278C1263002F9DD7 /* UserOverrideModel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DD2278C1263002F9DD7 /* UserOverrideModel.cpp */; };
|
||||||
D485D3B92796A8A000657FF3 /* PreferencesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D485D3B82796A8A000657FF3 /* PreferencesTests.swift */; };
|
D485D3B92796A8A000657FF3 /* PreferencesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D485D3B82796A8A000657FF3 /* PreferencesTests.swift */; };
|
||||||
D485D3C02796CE3200657FF3 /* VersionUpdateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D485D3BF2796CE3200657FF3 /* VersionUpdateTests.swift */; };
|
D485D3C02796CE3200657FF3 /* VersionUpdateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D485D3BF2796CE3200657FF3 /* VersionUpdateTests.swift */; };
|
||||||
|
D4E569DC27A34D0E00AC2CEF /* KeyHandler.mm in Sources */ = {isa = PBXBuildFile; fileRef = D4E569DB27A34CC100AC2CEF /* KeyHandler.mm */; };
|
||||||
|
D4E569DF27A40F1400AC2CEF /* KeyHandlerBopomofoTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = D4E569DE27A40F1400AC2CEF /* KeyHandlerBopomofoTests.mm */; };
|
||||||
|
D4E569E027A4123200AC2CEF /* KeyHandlerInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = D456576D279E4F7B00DF6BC9 /* KeyHandlerInput.swift */; };
|
||||||
|
D4E569E127A4128300AC2CEF /* InputState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D461B791279DAC010070E734 /* InputState.swift */; };
|
||||||
|
D4E569E227A412E700AC2CEF /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = D44FB74427915555003C80A6 /* Preferences.swift */; };
|
||||||
|
D4E569E427A414CB00AC2CEF /* data-plain-bpmf.txt in Resources */ = {isa = PBXBuildFile; fileRef = 6AD7CBC715FE555000691B5B /* data-plain-bpmf.txt */; };
|
||||||
|
D4E569E527A414CB00AC2CEF /* data.txt in Resources */ = {isa = PBXBuildFile; fileRef = 6A38BBF615FC117A00A8A51F /* data.txt */; };
|
||||||
D4F0BBDF279AF1AF0071253C /* ArchiveUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4F0BBDE279AF1AF0071253C /* ArchiveUtil.swift */; };
|
D4F0BBDF279AF1AF0071253C /* ArchiveUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4F0BBDE279AF1AF0071253C /* ArchiveUtil.swift */; };
|
||||||
D4F0BBE1279AF8B30071253C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4F0BBE0279AF8B30071253C /* AppDelegate.swift */; };
|
D4F0BBE1279AF8B30071253C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4F0BBE0279AF8B30071253C /* AppDelegate.swift */; };
|
||||||
D4F0BBE4279B08900071253C /* BundleTranslocate.m in Sources */ = {isa = PBXBuildFile; fileRef = D4F0BBE3279B08900071253C /* BundleTranslocate.m */; };
|
D4F0BBE4279B08900071253C /* BundleTranslocate.m in Sources */ = {isa = PBXBuildFile; fileRef = D4F0BBE3279B08900071253C /* BundleTranslocate.m */; };
|
||||||
|
@ -200,10 +208,11 @@
|
||||||
D427F7B2279086B5004A2160 /* InputSourceHelper */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = InputSourceHelper; path = Packages/InputSourceHelper; sourceTree = "<group>"; };
|
D427F7B2279086B5004A2160 /* InputSourceHelper */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = InputSourceHelper; path = Packages/InputSourceHelper; sourceTree = "<group>"; };
|
||||||
D427F7BF27908EAC004A2160 /* OpenCCBridge */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = OpenCCBridge; path = Packages/OpenCCBridge; sourceTree = "<group>"; };
|
D427F7BF27908EAC004A2160 /* OpenCCBridge */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = OpenCCBridge; path = Packages/OpenCCBridge; sourceTree = "<group>"; };
|
||||||
D44FB74427915555003C80A6 /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
|
D44FB74427915555003C80A6 /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
|
||||||
D44FB74627919C83003C80A6 /* EmacsKeyHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmacsKeyHelper.swift; sourceTree = "<group>"; };
|
|
||||||
D44FB7482791B346003C80A6 /* VXHanConvert */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = VXHanConvert; path = Packages/VXHanConvert; sourceTree = "<group>"; };
|
D44FB7482791B346003C80A6 /* VXHanConvert */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = VXHanConvert; path = Packages/VXHanConvert; sourceTree = "<group>"; };
|
||||||
D44FB74B2792189A003C80A6 /* PhraseReplacementMap.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = PhraseReplacementMap.cpp; sourceTree = "<group>"; };
|
D44FB74B2792189A003C80A6 /* PhraseReplacementMap.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = PhraseReplacementMap.cpp; sourceTree = "<group>"; };
|
||||||
D44FB74C2792189A003C80A6 /* PhraseReplacementMap.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PhraseReplacementMap.h; sourceTree = "<group>"; };
|
D44FB74C2792189A003C80A6 /* PhraseReplacementMap.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PhraseReplacementMap.h; sourceTree = "<group>"; };
|
||||||
|
D456576D279E4F7B00DF6BC9 /* KeyHandlerInput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyHandlerInput.swift; sourceTree = "<group>"; };
|
||||||
|
D461B791279DAC010070E734 /* InputState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputState.swift; sourceTree = "<group>"; };
|
||||||
D47B92BF27972AC800458394 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
D47B92BF27972AC800458394 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
||||||
D47F7DCD278BFB57002F9DD7 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = "<group>"; };
|
D47F7DCD278BFB57002F9DD7 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = "<group>"; };
|
||||||
D47F7DCF278C0897002F9DD7 /* NonModalAlertWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonModalAlertWindowController.swift; sourceTree = "<group>"; };
|
D47F7DCF278C0897002F9DD7 /* NonModalAlertWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonModalAlertWindowController.swift; sourceTree = "<group>"; };
|
||||||
|
@ -212,6 +221,10 @@
|
||||||
D485D3B62796A8A000657FF3 /* McBopomofoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = McBopomofoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
D485D3B62796A8A000657FF3 /* McBopomofoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = McBopomofoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
D485D3B82796A8A000657FF3 /* PreferencesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesTests.swift; sourceTree = "<group>"; };
|
D485D3B82796A8A000657FF3 /* PreferencesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesTests.swift; sourceTree = "<group>"; };
|
||||||
D485D3BF2796CE3200657FF3 /* VersionUpdateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionUpdateTests.swift; sourceTree = "<group>"; };
|
D485D3BF2796CE3200657FF3 /* VersionUpdateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionUpdateTests.swift; sourceTree = "<group>"; };
|
||||||
|
D4E569DA27A34CC100AC2CEF /* KeyHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyHandler.h; sourceTree = "<group>"; };
|
||||||
|
D4E569DB27A34CC100AC2CEF /* KeyHandler.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = KeyHandler.mm; sourceTree = "<group>"; };
|
||||||
|
D4E569DD27A40F1300AC2CEF /* McBopomofoTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "McBopomofoTests-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
|
D4E569DE27A40F1400AC2CEF /* KeyHandlerBopomofoTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = KeyHandlerBopomofoTests.mm; sourceTree = "<group>"; };
|
||||||
D4F0BBDE279AF1AF0071253C /* ArchiveUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArchiveUtil.swift; sourceTree = "<group>"; };
|
D4F0BBDE279AF1AF0071253C /* ArchiveUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArchiveUtil.swift; sourceTree = "<group>"; };
|
||||||
D4F0BBE0279AF8B30071253C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
D4F0BBE0279AF8B30071253C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
D4F0BBE2279B08900071253C /* BundleTranslocate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BundleTranslocate.h; sourceTree = "<group>"; };
|
D4F0BBE2279B08900071253C /* BundleTranslocate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BundleTranslocate.h; sourceTree = "<group>"; };
|
||||||
|
@ -299,12 +312,15 @@
|
||||||
6A0D4EC715FC0D6400ABF4B3 /* InputMethodController.mm */,
|
6A0D4EC715FC0D6400ABF4B3 /* InputMethodController.mm */,
|
||||||
D41355D6278D7409005E5CBD /* LanguageModelManager.h */,
|
D41355D6278D7409005E5CBD /* LanguageModelManager.h */,
|
||||||
D41355D7278D7409005E5CBD /* LanguageModelManager.mm */,
|
D41355D7278D7409005E5CBD /* LanguageModelManager.mm */,
|
||||||
D47B92BF27972AC800458394 /* main.swift */,
|
D4E569DA27A34CC100AC2CEF /* KeyHandler.h */,
|
||||||
|
D4E569DB27A34CC100AC2CEF /* KeyHandler.mm */,
|
||||||
|
D456576D279E4F7B00DF6BC9 /* KeyHandlerInput.swift */,
|
||||||
|
D461B791279DAC010070E734 /* InputState.swift */,
|
||||||
D427F76B278CA1BA004A2160 /* AppDelegate.swift */,
|
D427F76B278CA1BA004A2160 /* AppDelegate.swift */,
|
||||||
D44FB74427915555003C80A6 /* Preferences.swift */,
|
D44FB74427915555003C80A6 /* Preferences.swift */,
|
||||||
D44FB74627919C83003C80A6 /* EmacsKeyHelper.swift */,
|
|
||||||
D47F7DCF278C0897002F9DD7 /* NonModalAlertWindowController.swift */,
|
D47F7DCF278C0897002F9DD7 /* NonModalAlertWindowController.swift */,
|
||||||
D47F7DCD278BFB57002F9DD7 /* PreferencesWindowController.swift */,
|
D47F7DCD278BFB57002F9DD7 /* PreferencesWindowController.swift */,
|
||||||
|
D47B92BF27972AC800458394 /* main.swift */,
|
||||||
6A0D4EF615FC0DA600ABF4B3 /* McBopomofo-Prefix.pch */,
|
6A0D4EF615FC0DA600ABF4B3 /* McBopomofo-Prefix.pch */,
|
||||||
D427A9BF25ED28CC005D43E0 /* McBopomofo-Bridging-Header.h */,
|
D427A9BF25ED28CC005D43E0 /* McBopomofo-Bridging-Header.h */,
|
||||||
);
|
);
|
||||||
|
@ -486,6 +502,8 @@
|
||||||
children = (
|
children = (
|
||||||
D485D3B82796A8A000657FF3 /* PreferencesTests.swift */,
|
D485D3B82796A8A000657FF3 /* PreferencesTests.swift */,
|
||||||
D485D3BF2796CE3200657FF3 /* VersionUpdateTests.swift */,
|
D485D3BF2796CE3200657FF3 /* VersionUpdateTests.swift */,
|
||||||
|
D4E569DE27A40F1400AC2CEF /* KeyHandlerBopomofoTests.mm */,
|
||||||
|
D4E569DD27A40F1300AC2CEF /* McBopomofoTests-Bridging-Header.h */,
|
||||||
);
|
);
|
||||||
path = McBopomofoTests;
|
path = McBopomofoTests;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -593,6 +611,7 @@
|
||||||
};
|
};
|
||||||
D485D3B52796A8A000657FF3 = {
|
D485D3B52796A8A000657FF3 = {
|
||||||
CreatedOnToolsVersion = 13.2.1;
|
CreatedOnToolsVersion = 13.2.1;
|
||||||
|
LastSwiftMigration = 1320;
|
||||||
TestTargetID = 6A0D4EA115FC0D2D00ABF4B3;
|
TestTargetID = 6A0D4EA115FC0D2D00ABF4B3;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -664,6 +683,8 @@
|
||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
D4E569E527A414CB00AC2CEF /* data.txt in Resources */,
|
||||||
|
D4E569E427A414CB00AC2CEF /* data-plain-bpmf.txt in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -696,11 +717,13 @@
|
||||||
files = (
|
files = (
|
||||||
D427F76C278CA2B0004A2160 /* AppDelegate.swift in Sources */,
|
D427F76C278CA2B0004A2160 /* AppDelegate.swift in Sources */,
|
||||||
6ACC3D442793701600F1B140 /* ParselessPhraseDB.cpp in Sources */,
|
6ACC3D442793701600F1B140 /* ParselessPhraseDB.cpp in Sources */,
|
||||||
|
D461B792279DAC010070E734 /* InputState.swift in Sources */,
|
||||||
D47B92C027972AD100458394 /* main.swift in Sources */,
|
D47B92C027972AD100458394 /* main.swift in Sources */,
|
||||||
D44FB74727919D35003C80A6 /* EmacsKeyHelper.swift in Sources */,
|
|
||||||
D44FB74D2792189A003C80A6 /* PhraseReplacementMap.cpp in Sources */,
|
D44FB74D2792189A003C80A6 /* PhraseReplacementMap.cpp in Sources */,
|
||||||
D44FB74527915565003C80A6 /* Preferences.swift in Sources */,
|
D44FB74527915565003C80A6 /* Preferences.swift in Sources */,
|
||||||
|
D4E569DC27A34D0E00AC2CEF /* KeyHandler.mm in Sources */,
|
||||||
D47F7DD0278C0897002F9DD7 /* NonModalAlertWindowController.swift in Sources */,
|
D47F7DD0278C0897002F9DD7 /* NonModalAlertWindowController.swift in Sources */,
|
||||||
|
D456576E279E4F7B00DF6BC9 /* KeyHandlerInput.swift in Sources */,
|
||||||
D47F7DCE278BFB57002F9DD7 /* PreferencesWindowController.swift in Sources */,
|
D47F7DCE278BFB57002F9DD7 /* PreferencesWindowController.swift in Sources */,
|
||||||
6A0D4ED215FC0D6400ABF4B3 /* InputMethodController.mm in Sources */,
|
6A0D4ED215FC0D6400ABF4B3 /* InputMethodController.mm in Sources */,
|
||||||
D41355DB278E6D17005E5CBD /* McBopomofoLM.cpp in Sources */,
|
D41355DB278E6D17005E5CBD /* McBopomofoLM.cpp in Sources */,
|
||||||
|
@ -727,8 +750,12 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
D4E569DF27A40F1400AC2CEF /* KeyHandlerBopomofoTests.mm in Sources */,
|
||||||
D485D3B92796A8A000657FF3 /* PreferencesTests.swift in Sources */,
|
D485D3B92796A8A000657FF3 /* PreferencesTests.swift in Sources */,
|
||||||
|
D4E569E227A412E700AC2CEF /* Preferences.swift in Sources */,
|
||||||
|
D4E569E127A4128300AC2CEF /* InputState.swift in Sources */,
|
||||||
D485D3C02796CE3200657FF3 /* VersionUpdateTests.swift in Sources */,
|
D485D3C02796CE3200657FF3 /* VersionUpdateTests.swift in Sources */,
|
||||||
|
D4E569E027A4123200AC2CEF /* KeyHandlerInput.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -1226,6 +1253,7 @@
|
||||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
@ -1246,6 +1274,11 @@
|
||||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/../Frameworks",
|
||||||
|
"@loader_path/../Frameworks",
|
||||||
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
|
@ -1255,6 +1288,7 @@
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "McBopomofoTests/McBopomofoTests-Bridging-Header.h";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/McBopomofo.app/Contents/MacOS/McBopomofo";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/McBopomofo.app/Contents/MacOS/McBopomofo";
|
||||||
|
@ -1270,6 +1304,7 @@
|
||||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
@ -1285,6 +1320,11 @@
|
||||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/../Frameworks",
|
||||||
|
"@loader_path/../Frameworks",
|
||||||
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
@ -1293,6 +1333,7 @@
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "McBopomofoTests/McBopomofoTests-Bridging-Header.h";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/McBopomofo.app/Contents/MacOS/McBopomofo";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/McBopomofo.app/Contents/MacOS/McBopomofo";
|
||||||
|
|
|
@ -0,0 +1,655 @@
|
||||||
|
#import <XCTest/XCTest.h>
|
||||||
|
#import "KeyHandler.h"
|
||||||
|
#import "LanguageModelManager.h"
|
||||||
|
#import "McBopomofoTests-Swift.h"
|
||||||
|
|
||||||
|
@interface KeyHandlerBopomofoTests : XCTestCase
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation KeyHandlerBopomofoTests
|
||||||
|
|
||||||
|
- (void)setUp
|
||||||
|
{
|
||||||
|
[LanguageModelManager loadDataModels];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)tearDown
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testPunctuationComma
|
||||||
|
{
|
||||||
|
KeyHandler *handler = [[KeyHandler alloc] init];
|
||||||
|
handler.inputMode = kBopomofoModeIdentifier;
|
||||||
|
|
||||||
|
KeyHandlerInput *input;
|
||||||
|
__block InputState *state;
|
||||||
|
state = [[InputStateEmpty alloc] init];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"<" keyCode:0 charCode:'<' flags:NSEventModifierFlagShift isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertTrue([state isKindOfClass:NSClassFromString(@"McBopomofo.InputStateInputting")], @"It should be an inputting state %@.", NSStringFromClass([state class]));
|
||||||
|
NSString *composingBuffer = [(InputStateInputting *)state composingBuffer];
|
||||||
|
XCTAssertTrue([composingBuffer isEqualToString:@","], @"It should be , but %@", composingBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testPunctuationPeriod
|
||||||
|
{
|
||||||
|
KeyHandler *handler = [[KeyHandler alloc] init];
|
||||||
|
handler.inputMode = kBopomofoModeIdentifier;
|
||||||
|
|
||||||
|
KeyHandlerInput *input;
|
||||||
|
__block InputState *state;
|
||||||
|
state = [[InputStateEmpty alloc] init];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@">" keyCode:0 charCode:'>' flags:NSEventModifierFlagShift isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertTrue([state isKindOfClass:NSClassFromString(@"McBopomofo.InputStateInputting")], @"It should be an inputting state %@.", NSStringFromClass([state class]));
|
||||||
|
NSString *composingBuffer = [(InputStateInputting *)state composingBuffer];
|
||||||
|
XCTAssertTrue([composingBuffer isEqualToString:@"。"], @"It should be 。 but %@", composingBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testInputtingNihao
|
||||||
|
{
|
||||||
|
KeyHandler *handler = [[KeyHandler alloc] init];
|
||||||
|
handler.inputMode = kBopomofoModeIdentifier;
|
||||||
|
|
||||||
|
KeyHandlerInput *input;
|
||||||
|
__block InputState *state;
|
||||||
|
state = [[InputStateEmpty alloc] init];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"s" keyCode:0 charCode:'s' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"u" keyCode:0 charCode:'u' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"3" keyCode:0 charCode:'3' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"c" keyCode:0 charCode:'c' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"l" keyCode:0 charCode:'l' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"3" keyCode:0 charCode:'3' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertTrue([state isKindOfClass:NSClassFromString(@"McBopomofo.InputStateInputting")], @"It should be an inputting state %@.", NSStringFromClass([state class]));
|
||||||
|
NSString *composingBuffer = [(InputStateInputting *)state composingBuffer];
|
||||||
|
XCTAssertTrue([composingBuffer isEqualToString:@"你好"], @"It should be 你好 but %@", composingBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testCommittingNihao
|
||||||
|
{
|
||||||
|
KeyHandler *handler = [[KeyHandler alloc] init];
|
||||||
|
handler.inputMode = kBopomofoModeIdentifier;
|
||||||
|
|
||||||
|
KeyHandlerInput *input;
|
||||||
|
__block InputState *state;
|
||||||
|
state = [[InputStateEmpty alloc] init];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"s" keyCode:0 charCode:'s' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"u" keyCode:0 charCode:'u' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"3" keyCode:0 charCode:'3' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"c" keyCode:0 charCode:'c' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"l" keyCode:0 charCode:'l' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"3" keyCode:0 charCode:'3' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
__block NSInteger count = 0;
|
||||||
|
|
||||||
|
__block InputState *empty;
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@" " keyCode:0 charCode:13 flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
if (!count) {
|
||||||
|
state = inState;
|
||||||
|
}
|
||||||
|
empty = inState;
|
||||||
|
count++;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertTrue([state isKindOfClass:NSClassFromString(@"McBopomofo.InputStateCommitting")], @"It should be a committing state %@.", NSStringFromClass([state class]));
|
||||||
|
NSString *poppedText = [(InputStateCommitting *)state poppedText];
|
||||||
|
XCTAssertTrue([poppedText isEqualToString:@"你好"], @"It should be 你好 but %@", poppedText);
|
||||||
|
|
||||||
|
XCTAssertTrue([empty isKindOfClass:NSClassFromString(@"McBopomofo.InputStateEmpty")], @"It should be an empty state %@.", NSStringFromClass([state class]));
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testDelete
|
||||||
|
{
|
||||||
|
KeyHandler *handler = [[KeyHandler alloc] init];
|
||||||
|
handler.inputMode = kBopomofoModeIdentifier;
|
||||||
|
|
||||||
|
KeyHandlerInput *input;
|
||||||
|
__block InputState *state;
|
||||||
|
state = [[InputStateEmpty alloc] init];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"s" keyCode:0 charCode:'s' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"u" keyCode:0 charCode:'u' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"3" keyCode:0 charCode:'3' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"c" keyCode:0 charCode:'c' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"l" keyCode:0 charCode:'l' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"3" keyCode:0 charCode:'3' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertTrue([state isKindOfClass:NSClassFromString(@"McBopomofo.InputStateInputting")], @"It should be an inputting state %@.", NSStringFromClass([state class]));
|
||||||
|
NSString *composingBuffer = [(InputStateInputting *)state composingBuffer];
|
||||||
|
XCTAssertTrue([composingBuffer isEqualToString:@"你好"], @"It should be 你好 but %@", composingBuffer);
|
||||||
|
XCTAssertEqual([(InputStateInputting *)state cursorIndex], 2);
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@" " keyCode:123 charCode:0 flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = (InputStateInputting *)inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertTrue([state isKindOfClass:NSClassFromString(@"McBopomofo.InputStateInputting")], @"It should be a inputting state %@.", NSStringFromClass([state class]));
|
||||||
|
composingBuffer = [(InputStateInputting *)state composingBuffer];
|
||||||
|
XCTAssertTrue([composingBuffer isEqualToString:@"你好"], @"It should be 你好 but %@", composingBuffer);
|
||||||
|
XCTAssertEqual([(InputStateInputting *)state cursorIndex], 1);
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@" " keyCode:117 charCode:0 flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = (InputStateInputting *)inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertTrue([state isKindOfClass:NSClassFromString(@"McBopomofo.InputStateInputting")], @"It should be a inputting state %@.", NSStringFromClass([state class]));
|
||||||
|
composingBuffer = [(InputStateInputting *)state composingBuffer];
|
||||||
|
XCTAssertTrue([composingBuffer isEqualToString:@"你"], @"It should be 你 but %@", composingBuffer);
|
||||||
|
XCTAssertEqual([(InputStateInputting *)state cursorIndex], 1);
|
||||||
|
|
||||||
|
__block BOOL errorCalled = NO;
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@" " keyCode:117 charCode:0 flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = (InputStateInputting *)inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
errorCalled = YES;
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertTrue([state isKindOfClass:NSClassFromString(@"McBopomofo.InputStateInputting")], @"It should be a inputting state %@.", NSStringFromClass([state class]));
|
||||||
|
composingBuffer = [(InputStateInputting *)state composingBuffer];
|
||||||
|
XCTAssertTrue([composingBuffer isEqualToString:@"你"], @"It should be 你 but %@", composingBuffer);
|
||||||
|
XCTAssertEqual([(InputStateInputting *)state cursorIndex], 1);
|
||||||
|
XCTAssertTrue(errorCalled);
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@" " keyCode:123 charCode:0 flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = (InputStateInputting *)inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertTrue([state isKindOfClass:NSClassFromString(@"McBopomofo.InputStateInputting")], @"It should be a inputting state %@.", NSStringFromClass([state class]));
|
||||||
|
composingBuffer = [(InputStateInputting *)state composingBuffer];
|
||||||
|
XCTAssertTrue([composingBuffer isEqualToString:@"你"], @"It should be 你 but %@", composingBuffer);
|
||||||
|
XCTAssertEqual([(InputStateInputting *)state cursorIndex], 0);
|
||||||
|
|
||||||
|
errorCalled = NO;
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@" " keyCode:117 charCode:0 flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = (InputStateInputting *)inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
errorCalled = YES;
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertTrue([state isKindOfClass:NSClassFromString(@"McBopomofo.InputStateEmptyIgnoringPreviousState")], @"It should be a inputting state %@.", NSStringFromClass([state class]));
|
||||||
|
XCTAssertFalse(errorCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testBackspace
|
||||||
|
{
|
||||||
|
KeyHandler *handler = [[KeyHandler alloc] init];
|
||||||
|
handler.inputMode = kBopomofoModeIdentifier;
|
||||||
|
|
||||||
|
KeyHandlerInput *input;
|
||||||
|
__block InputState *state;
|
||||||
|
state = [[InputStateEmpty alloc] init];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"s" keyCode:0 charCode:'s' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"u" keyCode:0 charCode:'u' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"3" keyCode:0 charCode:'3' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"c" keyCode:0 charCode:'c' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"l" keyCode:0 charCode:'l' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"3" keyCode:0 charCode:'3' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertTrue([state isKindOfClass:NSClassFromString(@"McBopomofo.InputStateInputting")], @"It should be an inputting state %@.", NSStringFromClass([state class]));
|
||||||
|
NSString *composingBuffer = [(InputStateInputting *)state composingBuffer];
|
||||||
|
XCTAssertTrue([composingBuffer isEqualToString:@"你好"], @"It should be 你好 but %@", composingBuffer);
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@" " keyCode:0 charCode:8 flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = (InputStateInputting *)inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertTrue([state isKindOfClass:NSClassFromString(@"McBopomofo.InputStateInputting")], @"It should be a inputting state %@.", NSStringFromClass([state class]));
|
||||||
|
composingBuffer = [(InputStateInputting *)state composingBuffer];
|
||||||
|
XCTAssertTrue([composingBuffer isEqualToString:@"你"], @"It should be 你 but %@", composingBuffer);
|
||||||
|
|
||||||
|
__block InputStateEmpty *empty;
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@" " keyCode:0 charCode:8 flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
empty = (InputStateEmpty *)inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertTrue([empty isKindOfClass:NSClassFromString(@"McBopomofo.InputStateEmptyIgnoringPreviousState")], @"It should be a inputting state %@.", NSStringFromClass([state class]));
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testCursor
|
||||||
|
{
|
||||||
|
KeyHandler *handler = [[KeyHandler alloc] init];
|
||||||
|
handler.inputMode = kBopomofoModeIdentifier;
|
||||||
|
|
||||||
|
KeyHandlerInput *input;
|
||||||
|
__block InputState *state;
|
||||||
|
state = [[InputStateEmpty alloc] init];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"s" keyCode:0 charCode:'s' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"u" keyCode:0 charCode:'u' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"3" keyCode:0 charCode:'3' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"c" keyCode:0 charCode:'c' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"l" keyCode:0 charCode:'l' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"3" keyCode:0 charCode:'3' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertTrue([state isKindOfClass:NSClassFromString(@"McBopomofo.InputStateInputting")], @"It should be an inputting state %@.", NSStringFromClass([state class]));
|
||||||
|
NSString *composingBuffer = [(InputStateInputting *)state composingBuffer];
|
||||||
|
XCTAssertTrue([composingBuffer isEqualToString:@"你好"], @"It should be 你好 but %@", composingBuffer);
|
||||||
|
XCTAssertEqual([(InputStateInputting *)state cursorIndex], 2);
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@" " keyCode:123 charCode:0 flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = (InputStateInputting *)inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertTrue([state isKindOfClass:NSClassFromString(@"McBopomofo.InputStateInputting")], @"It should be a inputting state %@.", NSStringFromClass([state class]));
|
||||||
|
composingBuffer = [(InputStateInputting *)state composingBuffer];
|
||||||
|
XCTAssertTrue([composingBuffer isEqualToString:@"你好"], @"It should be 你好 but %@", composingBuffer);
|
||||||
|
XCTAssertEqual([(InputStateInputting *)state cursorIndex], 1);
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@" " keyCode:123 charCode:0 flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = (InputStateInputting *)inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertTrue([state isKindOfClass:NSClassFromString(@"McBopomofo.InputStateInputting")], @"It should be a inputting state %@.", NSStringFromClass([state class]));
|
||||||
|
composingBuffer = [(InputStateInputting *)state composingBuffer];
|
||||||
|
XCTAssertTrue([composingBuffer isEqualToString:@"你好"], @"It should be 你好 but %@", composingBuffer);
|
||||||
|
XCTAssertEqual([(InputStateInputting *)state cursorIndex], 0);
|
||||||
|
|
||||||
|
__block BOOL errorCalled = NO;
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@" " keyCode:123 charCode:0 flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = (InputStateInputting *)inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
errorCalled = YES;
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertTrue([state isKindOfClass:NSClassFromString(@"McBopomofo.InputStateInputting")], @"It should be a inputting state %@.", NSStringFromClass([state class]));
|
||||||
|
composingBuffer = [(InputStateInputting *)state composingBuffer];
|
||||||
|
XCTAssertTrue([composingBuffer isEqualToString:@"你好"], @"It should be 你好 but %@", composingBuffer);
|
||||||
|
XCTAssertEqual([(InputStateInputting *)state cursorIndex], 0);
|
||||||
|
XCTAssertTrue(errorCalled);
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@" " keyCode:124 charCode:0 flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = (InputStateInputting *)inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertTrue([state isKindOfClass:NSClassFromString(@"McBopomofo.InputStateInputting")], @"It should be a inputting state %@.", NSStringFromClass([state class]));
|
||||||
|
composingBuffer = [(InputStateInputting *)state composingBuffer];
|
||||||
|
XCTAssertTrue([composingBuffer isEqualToString:@"你好"], @"It should be 你好 but %@", composingBuffer);
|
||||||
|
XCTAssertEqual([(InputStateInputting *)state cursorIndex], 1);
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@" " keyCode:124 charCode:0 flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = (InputStateInputting *)inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertTrue([state isKindOfClass:NSClassFromString(@"McBopomofo.InputStateInputting")], @"It should be a inputting state %@.", NSStringFromClass([state class]));
|
||||||
|
composingBuffer = [(InputStateInputting *)state composingBuffer];
|
||||||
|
XCTAssertTrue([composingBuffer isEqualToString:@"你好"], @"It should be 你好 but %@", composingBuffer);
|
||||||
|
XCTAssertEqual([(InputStateInputting *)state cursorIndex], 2);
|
||||||
|
|
||||||
|
errorCalled = NO;
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@" " keyCode:124 charCode:0 flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = (InputStateInputting *)inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
errorCalled = YES;
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertTrue([state isKindOfClass:NSClassFromString(@"McBopomofo.InputStateInputting")], @"It should be a inputting state %@.", NSStringFromClass([state class]));
|
||||||
|
composingBuffer = [(InputStateInputting *)state composingBuffer];
|
||||||
|
XCTAssertTrue([composingBuffer isEqualToString:@"你好"], @"It should be 你好 but %@", composingBuffer);
|
||||||
|
XCTAssertEqual([(InputStateInputting *)state cursorIndex], 2);
|
||||||
|
XCTAssertTrue(errorCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testCandidateWithDown
|
||||||
|
{
|
||||||
|
KeyHandler *handler = [[KeyHandler alloc] init];
|
||||||
|
handler.inputMode = kBopomofoModeIdentifier;
|
||||||
|
|
||||||
|
KeyHandlerInput *input;
|
||||||
|
__block InputState *state;
|
||||||
|
state = [[InputStateEmpty alloc] init];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"s" keyCode:0 charCode:'s' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"u" keyCode:0 charCode:'u' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"3" keyCode:0 charCode:'3' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@" " keyCode:125 charCode:0 flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertTrue([state isKindOfClass:NSClassFromString(@"McBopomofo.InputStateChoosingCandidate")], @"It should be a inputting state %@.", NSStringFromClass([state class]));
|
||||||
|
NSArray *candidates = [(InputStateChoosingCandidate *)state candidates];
|
||||||
|
XCTAssertTrue([candidates containsObject:@"你"]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testHomeAndEnd
|
||||||
|
{
|
||||||
|
KeyHandler *handler = [[KeyHandler alloc] init];
|
||||||
|
handler.inputMode = kBopomofoModeIdentifier;
|
||||||
|
|
||||||
|
KeyHandlerInput *input;
|
||||||
|
__block InputState *state;
|
||||||
|
state = [[InputStateEmpty alloc] init];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"s" keyCode:0 charCode:'s' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"u" keyCode:0 charCode:'u' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"3" keyCode:0 charCode:'3' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"c" keyCode:0 charCode:'c' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"l" keyCode:0 charCode:'l' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@"3" keyCode:0 charCode:'3' flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertTrue([state isKindOfClass:NSClassFromString(@"McBopomofo.InputStateInputting")], @"It should be an inputting state %@.", NSStringFromClass([state class]));
|
||||||
|
NSString *composingBuffer = [(InputStateInputting *)state composingBuffer];
|
||||||
|
XCTAssertTrue([composingBuffer isEqualToString:@"你好"], @"It should be 你好 but %@", composingBuffer);
|
||||||
|
XCTAssertEqual([(InputStateInputting *)state cursorIndex], 2);
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@" " keyCode:115 charCode:0 flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertTrue([state isKindOfClass:NSClassFromString(@"McBopomofo.InputStateInputting")], @"It should be an inputting state %@.", NSStringFromClass([state class]));
|
||||||
|
composingBuffer = [(InputStateInputting *)state composingBuffer];
|
||||||
|
XCTAssertTrue([composingBuffer isEqualToString:@"你好"], @"It should be 你好 but %@", composingBuffer);
|
||||||
|
XCTAssertEqual([(InputStateInputting *)state cursorIndex], 0);
|
||||||
|
|
||||||
|
input = [[KeyHandlerInput alloc] initWithInputText:@" " keyCode:119 charCode:0 flags:0 isVerticalMode:0];
|
||||||
|
[handler handleInput:input state:state stateCallback:^(InputState * inState) {
|
||||||
|
state = inState;
|
||||||
|
} candidateSelectionCallback:^{
|
||||||
|
} errorCallback:^{
|
||||||
|
}];
|
||||||
|
|
||||||
|
XCTAssertTrue([state isKindOfClass:NSClassFromString(@"McBopomofo.InputStateInputting")], @"It should be an inputting state %@.", NSStringFromClass([state class]));
|
||||||
|
composingBuffer = [(InputStateInputting *)state composingBuffer];
|
||||||
|
XCTAssertTrue([composingBuffer isEqualToString:@"你好"], @"It should be 你好 but %@", composingBuffer);
|
||||||
|
XCTAssertEqual([(InputStateInputting *)state cursorIndex], 2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
//
|
||||||
|
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||||
|
//
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import XCTest
|
import XCTest
|
||||||
@testable import McBopomofo
|
//@testable import McBopomofo
|
||||||
|
|
||||||
class PreferencesTests: XCTestCase {
|
class PreferencesTests: XCTestCase {
|
||||||
|
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
// Copyright (c) 2022 and onwards The McBopomofo Authors.
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person
|
|
||||||
// obtaining a copy of this software and associated documentation
|
|
||||||
// files (the "Software"), to deal in the Software without
|
|
||||||
// restriction, including without limitation the rights to use,
|
|
||||||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the
|
|
||||||
// Software is furnished to do so, subject to the following
|
|
||||||
// conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be
|
|
||||||
// included in all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
||||||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
||||||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
||||||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
||||||
// OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
import Cocoa
|
|
||||||
|
|
||||||
@objc enum McBopomofoEmacsKey: UInt16 {
|
|
||||||
case none = 0
|
|
||||||
case forward = 6 // F
|
|
||||||
case backward = 2 // B
|
|
||||||
case home = 1 // A
|
|
||||||
case end = 5 // E
|
|
||||||
case delete = 4 // D
|
|
||||||
case nextPage = 22 // V
|
|
||||||
}
|
|
||||||
|
|
||||||
class EmacsKeyHelper: NSObject {
|
|
||||||
@objc static func detect(charCode: UniChar, flags: NSEvent.ModifierFlags) -> McBopomofoEmacsKey {
|
|
||||||
if flags.contains(.control) {
|
|
||||||
return McBopomofoEmacsKey(rawValue: charCode) ?? .none
|
|
||||||
}
|
|
||||||
return .none;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -53,9 +53,7 @@ namespace Formosa {
|
||||||
void setJoinSeparator(const string& separator);
|
void setJoinSeparator(const string& separator);
|
||||||
const string joinSeparator() const;
|
const string joinSeparator() const;
|
||||||
|
|
||||||
size_t markerCursorIndex() const;
|
vector<string> readings() const;
|
||||||
void setMarkerCursorIndex(size_t inNewIndex);
|
|
||||||
vector<string> readingsAtRange(size_t begin, size_t end) const;
|
|
||||||
|
|
||||||
Grid& grid();
|
Grid& grid();
|
||||||
|
|
||||||
|
@ -68,7 +66,6 @@ namespace Formosa {
|
||||||
static const size_t MaximumBuildSpanLength = 6;
|
static const size_t MaximumBuildSpanLength = 6;
|
||||||
|
|
||||||
size_t m_cursorIndex;
|
size_t m_cursorIndex;
|
||||||
size_t m_markerCursorIndex;
|
|
||||||
vector<string> m_readings;
|
vector<string> m_readings;
|
||||||
|
|
||||||
Grid m_grid;
|
Grid m_grid;
|
||||||
|
@ -79,14 +76,12 @@ namespace Formosa {
|
||||||
inline BlockReadingBuilder::BlockReadingBuilder(LanguageModel *inLM)
|
inline BlockReadingBuilder::BlockReadingBuilder(LanguageModel *inLM)
|
||||||
: m_LM(inLM)
|
: m_LM(inLM)
|
||||||
, m_cursorIndex(0)
|
, m_cursorIndex(0)
|
||||||
, m_markerCursorIndex(SIZE_MAX)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void BlockReadingBuilder::clear()
|
inline void BlockReadingBuilder::clear()
|
||||||
{
|
{
|
||||||
m_cursorIndex = 0;
|
m_cursorIndex = 0;
|
||||||
m_markerCursorIndex = SIZE_MAX;
|
|
||||||
m_readings.clear();
|
m_readings.clear();
|
||||||
m_grid.clear();
|
m_grid.clear();
|
||||||
}
|
}
|
||||||
|
@ -105,21 +100,6 @@ namespace Formosa {
|
||||||
{
|
{
|
||||||
m_cursorIndex = inNewIndex > m_readings.size() ? m_readings.size() : inNewIndex;
|
m_cursorIndex = inNewIndex > m_readings.size() ? m_readings.size() : inNewIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline size_t BlockReadingBuilder::markerCursorIndex() const
|
|
||||||
{
|
|
||||||
return m_markerCursorIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void BlockReadingBuilder::setMarkerCursorIndex(size_t inNewIndex)
|
|
||||||
{
|
|
||||||
if (inNewIndex == SIZE_MAX) {
|
|
||||||
m_markerCursorIndex = SIZE_MAX;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_markerCursorIndex = inNewIndex > m_readings.size() ? m_readings.size() : inNewIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void BlockReadingBuilder::insertReadingAtCursor(const string& inReading)
|
inline void BlockReadingBuilder::insertReadingAtCursor(const string& inReading)
|
||||||
{
|
{
|
||||||
|
@ -130,12 +110,9 @@ namespace Formosa {
|
||||||
m_cursorIndex++;
|
m_cursorIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline vector<string> BlockReadingBuilder::readingsAtRange(size_t begin, size_t end) const {
|
inline vector<string> BlockReadingBuilder::readings() const
|
||||||
vector<string> v;
|
{
|
||||||
for (size_t i = begin; i < end; i++) {
|
return m_readings;
|
||||||
v.push_back(m_readings[i]);
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool BlockReadingBuilder::deleteReadingBeforeCursor()
|
inline bool BlockReadingBuilder::deleteReadingBeforeCursor()
|
||||||
|
|
|
@ -23,44 +23,10 @@
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#import <InputMethodKit/InputMethodKit.h>
|
#import <InputMethodKit/InputMethodKit.h>
|
||||||
#import "Mandarin.h"
|
#import "McBopomofo-Swift.h"
|
||||||
#import "Gramambular.h"
|
|
||||||
#import "McBopomofoLM.h"
|
|
||||||
#import "UserOverrideModel.h"
|
|
||||||
|
|
||||||
@interface McBopomofoInputMethodController : IMKInputController
|
@interface McBopomofoInputMethodController : IMKInputController
|
||||||
{
|
|
||||||
@private
|
|
||||||
// the reading buffer that takes user input
|
|
||||||
Formosa::Mandarin::BopomofoReadingBuffer* _bpmfReadingBuffer;
|
|
||||||
|
|
||||||
// language model
|
- (void)handleState:(InputState *)newState client:(id)client;
|
||||||
McBopomofo::McBopomofoLM *_languageModel;
|
|
||||||
|
|
||||||
// user override model
|
|
||||||
McBopomofo::UserOverrideModel *_userOverrideModel;
|
|
||||||
|
|
||||||
// the grid (lattice) builder for the unigrams (and bigrams)
|
|
||||||
Formosa::Gramambular::BlockReadingBuilder* _builder;
|
|
||||||
|
|
||||||
// latest walked path (trellis) using the Viterbi algorithm
|
|
||||||
std::vector<Formosa::Gramambular::NodeAnchor> _walkedNodes;
|
|
||||||
|
|
||||||
// the latest composing buffer that is updated to the foreground app
|
|
||||||
NSMutableString *_composingBuffer;
|
|
||||||
NSInteger _latestReadingCursor;
|
|
||||||
|
|
||||||
// the current text input client; we need to keep this when candidate panel is on
|
|
||||||
id _currentCandidateClient;
|
|
||||||
|
|
||||||
// a special deferred client for Terminal.app fix
|
|
||||||
id _currentDeferredClient;
|
|
||||||
|
|
||||||
// current available candidates
|
|
||||||
NSMutableArray *_candidates;
|
|
||||||
|
|
||||||
// current input mode
|
|
||||||
NSString *_inputMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,236 @@
|
||||||
|
// Copyright (c) 2022 and onwards The McBopomofo Authors.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person
|
||||||
|
// obtaining a copy of this software and associated documentation
|
||||||
|
// files (the "Software"), to deal in the Software without
|
||||||
|
// restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the
|
||||||
|
// Software is furnished to do so, subject to the following
|
||||||
|
// conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
// OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
/// Represents the states for the input method controller.
|
||||||
|
///
|
||||||
|
/// An input method is actually a finite state machine. It receives the inputs
|
||||||
|
/// from hardware like keyboard and mouse, changes its state, updates user
|
||||||
|
/// interface by the state, and finally produces the text output and then them
|
||||||
|
/// to the client apps. It should be a one-way data flow, and the user interface
|
||||||
|
/// and text output should follow unconditionally one single data source.
|
||||||
|
///
|
||||||
|
/// The InputState class is for representing what the input controller is doing,
|
||||||
|
/// and the place to store the variables that could be used. For example, the
|
||||||
|
/// array for the candidate list is useful only when the user is choosing a
|
||||||
|
/// candidate, and the array should not exist when the input controller is in
|
||||||
|
/// another state.
|
||||||
|
///
|
||||||
|
/// They are immutable objects. When the state changes, the controller should
|
||||||
|
/// create a new state object to replace the current state instead of modifying
|
||||||
|
/// the existing one.
|
||||||
|
///
|
||||||
|
/// McBopomofo's input controller has following possible states:
|
||||||
|
///
|
||||||
|
/// - Deactivated: The user is not using McBopomofo yet.
|
||||||
|
/// - Empty: The user has switched to McBopomofo but did not input anything yet,
|
||||||
|
/// or, he or she has committed text into the client apps and starts a new
|
||||||
|
/// input phase.
|
||||||
|
/// - Committing: The input controller is sending text to the client apps.
|
||||||
|
/// - Inputting: The user has inputted something and the input buffer is
|
||||||
|
/// visible.
|
||||||
|
/// - Marking: The user is creating a area in the input buffer and about to
|
||||||
|
/// create a new user phrase.
|
||||||
|
/// - Choosing Candidate: The candidate window is open to let the user to choose
|
||||||
|
/// one among the candidates.
|
||||||
|
class InputState: NSObject {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents that the input controller is deactivated.
|
||||||
|
class InputStateDeactivated: InputState {
|
||||||
|
override var description: String {
|
||||||
|
"<InputStateDeactivated>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents that the composing buffer is empty.
|
||||||
|
class InputStateEmpty: InputState {
|
||||||
|
@objc var composingBuffer: String {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents that the composing buffer is empty.
|
||||||
|
class InputStateEmptyIgnoringPreviousState: InputState {
|
||||||
|
@objc var composingBuffer: String {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents that the input controller is committing text into client app.
|
||||||
|
class InputStateCommitting: InputState {
|
||||||
|
@objc private(set) var poppedText: String = ""
|
||||||
|
|
||||||
|
@objc convenience init(poppedText: String) {
|
||||||
|
self.init()
|
||||||
|
self.poppedText = poppedText
|
||||||
|
}
|
||||||
|
|
||||||
|
override var description: String {
|
||||||
|
"<InputStateCommitting poppedText:\(poppedText)>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents that the composing buffer is not empty.
|
||||||
|
class InputStateNotEmpty: InputState {
|
||||||
|
@objc private(set) var composingBuffer: String = ""
|
||||||
|
@objc private(set) var cursorIndex: UInt = 0
|
||||||
|
|
||||||
|
@objc init(composingBuffer: String, cursorIndex: UInt) {
|
||||||
|
self.composingBuffer = composingBuffer
|
||||||
|
self.cursorIndex = cursorIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
override var description: String {
|
||||||
|
"<InputStateNotEmpty, composingBuffer:\(composingBuffer), cursorIndex:\(cursorIndex)>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents that the user is inputting text.
|
||||||
|
class InputStateInputting: InputStateNotEmpty {
|
||||||
|
@objc var bpmfReading: String = ""
|
||||||
|
@objc var bpmfReadingCursorIndex: UInt8 = 0
|
||||||
|
@objc var poppedText: String = ""
|
||||||
|
|
||||||
|
@objc override init(composingBuffer: String, cursorIndex: UInt) {
|
||||||
|
super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var attributedString: NSAttributedString {
|
||||||
|
let attributedSting = NSAttributedString(string: composingBuffer, attributes: [
|
||||||
|
.underlineStyle: NSUnderlineStyle.single.rawValue,
|
||||||
|
.markedClauseSegment: 0
|
||||||
|
])
|
||||||
|
return attributedSting
|
||||||
|
}
|
||||||
|
|
||||||
|
override var description: String {
|
||||||
|
"<InputStateInputting, composingBuffer:\(composingBuffer), cursorIndex:\(cursorIndex), poppedText:\(poppedText)>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private let kMinMarkRangeLength = 2
|
||||||
|
private let kMaxMarkRangeLength = 6
|
||||||
|
|
||||||
|
/// Represents that the user is marking a range in the composing buffer.
|
||||||
|
class InputStateMarking: InputStateNotEmpty {
|
||||||
|
@objc private(set) var markerIndex: UInt
|
||||||
|
@objc private(set) var markedRange: NSRange
|
||||||
|
@objc var tooltip: String {
|
||||||
|
|
||||||
|
if Preferences.phraseReplacementEnabled {
|
||||||
|
return NSLocalizedString("Phrase replacement mode is on. Not suggested to add phrase in the mode.", comment: "")
|
||||||
|
}
|
||||||
|
if Preferences.chineseConversionStyle == 1 && Preferences.chineseConversionEnabled {
|
||||||
|
return NSLocalizedString("Model based Chinese conversion is on. Not suggested to add phrase in the mode.", comment: "")
|
||||||
|
}
|
||||||
|
if markedRange.length == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
let text = (composingBuffer as NSString).substring(with: markedRange)
|
||||||
|
if markedRange.length < kMinMarkRangeLength {
|
||||||
|
return String(format: NSLocalizedString("You are now selecting \"%@\". You can add a phrase with two or more characters.", comment: ""), text)
|
||||||
|
} else if (markedRange.length > kMaxMarkRangeLength) {
|
||||||
|
return String(format: NSLocalizedString("You are now selecting \"%@\". A phrase cannot be longer than %d characters.", comment: ""), text, kMaxMarkRangeLength)
|
||||||
|
}
|
||||||
|
return String(format: NSLocalizedString("You are now selecting \"%@\". Press enter to add a new phrase.", comment: ""), text)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private(set) var readings: [String] = []
|
||||||
|
|
||||||
|
@objc init(composingBuffer: String, cursorIndex: UInt, markerIndex: UInt, readings: [String]) {
|
||||||
|
self.markerIndex = markerIndex
|
||||||
|
let begin = min(cursorIndex, markerIndex)
|
||||||
|
let end = max(cursorIndex, markerIndex)
|
||||||
|
markedRange = NSMakeRange(Int(begin), Int(end - begin))
|
||||||
|
self.readings = readings
|
||||||
|
super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var attributedString: NSAttributedString {
|
||||||
|
let attributedSting = NSMutableAttributedString(string: composingBuffer)
|
||||||
|
let end = markedRange.location + markedRange.length
|
||||||
|
|
||||||
|
attributedSting.setAttributes([
|
||||||
|
.underlineStyle: NSUnderlineStyle.single.rawValue,
|
||||||
|
.markedClauseSegment: 0
|
||||||
|
], range: NSRange(location: 0, length: markedRange.location))
|
||||||
|
attributedSting.setAttributes([
|
||||||
|
.underlineStyle: NSUnderlineStyle.thick.rawValue,
|
||||||
|
.markedClauseSegment: 1
|
||||||
|
], range: markedRange)
|
||||||
|
attributedSting.setAttributes([
|
||||||
|
.underlineStyle: NSUnderlineStyle.single.rawValue,
|
||||||
|
.markedClauseSegment: 2
|
||||||
|
], range: NSRange(location: end,
|
||||||
|
length: composingBuffer.count - end))
|
||||||
|
return attributedSting
|
||||||
|
}
|
||||||
|
|
||||||
|
override var description: String {
|
||||||
|
"<InputStateMarking, composingBuffer:\(composingBuffer), cursorIndex:\(cursorIndex), markedRange:\(markedRange), readings:\(readings)>"
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func convertToInputting() -> InputStateInputting {
|
||||||
|
let state = InputStateInputting(composingBuffer: composingBuffer, cursorIndex: cursorIndex)
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var validToWrite: Bool {
|
||||||
|
markedRange.length >= kMinMarkRangeLength && markedRange.length <= kMaxMarkRangeLength
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var userPhrase: String {
|
||||||
|
let text = (composingBuffer as NSString).substring(with: markedRange)
|
||||||
|
let end = markedRange.location + markedRange.length
|
||||||
|
let readings = readings[markedRange.location..<end]
|
||||||
|
let joined = readings.joined(separator: "-")
|
||||||
|
return "\(text) \(joined)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents that the user is choosing in a candidates list.
|
||||||
|
class InputStateChoosingCandidate: InputStateNotEmpty {
|
||||||
|
@objc private(set) var candidates: [String] = []
|
||||||
|
@objc private(set) var useVerticalMode: Bool = false
|
||||||
|
|
||||||
|
@objc init(composingBuffer: String, cursorIndex: UInt, candidates: [String], useVerticalMode: Bool) {
|
||||||
|
self.candidates = candidates
|
||||||
|
self.useVerticalMode = useVerticalMode
|
||||||
|
super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var attributedString: NSAttributedString {
|
||||||
|
let attributedSting = NSAttributedString(string: composingBuffer, attributes: [
|
||||||
|
.underlineStyle: NSUnderlineStyle.single.rawValue,
|
||||||
|
.markedClauseSegment: 0
|
||||||
|
])
|
||||||
|
return attributedSting
|
||||||
|
}
|
||||||
|
|
||||||
|
override var description: String {
|
||||||
|
"<InputStateChoosingCandidate, candidates:\(candidates), useVerticalMode:\(useVerticalMode), composingBuffer:\(composingBuffer), cursorIndex:\(cursorIndex)>"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
// Copyright (c) 2011 and onwards The McBopomofo Authors.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person
|
||||||
|
// obtaining a copy of this software and associated documentation
|
||||||
|
// files (the "Software"), to deal in the Software without
|
||||||
|
// restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the
|
||||||
|
// Software is furnished to do so, subject to the following
|
||||||
|
// conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
// OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <string>
|
||||||
|
@import CandidateUI;
|
||||||
|
|
||||||
|
@class KeyHandlerInput;
|
||||||
|
@class InputState;
|
||||||
|
@class InputStateInputting;
|
||||||
|
@class InputStateMarking;
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
extern NSString *const kBopomofoModeIdentifier;
|
||||||
|
extern NSString *const kPlainBopomofoModeIdentifier;
|
||||||
|
|
||||||
|
@class KeyHandler;
|
||||||
|
|
||||||
|
@protocol KeyHandlerDelegate <NSObject>
|
||||||
|
- (VTCandidateController *)candidateControllerForKeyHandler:(KeyHandler *)keyHandler;
|
||||||
|
- (void)keyHandler:(KeyHandler *)keyHandler didSelectCandidateAtIndex:(NSInteger)index candidateController:(VTCandidateController *)controller;
|
||||||
|
- (BOOL)keyHandler:(KeyHandler *)keyHandler didRequestWriteUserPhraseWithState:(InputStateMarking *)state;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface KeyHandler : NSObject
|
||||||
|
|
||||||
|
- (BOOL)handleInput:(KeyHandlerInput *)input
|
||||||
|
state:(InputState *)state
|
||||||
|
stateCallback:(void (^)(InputState *))stateCallback
|
||||||
|
candidateSelectionCallback:(void (^)(void))candidateSelectionCallback
|
||||||
|
errorCallback:(void (^)(void))errorCallback;
|
||||||
|
|
||||||
|
- (void)syncWithPreferences;
|
||||||
|
- (void)fixNodeWithValue:(std::string)value;
|
||||||
|
- (void)clear;
|
||||||
|
|
||||||
|
- (InputStateInputting *)_buildInputtingState;
|
||||||
|
|
||||||
|
@property (strong, nonatomic) NSString *inputMode;
|
||||||
|
@property (weak, nonatomic) id <KeyHandlerDelegate> delegate;
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,193 @@
|
||||||
|
// Copyright (c) 2022 and onwards The McBopomofo Authors.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person
|
||||||
|
// obtaining a copy of this software and associated documentation
|
||||||
|
// files (the "Software"), to deal in the Software without
|
||||||
|
// restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the
|
||||||
|
// Software is furnished to do so, subject to the following
|
||||||
|
// conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
// OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
enum KeyCode: UInt16 {
|
||||||
|
case none = 0
|
||||||
|
case enter = 76
|
||||||
|
case up = 126
|
||||||
|
case down = 125
|
||||||
|
case left = 123
|
||||||
|
case right = 124
|
||||||
|
case pageUp = 116
|
||||||
|
case pageDown = 121
|
||||||
|
case home = 115
|
||||||
|
case end = 119
|
||||||
|
case delete = 117
|
||||||
|
}
|
||||||
|
|
||||||
|
class KeyHandlerInput: NSObject {
|
||||||
|
@objc private (set) var useVerticalMode: Bool
|
||||||
|
@objc private (set) var inputText: String?
|
||||||
|
@objc private (set) var charCode: UInt16
|
||||||
|
private var keyCode: UInt16
|
||||||
|
private var flags: NSEvent.ModifierFlags
|
||||||
|
private var cursorForwardKey: KeyCode
|
||||||
|
private var cursorBackwardKey: KeyCode
|
||||||
|
private var extraChooseCandidateKey: KeyCode
|
||||||
|
private var absorbedArrowKey: KeyCode
|
||||||
|
private var verticalModeOnlyChooseCandidateKey: KeyCode
|
||||||
|
@objc private (set) var emacsKey: McBopomofoEmacsKey
|
||||||
|
|
||||||
|
@objc init(inputText: String?, keyCode: UInt16, charCode: UInt16, flags: NSEvent.ModifierFlags, isVerticalMode: Bool) {
|
||||||
|
self.inputText = inputText
|
||||||
|
self.keyCode = keyCode
|
||||||
|
self.charCode = charCode
|
||||||
|
self.flags = flags
|
||||||
|
useVerticalMode = isVerticalMode
|
||||||
|
emacsKey = EmacsKeyHelper.detect(charCode: charCode, flags: flags)
|
||||||
|
cursorForwardKey = useVerticalMode ? .down : .right
|
||||||
|
cursorBackwardKey = useVerticalMode ? .up : .left
|
||||||
|
extraChooseCandidateKey = useVerticalMode ? .left : .down
|
||||||
|
absorbedArrowKey = useVerticalMode ? .right : .up
|
||||||
|
verticalModeOnlyChooseCandidateKey = useVerticalMode ? absorbedArrowKey : .none
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc init(event: NSEvent, isVerticalMode: Bool) {
|
||||||
|
inputText = event.characters
|
||||||
|
keyCode = event.keyCode
|
||||||
|
flags = event.modifierFlags
|
||||||
|
useVerticalMode = isVerticalMode
|
||||||
|
let charCode: UInt16 = {
|
||||||
|
guard let inputText = event.characters, inputText.count > 0 else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
let first = inputText[inputText.startIndex].utf16.first!
|
||||||
|
return first
|
||||||
|
}()
|
||||||
|
self.charCode = charCode
|
||||||
|
emacsKey = EmacsKeyHelper.detect(charCode: charCode, flags: event.modifierFlags)
|
||||||
|
cursorForwardKey = useVerticalMode ? .down : .right
|
||||||
|
cursorBackwardKey = useVerticalMode ? .up : .left
|
||||||
|
extraChooseCandidateKey = useVerticalMode ? .left : .down
|
||||||
|
absorbedArrowKey = useVerticalMode ? .right : .up
|
||||||
|
verticalModeOnlyChooseCandidateKey = useVerticalMode ? absorbedArrowKey : .none
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var isShiftHold: Bool {
|
||||||
|
flags.contains([.shift])
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var isCommandHold: Bool {
|
||||||
|
flags.contains([.command])
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var isControlHold: Bool {
|
||||||
|
flags.contains([.control])
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var isOptionHold: Bool {
|
||||||
|
flags.contains([.option])
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var isCapsLockOn: Bool {
|
||||||
|
flags.contains([.capsLock])
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var isNumericPad: Bool {
|
||||||
|
flags.contains([.numericPad])
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var isEnter: Bool {
|
||||||
|
KeyCode(rawValue: keyCode) == KeyCode.enter
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var isUp: Bool {
|
||||||
|
KeyCode(rawValue: keyCode) == KeyCode.up
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var isDown: Bool {
|
||||||
|
KeyCode(rawValue: keyCode) == KeyCode.down
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var isLeft: Bool {
|
||||||
|
KeyCode(rawValue: keyCode) == KeyCode.left
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var isRight: Bool {
|
||||||
|
KeyCode(rawValue: keyCode) == KeyCode.right
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var isPageUp: Bool {
|
||||||
|
KeyCode(rawValue: keyCode) == KeyCode.pageUp
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var isPageDown: Bool {
|
||||||
|
KeyCode(rawValue: keyCode) == KeyCode.pageDown
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var isHome: Bool {
|
||||||
|
KeyCode(rawValue: keyCode) == KeyCode.home
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var isEnd: Bool {
|
||||||
|
KeyCode(rawValue: keyCode) == KeyCode.end
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var isDelete: Bool {
|
||||||
|
KeyCode(rawValue: keyCode) == KeyCode.delete
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var isCursorBackward: Bool {
|
||||||
|
KeyCode(rawValue: keyCode) == cursorBackwardKey
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var isCursorForward: Bool {
|
||||||
|
KeyCode(rawValue: keyCode) == cursorForwardKey
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var isAbsorbedArrowKey: Bool {
|
||||||
|
KeyCode(rawValue: keyCode) == absorbedArrowKey
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var isExtraChooseCandidateKey: Bool {
|
||||||
|
KeyCode(rawValue: keyCode) == extraChooseCandidateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var isVerticalModeOnlyChooseCandidateKey: Bool {
|
||||||
|
KeyCode(rawValue: keyCode) == verticalModeOnlyChooseCandidateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc enum McBopomofoEmacsKey: UInt16 {
|
||||||
|
case none = 0
|
||||||
|
case forward = 6 // F
|
||||||
|
case backward = 2 // B
|
||||||
|
case home = 1 // A
|
||||||
|
case end = 5 // E
|
||||||
|
case delete = 4 // D
|
||||||
|
case nextPage = 22 // V
|
||||||
|
}
|
||||||
|
|
||||||
|
class EmacsKeyHelper: NSObject {
|
||||||
|
@objc static func detect(charCode: UniChar, flags: NSEvent.ModifierFlags) -> McBopomofoEmacsKey {
|
||||||
|
if flags.contains(.control) {
|
||||||
|
return McBopomofoEmacsKey(rawValue: charCode) ?? .none
|
||||||
|
}
|
||||||
|
return .none;
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,20 +22,13 @@
|
||||||
// OTHER DEALINGS IN THE SOFTWARE.
|
// OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
#import "LanguageModelManager.h"
|
#import "LanguageModelManager.h"
|
||||||
#import <fstream>
|
|
||||||
#import <iostream>
|
|
||||||
#import <set>
|
|
||||||
#import "OVStringHelper.h"
|
|
||||||
#import "OVUTF8Helper.h"
|
|
||||||
#import "McBopomofo-Swift.h"
|
#import "McBopomofo-Swift.h"
|
||||||
|
|
||||||
@import VXHanConvert;
|
@import VXHanConvert;
|
||||||
@import OpenCCBridge;
|
@import OpenCCBridge;
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace Formosa::Gramambular;
|
|
||||||
using namespace McBopomofo;
|
using namespace McBopomofo;
|
||||||
using namespace OpenVanilla;
|
|
||||||
|
|
||||||
static const int kUserOverrideModelCapacity = 500;
|
static const int kUserOverrideModelCapacity = 500;
|
||||||
static const double kObservedOverrideHalflife = 5400.0; // 1.5 hr.
|
static const double kObservedOverrideHalflife = 5400.0; // 1.5 hr.
|
||||||
|
@ -174,7 +167,7 @@ static void LTLoadLanguageModelFile(NSString *filenameWithoutExtension, McBopomo
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL shuoldAddLineBreakAtFront = NO;
|
BOOL addLineBreakAtFront = NO;
|
||||||
NSString *path = [self userPhrasesDataPathMcBopomofo];
|
NSString *path = [self userPhrasesDataPathMcBopomofo];
|
||||||
|
|
||||||
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
|
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
|
||||||
|
@ -188,7 +181,7 @@ static void LTLoadLanguageModelFile(NSString *filenameWithoutExtension, McBopomo
|
||||||
NSData *data = [readFile readDataToEndOfFile];
|
NSData *data = [readFile readDataToEndOfFile];
|
||||||
const void *bytes = [data bytes];
|
const void *bytes = [data bytes];
|
||||||
if (*(char *)bytes != '\n') {
|
if (*(char *)bytes != '\n') {
|
||||||
shuoldAddLineBreakAtFront = YES;
|
addLineBreakAtFront = YES;
|
||||||
}
|
}
|
||||||
[readFile closeFile];
|
[readFile closeFile];
|
||||||
}
|
}
|
||||||
|
@ -196,7 +189,7 @@ static void LTLoadLanguageModelFile(NSString *filenameWithoutExtension, McBopomo
|
||||||
}
|
}
|
||||||
|
|
||||||
NSMutableString *currentMarkedPhrase = [NSMutableString string];
|
NSMutableString *currentMarkedPhrase = [NSMutableString string];
|
||||||
if (shuoldAddLineBreakAtFront) {
|
if (addLineBreakAtFront) {
|
||||||
[currentMarkedPhrase appendString:@"\n"];
|
[currentMarkedPhrase appendString:@"\n"];
|
||||||
}
|
}
|
||||||
[currentMarkedPhrase appendString:userPhrase];
|
[currentMarkedPhrase appendString:userPhrase];
|
||||||
|
@ -218,7 +211,7 @@ static void LTLoadLanguageModelFile(NSString *filenameWithoutExtension, McBopomo
|
||||||
+ (NSString *)dataFolderPath
|
+ (NSString *)dataFolderPath
|
||||||
{
|
{
|
||||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDirectory, YES);
|
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDirectory, YES);
|
||||||
NSString *appSupportPath = [paths objectAtIndex:0];
|
NSString *appSupportPath = paths[0];
|
||||||
NSString *userDictPath = [appSupportPath stringByAppendingPathComponent:@"McBopomofo"];
|
NSString *userDictPath = [appSupportPath stringByAppendingPathComponent:@"McBopomofo"];
|
||||||
return userDictPath;
|
return userDictPath;
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@
|
||||||
|
|
||||||
"You are now selecting \"%@\". Press enter to add a new phrase." = "You are now selecting \"%@\". Press enter to add a new phrase.";
|
"You are now selecting \"%@\". Press enter to add a new phrase." = "You are now selecting \"%@\". Press enter to add a new phrase.";
|
||||||
|
|
||||||
"You are now selecting \"%@\". A phrase cannot be longer than 6 characters." = "You are now selecting \"%@\". A phrase cannot be longer than 6 characters.";
|
"You are now selecting \"%@\". A phrase cannot be longer than %d characters." = "You are now selecting \"%@\". A phrase cannot be longer than %d characters.";
|
||||||
|
|
||||||
"Chinese conversion on" = "Chinese conversion on";
|
"Chinese conversion on" = "Chinese conversion on";
|
||||||
|
|
||||||
|
@ -94,3 +94,7 @@
|
||||||
"Phrase replacement mode is on. Not suggested to add phrase in the mode." = "Phrase replacement mode is on. Not suggested to add phrase in the mode.";
|
"Phrase replacement mode is on. Not suggested to add phrase in the mode." = "Phrase replacement mode is on. Not suggested to add phrase in the mode.";
|
||||||
|
|
||||||
"Model based Chinese conversion is on. Not suggested to add phrase in the mode." = "Model based Chinese conversion is on. Not suggested to add phrase in the mode.";
|
"Model based Chinese conversion is on. Not suggested to add phrase in the mode." = "Model based Chinese conversion is on. Not suggested to add phrase in the mode.";
|
||||||
|
|
||||||
|
"Half-width punctuation on" = "Half-width punctuation on";
|
||||||
|
|
||||||
|
"Half-width punctuation off" = "Half-width punctuation off";
|
||||||
|
|
|
@ -69,7 +69,7 @@
|
||||||
|
|
||||||
"You are now selecting \"%@\". Press enter to add a new phrase." = "您目前選擇了 \"%@\"。按下 Enter 就可以加入到使用者詞彙中。";
|
"You are now selecting \"%@\". Press enter to add a new phrase." = "您目前選擇了 \"%@\"。按下 Enter 就可以加入到使用者詞彙中。";
|
||||||
|
|
||||||
"You are now selecting \"%@\". A phrase cannot be longer than 6 characters." = "您目前選擇了 \"%@\"。自訂詞彙不能超過六個字元。";
|
"You are now selecting \"%@\". A phrase cannot be longer than %d characters." = "您目前選擇了 \"%@\"。自訂詞彙不能超過 %d 個字元。";
|
||||||
|
|
||||||
"Chinese conversion on" = "已經切換到簡體中文模式";
|
"Chinese conversion on" = "已經切換到簡體中文模式";
|
||||||
|
|
||||||
|
@ -94,3 +94,7 @@
|
||||||
"Phrase replacement mode is on. Not suggested to add phrase in the mode." = "詞彙轉換已開啟,不建議在此模式下加詞。";
|
"Phrase replacement mode is on. Not suggested to add phrase in the mode." = "詞彙轉換已開啟,不建議在此模式下加詞。";
|
||||||
|
|
||||||
"Model based Chinese conversion is on. Not suggested to add phrase in the mode." = "您已開啟將語言模型轉為簡體中文,不建議在此模式下加詞。";
|
"Model based Chinese conversion is on. Not suggested to add phrase in the mode." = "您已開啟將語言模型轉為簡體中文,不建議在此模式下加詞。";
|
||||||
|
|
||||||
|
"Half-width punctuation on" = "已經切換到半型標點模式";
|
||||||
|
|
||||||
|
"Half-width punctuation off" = "已經切回到全型標點模式";
|
||||||
|
|
Loading…
Reference in New Issue