Moves the Swift StringUtils class to a Swift package.
This commit is contained in:
parent
5849478fe5
commit
f39b4fa0bc
|
@ -46,7 +46,6 @@
|
||||||
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 */; };
|
D456576E279E4F7B00DF6BC9 /* KeyHandlerInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = D456576D279E4F7B00DF6BC9 /* KeyHandlerInput.swift */; };
|
||||||
D45EB5C127A9894C00E28B17 /* StringUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D45EB5BF27A9890C00E28B17 /* StringUtils.swift */; };
|
|
||||||
D461B792279DAC010070E734 /* InputState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D461B791279DAC010070E734 /* InputState.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 */; };
|
||||||
D47D73A427A5D43900255A50 /* KeyHandlerBopomofoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47D73A327A5D43900255A50 /* KeyHandlerBopomofoTests.swift */; };
|
D47D73A427A5D43900255A50 /* KeyHandlerBopomofoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47D73A327A5D43900255A50 /* KeyHandlerBopomofoTests.swift */; };
|
||||||
|
@ -60,6 +59,7 @@
|
||||||
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 */; };
|
||||||
D4A13D5A27A59F0B003BE359 /* InputMethodController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A13D5927A59D5C003BE359 /* InputMethodController.swift */; };
|
D4A13D5A27A59F0B003BE359 /* InputMethodController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A13D5927A59D5C003BE359 /* InputMethodController.swift */; };
|
||||||
|
D4C9CAB127AAC9690058DFEA /* NSStringUtils in Frameworks */ = {isa = PBXBuildFile; productRef = D4C9CAB027AAC9690058DFEA /* NSStringUtils */; };
|
||||||
D4E33D8A27A838CF006DB1CF /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D4E33D8827A838CF006DB1CF /* Localizable.strings */; };
|
D4E33D8A27A838CF006DB1CF /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D4E33D8827A838CF006DB1CF /* Localizable.strings */; };
|
||||||
D4E33D8F27A838F0006DB1CF /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D4E33D8D27A838F0006DB1CF /* InfoPlist.strings */; };
|
D4E33D8F27A838F0006DB1CF /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D4E33D8D27A838F0006DB1CF /* InfoPlist.strings */; };
|
||||||
D4E569DC27A34D0E00AC2CEF /* KeyHandler.mm in Sources */ = {isa = PBXBuildFile; fileRef = D4E569DB27A34CC100AC2CEF /* KeyHandler.mm */; };
|
D4E569DC27A34D0E00AC2CEF /* KeyHandler.mm in Sources */ = {isa = PBXBuildFile; fileRef = D4E569DB27A34CC100AC2CEF /* KeyHandler.mm */; };
|
||||||
|
@ -169,7 +169,6 @@
|
||||||
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>"; };
|
D456576D279E4F7B00DF6BC9 /* KeyHandlerInput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyHandlerInput.swift; sourceTree = "<group>"; };
|
||||||
D45EB5BF27A9890C00E28B17 /* StringUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringUtils.swift; sourceTree = "<group>"; };
|
|
||||||
D461B791279DAC010070E734 /* InputState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputState.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>"; };
|
||||||
D47D73A327A5D43900255A50 /* KeyHandlerBopomofoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyHandlerBopomofoTests.swift; sourceTree = "<group>"; };
|
D47D73A327A5D43900255A50 /* KeyHandlerBopomofoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyHandlerBopomofoTests.swift; sourceTree = "<group>"; };
|
||||||
|
@ -186,6 +185,7 @@
|
||||||
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>"; };
|
||||||
D495583A27A5C6C4006ADE1C /* LanguageModelManager+Privates.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LanguageModelManager+Privates.h"; sourceTree = "<group>"; };
|
D495583A27A5C6C4006ADE1C /* LanguageModelManager+Privates.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LanguageModelManager+Privates.h"; sourceTree = "<group>"; };
|
||||||
D4A13D5927A59D5C003BE359 /* InputMethodController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputMethodController.swift; sourceTree = "<group>"; };
|
D4A13D5927A59D5C003BE359 /* InputMethodController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputMethodController.swift; sourceTree = "<group>"; };
|
||||||
|
D4C9CAAF27AAC8EC0058DFEA /* NSStringUtils */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = NSStringUtils; path = Packages/NSStringUtils; sourceTree = "<group>"; };
|
||||||
D4E33D8927A838CF006DB1CF /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
|
D4E33D8927A838CF006DB1CF /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
D4E33D8B27A838D5006DB1CF /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
D4E33D8B27A838D5006DB1CF /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
D4E33D8C27A838D8006DB1CF /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
D4E33D8C27A838D8006DB1CF /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||||
|
@ -214,6 +214,7 @@
|
||||||
D427F7A927905E90004A2160 /* TooltipUI in Frameworks */,
|
D427F7A927905E90004A2160 /* TooltipUI in Frameworks */,
|
||||||
D47D73C327A7200500255A50 /* FSEventStreamHelper in Frameworks */,
|
D47D73C327A7200500255A50 /* FSEventStreamHelper in Frameworks */,
|
||||||
D427F76A278C9E29004A2160 /* CandidateUI in Frameworks */,
|
D427F76A278C9E29004A2160 /* CandidateUI in Frameworks */,
|
||||||
|
D4C9CAB127AAC9690058DFEA /* NSStringUtils in Frameworks */,
|
||||||
D427F7AE27907B8A004A2160 /* NotifierUI in Frameworks */,
|
D427F7AE27907B8A004A2160 /* NotifierUI in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
@ -272,7 +273,6 @@
|
||||||
D4E569DB27A34CC100AC2CEF /* KeyHandler.mm */,
|
D4E569DB27A34CC100AC2CEF /* KeyHandler.mm */,
|
||||||
D456576D279E4F7B00DF6BC9 /* KeyHandlerInput.swift */,
|
D456576D279E4F7B00DF6BC9 /* KeyHandlerInput.swift */,
|
||||||
D461B791279DAC010070E734 /* InputState.swift */,
|
D461B791279DAC010070E734 /* InputState.swift */,
|
||||||
D45EB5BF27A9890C00E28B17 /* StringUtils.swift */,
|
|
||||||
D427F76B278CA1BA004A2160 /* AppDelegate.swift */,
|
D427F76B278CA1BA004A2160 /* AppDelegate.swift */,
|
||||||
D44FB74427915555003C80A6 /* Preferences.swift */,
|
D44FB74427915555003C80A6 /* Preferences.swift */,
|
||||||
D47F7DCF278C0897002F9DD7 /* NonModalAlertWindowController.swift */,
|
D47F7DCF278C0897002F9DD7 /* NonModalAlertWindowController.swift */,
|
||||||
|
@ -414,6 +414,7 @@
|
||||||
D427F7BF27908EAC004A2160 /* OpenCCBridge */,
|
D427F7BF27908EAC004A2160 /* OpenCCBridge */,
|
||||||
D44FB7482791B346003C80A6 /* VXHanConvert */,
|
D44FB7482791B346003C80A6 /* VXHanConvert */,
|
||||||
D47D73C027A71FFA00255A50 /* FSEventStreamHelper */,
|
D47D73C027A71FFA00255A50 /* FSEventStreamHelper */,
|
||||||
|
D4C9CAAF27AAC8EC0058DFEA /* NSStringUtils */,
|
||||||
);
|
);
|
||||||
name = Packages;
|
name = Packages;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -477,6 +478,7 @@
|
||||||
D427F7C027908EFC004A2160 /* OpenCCBridge */,
|
D427F7C027908EFC004A2160 /* OpenCCBridge */,
|
||||||
D44FB7492791B829003C80A6 /* VXHanConvert */,
|
D44FB7492791B829003C80A6 /* VXHanConvert */,
|
||||||
D47D73C227A7200500255A50 /* FSEventStreamHelper */,
|
D47D73C227A7200500255A50 /* FSEventStreamHelper */,
|
||||||
|
D4C9CAB027AAC9690058DFEA /* NSStringUtils */,
|
||||||
);
|
);
|
||||||
productName = McBopomofo;
|
productName = McBopomofo;
|
||||||
productReference = 6A0D4EA215FC0D2D00ABF4B3 /* McBopomofo.app */;
|
productReference = 6A0D4EA215FC0D2D00ABF4B3 /* McBopomofo.app */;
|
||||||
|
@ -661,7 +663,6 @@
|
||||||
D47F7DD3278C1263002F9DD7 /* UserOverrideModel.cpp in Sources */,
|
D47F7DD3278C1263002F9DD7 /* UserOverrideModel.cpp in Sources */,
|
||||||
6A0D4F4515FC0EB100ABF4B3 /* Mandarin.cpp in Sources */,
|
6A0D4F4515FC0EB100ABF4B3 /* Mandarin.cpp in Sources */,
|
||||||
6ACC3D452793701600F1B140 /* ParselessLM.cpp in Sources */,
|
6ACC3D452793701600F1B140 /* ParselessLM.cpp in Sources */,
|
||||||
D45EB5C127A9894C00E28B17 /* StringUtils.swift in Sources */,
|
|
||||||
D41355DE278EA3ED005E5CBD /* UserPhrasesLM.cpp in Sources */,
|
D41355DE278EA3ED005E5CBD /* UserPhrasesLM.cpp in Sources */,
|
||||||
6ACC3D3F27914F2400F1B140 /* KeyValueBlobReader.cpp in Sources */,
|
6ACC3D3F27914F2400F1B140 /* KeyValueBlobReader.cpp in Sources */,
|
||||||
D41355D8278D74B5005E5CBD /* LanguageModelManager.mm in Sources */,
|
D41355D8278D74B5005E5CBD /* LanguageModelManager.mm in Sources */,
|
||||||
|
@ -1350,6 +1351,10 @@
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
productName = FSEventStreamHelper;
|
productName = FSEventStreamHelper;
|
||||||
};
|
};
|
||||||
|
D4C9CAB027AAC9690058DFEA /* NSStringUtils */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
productName = NSStringUtils;
|
||||||
|
};
|
||||||
/* End XCSwiftPackageProductDependency section */
|
/* End XCSwiftPackageProductDependency section */
|
||||||
};
|
};
|
||||||
rootObject = 6A0D4E9415FC0CFA00ABF4B3 /* Project object */;
|
rootObject = 6A0D4E9415FC0CFA00ABF4B3 /* Project object */;
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
.DS_Store
|
||||||
|
/.build
|
||||||
|
/Packages
|
||||||
|
/*.xcodeproj
|
||||||
|
xcuserdata/
|
||||||
|
DerivedData/
|
||||||
|
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
|
@ -0,0 +1,28 @@
|
||||||
|
// swift-tools-version:5.3
|
||||||
|
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||||
|
|
||||||
|
import PackageDescription
|
||||||
|
|
||||||
|
let package = Package(
|
||||||
|
name: "NSStringUtils",
|
||||||
|
products: [
|
||||||
|
// Products define the executables and libraries a package produces, and make them visible to other packages.
|
||||||
|
.library(
|
||||||
|
name: "NSStringUtils",
|
||||||
|
targets: ["NSStringUtils"]),
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
// Dependencies declare other packages that this package depends on.
|
||||||
|
// .package(url: /* package url */, from: "1.0.0"),
|
||||||
|
],
|
||||||
|
targets: [
|
||||||
|
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
||||||
|
// Targets can depend on other targets in this package, and on products in packages this package depends on.
|
||||||
|
.target(
|
||||||
|
name: "NSStringUtils",
|
||||||
|
dependencies: []),
|
||||||
|
.testTarget(
|
||||||
|
name: "NSStringUtilsTests",
|
||||||
|
dependencies: ["NSStringUtils"]),
|
||||||
|
]
|
||||||
|
)
|
|
@ -0,0 +1,3 @@
|
||||||
|
# NSStringUtils
|
||||||
|
|
||||||
|
A description of this package.
|
|
@ -0,0 +1,50 @@
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
public extension NSString {
|
||||||
|
|
||||||
|
/// Converts the index in an NSString to the index in a Swift string.
|
||||||
|
///
|
||||||
|
/// An Emoji might be compose by more than one UTF-16 code points, however
|
||||||
|
/// the length of an NSString is only the sum of the UTF-16 code points. It
|
||||||
|
/// causes that the NSString and Swift string representation of the same
|
||||||
|
/// string have different lengths once the string contains such Emoji. The
|
||||||
|
/// method helps to find the index in a Swift string by passing the index
|
||||||
|
/// in an NSString.
|
||||||
|
func characterIndex(from utf16Index:Int) -> (Int, String) {
|
||||||
|
let string = (self as String)
|
||||||
|
var length = 0
|
||||||
|
for (i, character) in string.enumerated() {
|
||||||
|
length += character.utf16.count
|
||||||
|
if length > utf16Index {
|
||||||
|
return (i, string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (string.count, string)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func nextUtf16Position(for index: Int) -> Int {
|
||||||
|
var (fixedIndex, string) = characterIndex(from: index)
|
||||||
|
if fixedIndex < string.count {
|
||||||
|
fixedIndex += 1
|
||||||
|
}
|
||||||
|
return string[..<string.index(string.startIndex, offsetBy: fixedIndex)].utf16.count
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func previousUtf16Position(for index: Int) -> Int {
|
||||||
|
var (fixedIndex, string) = characterIndex(from: index)
|
||||||
|
if fixedIndex > 0 {
|
||||||
|
fixedIndex -= 1
|
||||||
|
}
|
||||||
|
return string[..<string.index(string.startIndex, offsetBy: fixedIndex)].utf16.count
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var count: Int {
|
||||||
|
(self as String).count
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc var split: [NSString] {
|
||||||
|
Array(self as String).map {
|
||||||
|
NSString(string: String($0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
import XCTest
|
||||||
|
@testable import NSStringUtils
|
||||||
|
|
||||||
|
final class NSStringUtilsTests: XCTestCase {
|
||||||
|
func testNextWith🌳_0() {
|
||||||
|
let s = NSString("🌳🌳")
|
||||||
|
XCTAssertEqual(s.nextUtf16Position(for: 0), 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testNextWith🌳_1() {
|
||||||
|
let s = NSString("🌳🌳")
|
||||||
|
XCTAssertEqual(s.nextUtf16Position(for: 1), 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testNextWith🌳_2() {
|
||||||
|
let s = NSString("🌳🌳")
|
||||||
|
XCTAssertEqual(s.nextUtf16Position(for: 2), 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testNextWith🌳_3() {
|
||||||
|
let s = NSString("🌳🌳")
|
||||||
|
XCTAssertEqual(s.nextUtf16Position(for: 3), 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testNextWith🌳_4() {
|
||||||
|
let s = NSString("🌳🌳")
|
||||||
|
XCTAssertEqual(s.nextUtf16Position(for: 4), 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testNextWith🌳_5() {
|
||||||
|
let s = NSString("🌳🌳🌳")
|
||||||
|
XCTAssertEqual(s.nextUtf16Position(for: 4), 6)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testPrevWith🌳_0() {
|
||||||
|
let s = NSString("🌳🌳")
|
||||||
|
XCTAssertEqual(s.previousUtf16Position(for: 0), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testPrevWith🌳_1() {
|
||||||
|
let s = NSString("🌳🌳")
|
||||||
|
XCTAssertEqual(s.previousUtf16Position(for: 1), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testINextWith🌳_2() {
|
||||||
|
let s = NSString("🌳🌳")
|
||||||
|
XCTAssertEqual(s.previousUtf16Position(for: 2), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testINextWith🌳_3() {
|
||||||
|
let s = NSString("🌳🌳")
|
||||||
|
XCTAssertEqual(s.previousUtf16Position(for: 3), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testINextWith🌳_4() {
|
||||||
|
let s = NSString("🌳🌳")
|
||||||
|
XCTAssertEqual(s.previousUtf16Position(for: 4), 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -22,6 +22,7 @@
|
||||||
// OTHER DEALINGS IN THE SOFTWARE.
|
// OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
import NSStringUtils
|
||||||
|
|
||||||
/// Represents the states for the input method controller.
|
/// Represents the states for the input method controller.
|
||||||
///
|
///
|
||||||
|
@ -244,8 +245,8 @@ class InputState: NSObject {
|
||||||
|
|
||||||
@objc var userPhrase: String {
|
@objc var userPhrase: String {
|
||||||
let text = (composingBuffer as NSString).substring(with: markedRange)
|
let text = (composingBuffer as NSString).substring(with: markedRange)
|
||||||
let exactBegin = StringUtils.convertToCharIndex(from: markedRange.location, in: composingBuffer)
|
let exactBegin = (composingBuffer as NSString).nextUtf16Position(for: markedRange.location)
|
||||||
let exactEnd = StringUtils.convertToCharIndex(from: markedRange.location + markedRange.length, in: composingBuffer)
|
let exactEnd = (composingBuffer as NSString).previousUtf16Position(for: markedRange.location)
|
||||||
let readings = readings[exactBegin..<exactEnd]
|
let readings = readings[exactBegin..<exactEnd]
|
||||||
let joined = readings.joined(separator: "-")
|
let joined = readings.joined(separator: "-")
|
||||||
return "\(text) \(joined)"
|
return "\(text) \(joined)"
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#import <string>
|
#import <string>
|
||||||
|
|
||||||
@import CandidateUI;
|
@import CandidateUI;
|
||||||
|
@import NSStringUtils;
|
||||||
|
|
||||||
// C++ namespace usages
|
// C++ namespace usages
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
@ -576,7 +577,7 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot
|
||||||
if ([input isShiftHold]) {
|
if ([input isShiftHold]) {
|
||||||
// Shift + left
|
// Shift + left
|
||||||
if (currentState.cursorIndex > 0) {
|
if (currentState.cursorIndex > 0) {
|
||||||
NSInteger previousPosition = [StringUtils previousUtf16PositionForIndex:currentState.cursorIndex in:currentState.composingBuffer];
|
NSInteger previousPosition = [currentState.composingBuffer nextUtf16PositionFor:currentState.cursorIndex];
|
||||||
InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:currentState.composingBuffer cursorIndex:currentState.cursorIndex markerIndex:previousPosition readings:[self _currentReadings]];
|
InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:currentState.composingBuffer cursorIndex:currentState.cursorIndex markerIndex:previousPosition readings:[self _currentReadings]];
|
||||||
marking.tooltipForInputting = currentState.tooltip;
|
marking.tooltipForInputting = currentState.tooltip;
|
||||||
stateCallback(marking);
|
stateCallback(marking);
|
||||||
|
@ -614,7 +615,7 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot
|
||||||
if ([input isShiftHold]) {
|
if ([input isShiftHold]) {
|
||||||
// Shift + Right
|
// Shift + Right
|
||||||
if (currentState.cursorIndex < currentState.composingBuffer.length) {
|
if (currentState.cursorIndex < currentState.composingBuffer.length) {
|
||||||
NSInteger nextPosition = [StringUtils nextUtf16PositionForIndex:currentState.cursorIndex in:currentState.composingBuffer];
|
NSInteger nextPosition = [currentState.composingBuffer nextUtf16PositionFor:currentState.cursorIndex];
|
||||||
InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:currentState.composingBuffer cursorIndex:currentState.cursorIndex markerIndex:nextPosition readings:[self _currentReadings]];
|
InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:currentState.composingBuffer cursorIndex:currentState.cursorIndex markerIndex:nextPosition readings:[self _currentReadings]];
|
||||||
marking.tooltipForInputting = currentState.tooltip;
|
marking.tooltipForInputting = currentState.tooltip;
|
||||||
stateCallback(marking);
|
stateCallback(marking);
|
||||||
|
@ -842,7 +843,7 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot
|
||||||
&& ([input isShiftHold])) {
|
&& ([input isShiftHold])) {
|
||||||
NSUInteger index = state.markerIndex;
|
NSUInteger index = state.markerIndex;
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
index = [StringUtils previousUtf16PositionForIndex:index in:state.composingBuffer];
|
index = [state.composingBuffer previousUtf16PositionFor:index];
|
||||||
InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:state.composingBuffer cursorIndex:state.cursorIndex markerIndex:index readings:state.readings];
|
InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:state.composingBuffer cursorIndex:state.cursorIndex markerIndex:index readings:state.readings];
|
||||||
marking.tooltipForInputting = state.tooltipForInputting;
|
marking.tooltipForInputting = state.tooltipForInputting;
|
||||||
stateCallback(marking);
|
stateCallback(marking);
|
||||||
|
@ -858,7 +859,7 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot
|
||||||
&& ([input isShiftHold])) {
|
&& ([input isShiftHold])) {
|
||||||
NSUInteger index = state.markerIndex;
|
NSUInteger index = state.markerIndex;
|
||||||
if (index < state.composingBuffer.length) {
|
if (index < state.composingBuffer.length) {
|
||||||
index = [StringUtils nextUtf16PositionForIndex:index in:state.composingBuffer];
|
index = [state.composingBuffer nextUtf16PositionFor:index];
|
||||||
InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:state.composingBuffer cursorIndex:state.cursorIndex markerIndex:index readings:state.readings];
|
InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:state.composingBuffer cursorIndex:state.cursorIndex markerIndex:index readings:state.readings];
|
||||||
marking.tooltipForInputting = state.tooltipForInputting;
|
marking.tooltipForInputting = state.tooltipForInputting;
|
||||||
stateCallback(marking);
|
stateCallback(marking);
|
||||||
|
|
|
@ -1,79 +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 Foundation
|
|
||||||
|
|
||||||
/// Utilities to convert the length of an NSString and a Swift string.
|
|
||||||
class StringUtils: NSObject {
|
|
||||||
|
|
||||||
/// Converts the index in an NSString to the index in a Swift string.
|
|
||||||
///
|
|
||||||
/// An Emoji might be compose by more than one UTF-16 code points, however
|
|
||||||
/// the length of an NSString is only the sum of the UTF-16 code points. It
|
|
||||||
/// causes that the NSString and Swift string representation of the same
|
|
||||||
/// string have different lengths once the string contains such Emoji. The
|
|
||||||
/// method helps to find the index in a Swift string by passing the index
|
|
||||||
/// in an NSString.
|
|
||||||
static func convertToCharIndex(from utf16Index: Int, in string: String) -> Int {
|
|
||||||
var length = 0
|
|
||||||
for (i, character) in string.enumerated() {
|
|
||||||
if length >= utf16Index {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
length += character.utf16.count
|
|
||||||
}
|
|
||||||
return string.count
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc (nextUtf16PositionForIndex:in:)
|
|
||||||
static func nextUtf16Position(for index: Int, in string: String) -> Int {
|
|
||||||
var index = convertToCharIndex(from: index, in: string)
|
|
||||||
if index < string.count {
|
|
||||||
index += 1
|
|
||||||
}
|
|
||||||
let count = string[..<string.index(string.startIndex, offsetBy: index)].utf16.count
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc (previousUtf16PositionForIndex:in:)
|
|
||||||
static func previousUtf16Position(for index: Int, in string: String) -> Int {
|
|
||||||
var index = convertToCharIndex(from: index, in: string)
|
|
||||||
if index > 0 {
|
|
||||||
index -= 1
|
|
||||||
}
|
|
||||||
let count = string[..<string.index(string.startIndex, offsetBy: index)].utf16.count
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension NSString {
|
|
||||||
@objc var count: Int {
|
|
||||||
(self as String).count
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc var split: [NSString] {
|
|
||||||
Array(self as String).map {
|
|
||||||
NSString(string: String($0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue