Repository navigation
Conversation
Message text that can't use the layer renderer, such as a message with a spoiler, is drawn by TextView. A Shift-click there extends the selection to findCharacterIndex(at:), which returns -1 for a click to the right of a line's end. Such a click lands inside the text view when another line of the message is longer. The Shift branch of mouseUp didn't check for -1, so it moved the selection's start to -1. The highlight then reached back over the start of the text, and the Quote item read the range from -1. In the first block of a message the quote was a stray character read from before the string's characters, the text before the selection and the selected text, with offset -1. Copy Selected Text, Copy, Translate and copying a link read the same range. The Shift branch now ignores an index below 0, as the Shift branch of ChatLayerTextNode and selectWord(at:) already do.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Key change
Shift-clicking to the right of the end of a line in a message no longer moves the start of the text selection to −1. The click now leaves the selection as it was. Before, the highlight spread back over the earlier lines, and Quote put a stray character and the text before the selection into the reply.
Reason
Message text that the layer renderer can't draw, for example a message with a spoiler (
ChatLayerMessageText+Layout.swift), is drawn byTextView. A Shift-click inTextView.mouseUpextends the selection to the character fromfindCharacterIndex(at:). That returns −1 when the click is to the right of the line's end, and such a click is still inside the text view when another line of the message is longer. The Shift branch didn't check for −1. Since −1 is less than the selection's start, it setrange.location = -1and lengthened the range.The highlight then covered everything from the start of the text.
FoldingTextLayout.mergedpasses the range on to the message menu, which reads it while it is built. The Quote item checks forNSNotFoundandNSMaxRangebut not for a negative location, so it calledattributedSubstring(from:)with location −1. The merged string is anNSMutableAttributedString, and it doesn't throw for that range: it returns one UTF-16 unit from just before its characters, then the rest. In the test below that unit was0x1C, the length of the 28-character message, so the quote became"\u{1C}Hi\n"plus the selected word, with offset −1. In a later block of a message the range starts on the last character of the block before instead. Copy Selected Text, Copy, Translate and copying a link from the selection read the same range, and media captions build the same menu frommerged.The Shift branch now ignores an index below 0. The layer-drawn message text already does this in its own Shift branch (
ChatLayerTextNode.swift), andselectWord(at:)returns early on −1. Shift-clicks on characters work as before. The change is in TGUIKit'sTextView, so it applies to every selectable text view, but only to clicks where the index is −1.iOS has no Shift-click. Its Quote action clamps the selected range to the text (
normalizedSelectionRangeinChatMessageTextBubbleContentNode.swift).Screenshot/video result (before/after)
Before
The word on the second line was double-clicked, then Shift-clicked to the right of
Hi. The highlight also coversHi, and Quote putsHiand the word into the reply panel. The panel's text starts with a0x1Ccharacter, which isn't visible.After
The same steps. The selection stays on the word, and Quote quotes only the word.
Confirmation
Tested on macOS 26.6.2 (25G83) in a Debug build (13.1.0, 283357), signed in to a test environment account. Both builds were master
e1883098with a local test harness and unrelated local changes. Those include edits inTextView.swift(copy-protection checks in the block-quote header click and incopy(_:)) and inChatMessageMenuItems.swift, none of them in the Shift-click branch or the Quote item. The After build also has this change.Steps:
Hi, then a word and a spoiler (quote<number> hidden, withhiddenas a spoiler).Hi, inside the bubble.HiHiHiquote…; its text is0x1C,Hi, a line break and the wordquote…These ran through a scripted test in a test group, one pass per build. The test reads the reply panel's text through accessibility.
tools/tests/shift-click-past-line-endtakesfindIndex(location:),findCharacterIndex(at:), the Shift branch ofmouseUp, the Shift branch ofChatLayerTextNode,FoldingTextLayout.merged, the Quote item's check and substring, the Copy Selected Text handler andTextView.copy(_:)from the source. It lays out real CoreText lines and runs 13 cases: Shift-clicks past the end of a short first or last line, with the selection before or after that line or at the start of the text, in the first or second block of a message; Shift-clicks on characters before, inside and after the selection; and three clicks past the end of a line that must not enter the Shift branch, with no selection, as a double-click and without Shift. Each case runs in a separate process, so a crash would show up as a failure. The test makes 100 checks. On master 42 of them fail, all in the 6 cases that Shift-click past the end of a line with a selection: the selection starts at −1, and Quote, Copy Selected Text and Copy get a stray character and the text before the selection (in a message's second block, Quote and Copy Selected Text get the first block's last character instead). Nothing crashes there. All 9 mutants of the fix that change its behavior are caught, including a guard at the Quote item alone.Limitations:
One pass per build in the app, with one message in one group. Other line positions, Copy Selected Text and Copy are covered by the test only.
The scripted test doesn't send the quoted reply, so what the server does with a quote at offset −1 isn't checked.
The app builds were based on
e1883098, not the current master.TextView.swiftdiffers between them only inrightMouseDown(public→open) and the block-quote header click.ChatMessageMenuItems.swiftdiffers too, but not in the Quote item or the Copy Selected Text, Copy Link and Translate items. The test runs against the current master source.Translate, copying a link and media captions read the same range, but weren't tested separately.
I confirm that I have tested the affected behavior and verified that it works properly, or verified the content and links for a documentation-only change.