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 */; };
|
||||
D44FB74D2792189A003C80A6 /* PhraseReplacementMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D44FB74B2792189A003C80A6 /* PhraseReplacementMap.cpp */; };
|
||||
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 */; };
|
||||
D47B92C027972AD100458394 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47B92BF27972AC800458394 /* main.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 */; };
|
||||
D485D3C02796CE3200657FF3 /* VersionUpdateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D485D3BF2796CE3200657FF3 /* VersionUpdateTests.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 */; };
|
||||
D4E33D8F27A838F0006DB1CF /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D4E33D8D27A838F0006DB1CF /* InfoPlist.strings */; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
|
@ -186,6 +185,7 @@
|
|||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
|
@ -214,6 +214,7 @@
|
|||
D427F7A927905E90004A2160 /* TooltipUI in Frameworks */,
|
||||
D47D73C327A7200500255A50 /* FSEventStreamHelper in Frameworks */,
|
||||
D427F76A278C9E29004A2160 /* CandidateUI in Frameworks */,
|
||||
D4C9CAB127AAC9690058DFEA /* NSStringUtils in Frameworks */,
|
||||
D427F7AE27907B8A004A2160 /* NotifierUI in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -272,7 +273,6 @@
|
|||
D4E569DB27A34CC100AC2CEF /* KeyHandler.mm */,
|
||||
D456576D279E4F7B00DF6BC9 /* KeyHandlerInput.swift */,
|
||||
D461B791279DAC010070E734 /* InputState.swift */,
|
||||
D45EB5BF27A9890C00E28B17 /* StringUtils.swift */,
|
||||
D427F76B278CA1BA004A2160 /* AppDelegate.swift */,
|
||||
D44FB74427915555003C80A6 /* Preferences.swift */,
|
||||
D47F7DCF278C0897002F9DD7 /* NonModalAlertWindowController.swift */,
|
||||
|
@ -414,6 +414,7 @@
|
|||
D427F7BF27908EAC004A2160 /* OpenCCBridge */,
|
||||
D44FB7482791B346003C80A6 /* VXHanConvert */,
|
||||
D47D73C027A71FFA00255A50 /* FSEventStreamHelper */,
|
||||
D4C9CAAF27AAC8EC0058DFEA /* NSStringUtils */,
|
||||
);
|
||||
name = Packages;
|
||||
sourceTree = "<group>";
|
||||
|
@ -477,6 +478,7 @@
|
|||
D427F7C027908EFC004A2160 /* OpenCCBridge */,
|
||||
D44FB7492791B829003C80A6 /* VXHanConvert */,
|
||||
D47D73C227A7200500255A50 /* FSEventStreamHelper */,
|
||||
D4C9CAB027AAC9690058DFEA /* NSStringUtils */,
|
||||
);
|
||||
productName = McBopomofo;
|
||||
productReference = 6A0D4EA215FC0D2D00ABF4B3 /* McBopomofo.app */;
|
||||
|
@ -661,7 +663,6 @@
|
|||
D47F7DD3278C1263002F9DD7 /* UserOverrideModel.cpp in Sources */,
|
||||
6A0D4F4515FC0EB100ABF4B3 /* Mandarin.cpp in Sources */,
|
||||
6ACC3D452793701600F1B140 /* ParselessLM.cpp in Sources */,
|
||||
D45EB5C127A9894C00E28B17 /* StringUtils.swift in Sources */,
|
||||
D41355DE278EA3ED005E5CBD /* UserPhrasesLM.cpp in Sources */,
|
||||
6ACC3D3F27914F2400F1B140 /* KeyValueBlobReader.cpp in Sources */,
|
||||
D41355D8278D74B5005E5CBD /* LanguageModelManager.mm in Sources */,
|
||||
|
@ -1350,6 +1351,10 @@
|
|||
isa = XCSwiftPackageProductDependency;
|
||||
productName = FSEventStreamHelper;
|
||||
};
|
||||
D4C9CAB027AAC9690058DFEA /* NSStringUtils */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = NSStringUtils;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
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.
|
||||
|
||||
import Cocoa
|
||||
import NSStringUtils
|
||||
|
||||
/// Represents the states for the input method controller.
|
||||
///
|
||||
|
@ -244,8 +245,8 @@ class InputState: NSObject {
|
|||
|
||||
@objc var userPhrase: String {
|
||||
let text = (composingBuffer as NSString).substring(with: markedRange)
|
||||
let exactBegin = StringUtils.convertToCharIndex(from: markedRange.location, in: composingBuffer)
|
||||
let exactEnd = StringUtils.convertToCharIndex(from: markedRange.location + markedRange.length, in: composingBuffer)
|
||||
let exactBegin = (composingBuffer as NSString).nextUtf16Position(for: markedRange.location)
|
||||
let exactEnd = (composingBuffer as NSString).previousUtf16Position(for: markedRange.location)
|
||||
let readings = readings[exactBegin..<exactEnd]
|
||||
let joined = readings.joined(separator: "-")
|
||||
return "\(text) \(joined)"
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#import <string>
|
||||
|
||||
@import CandidateUI;
|
||||
@import NSStringUtils;
|
||||
|
||||
// C++ namespace usages
|
||||
using namespace std;
|
||||
|
@ -576,7 +577,7 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot
|
|||
if ([input isShiftHold]) {
|
||||
// Shift + left
|
||||
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]];
|
||||
marking.tooltipForInputting = currentState.tooltip;
|
||||
stateCallback(marking);
|
||||
|
@ -614,7 +615,7 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot
|
|||
if ([input isShiftHold]) {
|
||||
// Shift + Right
|
||||
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]];
|
||||
marking.tooltipForInputting = currentState.tooltip;
|
||||
stateCallback(marking);
|
||||
|
@ -842,7 +843,7 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot
|
|||
&& ([input isShiftHold])) {
|
||||
NSUInteger index = state.markerIndex;
|
||||
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];
|
||||
marking.tooltipForInputting = state.tooltipForInputting;
|
||||
stateCallback(marking);
|
||||
|
@ -858,7 +859,7 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot
|
|||
&& ([input isShiftHold])) {
|
||||
NSUInteger index = state.markerIndex;
|
||||
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];
|
||||
marking.tooltipForInputting = state.tooltipForInputting;
|
||||
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