Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Sources/CodeEditorView/CodeStorageDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -804,7 +804,9 @@ extension CodeStorageDelegate {


let string = codeStorage.string,
char = string.utf16[string.index(string.startIndex, offsetBy: index)],
utf16View = string.utf16,
utf16Index = utf16View.index(utf16View.startIndex, offsetBy: index),
char = utf16View[utf16Index],
previousTypedToken = lastTypedToken,
currentTypedToken = codeStorage.tokenOnly(at: index)

Expand Down
47 changes: 47 additions & 0 deletions Tests/CodeEditorTests/TokenTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,51 @@ test */
, Tokeniser.Token(token: .curlyBracketClose, range: NSRange(location: 17, length: 1))])
}

func testInsertNewlineAfterEmoji() throws {
// Test case for UTF-16 indexing issue with emojis
// The emoji 馃弫 is 1 Character but 2 UTF-16 code units
// When inserting at the end, the UTF-16 offset differs from the Character offset
let code = "flag: 馃弫 fumble"
let codeStorageDelegate = CodeStorageDelegate(with: .swift(), setText: { _ in }),
codeStorage = CodeStorage(theme: .defaultLight)
codeStorage.delegate = codeStorageDelegate

codeStorage.setAttributedString(NSAttributedString(string: code))

// Verify initial state
XCTAssertEqual(code.count, 14) // 14 Characters
XCTAssertEqual((code as NSString).length, 15) // 15 UTF-16 code units

// Simulate inserting a newline at the very end (UTF-16 position 15)
// This should not crash
codeStorage.replaceCharacters(in: NSRange(location: 15, length: 0), with: "\n")

// Verify the insertion succeeded
XCTAssertEqual(codeStorage.string, "flag: 馃弫 fumble\n")
}

func testInsertCharacterAfterMultipleEmojis() throws {
// Test with multiple emojis to stress test UTF-16 handling
let code = "馃弫馃帀馃殌"
let codeStorageDelegate = CodeStorageDelegate(with: .swift(), setText: { _ in }),
codeStorage = CodeStorage(theme: .defaultLight)
codeStorage.delegate = codeStorageDelegate

codeStorage.setAttributedString(NSAttributedString(string: code))

// 3 Characters, 6 UTF-16 code units
XCTAssertEqual(code.count, 3)
XCTAssertEqual((code as NSString).length, 6)

// Insert at the end - should not crash
codeStorage.replaceCharacters(in: NSRange(location: 6, length: 0), with: "x")
XCTAssertEqual(codeStorage.string, "馃弫馃帀馃殌x")

// Insert in the middle after 馃弫 (UTF-16 position 2)
codeStorage.replaceCharacters(in: NSRange(location: 2, length: 0), with: "y")
XCTAssertEqual(codeStorage.string, "馃弫y馃帀馃殌x")
}

static var allTests = [
("testSimpleTokenise", testSimpleTokenise),
("testTokeniseAllComment", testTokeniseAllComment),
Expand All @@ -309,6 +354,8 @@ test */
("testCaseInsensitiveReservedIdentifiersUnspecified", testCaseInsensitiveReservedIdentifiersUnspecified),
("testCaseInsensitiveReservedIdentifiersFalse", testCaseInsensitiveReservedIdentifiersFalse),
("testCaseInsensitiveReservedIdentifiersTrue", testCaseInsensitiveReservedIdentifiersTrue),
("testInsertNewlineAfterEmoji", testInsertNewlineAfterEmoji),
("testInsertCharacterAfterMultipleEmojis", testInsertCharacterAfterMultipleEmojis),
]
}

Expand Down