-
Notifications
You must be signed in to change notification settings - Fork 27.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Expose text boundary combiner class #112085
Conversation
fd3d18f
to
2c54b83
Compare
return position; | ||
} | ||
|
||
for (index -= 1; index >= 0; index -= 1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: the first term index -= 1
isn't that common in for loops. Consider using something like
while ((index -= 1) >= 0)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be easier to read if there's an _effectiveTextOffset method that turns caret locations to int
offsets?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
while loop makes sense, I am not sure how _effectiveTextOffset should be used to implement this for loop. Can you elaborate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like (I thought you added a similar function in the previous PR?):
int _toTextOffset(TextPosition position) => max(0, min(upstream ? position.offset - 1 : position.offset, textLength - 1));
Then:
int offset = _toTextOffset(position);
while (offset >= 0 && !TextLayoutMetrics.isWhitespace(_text.codeUnitAt(offset)) {
offset -= 1;
}
return Offset(offset: offset + 1, affinity: offset < 0 ? upstream : downstream); // Is the affinity correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason I don't want to change the the position to text offset before the first
if (position.affinity == TextAffinity.downstream && !TextLayoutMetrics.isWhitespace(_text.codeUnitAt(index))) {
return position;
}
is that I need to preserve the affinity of the input if the input already point to a non whitespace character.
An example lets say you have string without any white space 'abcd'.
The expected behavior is the following:
getLeadingTextBoundaryAt(TextPosition(3, upstream)) = TextPosition(3, upstream)
getLeadingTextBoundaryAt(TextPosition(2, downstream)) = TextPosition(2, downstream)
however your code will produce
getLeadingTextBoundaryAt(TextPosition(3, upstream)) = TextPosition(3, upstream)
getLeadingTextBoundaryAt(TextPosition(2, downstream)) = TextPosition(3, upstream)
The problem is you can't tell between TextPosition(3, upstream)
and TextPosition(2, downstream)
because they are the same in text offset. If you convert it first before the return, you can't differentiate them.
I could do the conversion after the first return, but at that point, I don't care about affinity anymore because I only need to find first non whitespace character and do (index+1 upstream).
|
||
@override | ||
TextPosition getLeadingTextBoundaryAt(TextPosition position) { | ||
// Handles outside of right bound. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: left -> leading/start, right -> trailing/end. The TextBoundary class does not know the directionality of the text.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also I don't remember why we need to clamp here, doesn't seem to be the case for other text boundary classes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did this for character and this because these require manually calculation on the string, the others call into textlayoutmetrics, which handle the boundary check. we could do this for all text boundaries regardless though, WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't most text boundaries assert the incoming position is within the document range?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PushTextPosition may potentially push out side of the bound, unless I add a API to query text length in TextBoundary
, I can't do perform clamping there. I think it is not worth it though.
return inner.getTrailingTextBoundaryAt( | ||
outer.getTrailingTextBoundaryAt(position), | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider overridding
TextRange getTextBoundaryAt(TextPosition position)
too.
This method exists because it's sometimes slightly cheaper to query both the leading + trailing than to query them separately. (for example, the low-level line boundary call on ui.Paragraph
actually gives you both offsets so there's no need to call that twice to get both the leading boundary and trailing boundary). That probably should have been documented...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah LineBreak
didn't override that. Could you add that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added line break and wordboundary override, but I can't find a meaningful implementation of ExpandedTextBoundary.getTextBoundaryAt since the return value doesnot contain affinity. I can't use the return value of the outer textboundary to call inner textboundary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's sad. Can we assume the TextRange's start position is always downstream and end position upstream?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well if the TextRange is collapsed we'll lose information. Maybe we should make TextPosition
a subclass of TextRange
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's sad. Can we assume the TextRange's start position is always downstream and end position upstream?
That is not the case for whitespace text boundary.
abc def
the position at the whitespace in the middle will return
leading = TextPosition(3, upstream)
trailing = TextPosition(4, downstream)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think TextRange.start and TextRange.end should be a TextPosition instead of int
cc5ac7e
to
637856d
Compare
/// by one with its affinity sets to upstream. For example, | ||
/// `TextPosition(1, upstream)` becomes `TextPosition(1, downstream)`, | ||
/// `TextPosition(4, downstream)` becomes `TextPosition(5, upstream)`. | ||
class PushTextPosition extends TextBoundary { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be better to use TextBoundary composition instead of taking a TextBoundary
explicitly? With the former say we create 2 constants PreviousCaretLocation
and NextCaretLocation
,
So today's
_PushTextPosition(mixedBoundary, intent.forward);
will be equivalent to
(intent.forward ? TextBoundary.NextCaretLocation : TextBoundary.PreviousCaretLocation) + mixedBoundary;
or
_PushTextPosition(info.forward) + mixedBoundary;
The order of operations will be clearer (from left to right, with _PushTextPosition it's not immediately clear if mixedBoundary will be applied first or push will be applied first).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find it hard to understand than the current code
PushTextPosition( WhiteSpaceBoundary() + WordBoundary(), true)
will become
(WordBoundary()+ WordBoundary).nextCaretLocation where the nextCaretLocation is from right to left.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not suggesting making that a getter. Your example will be equivalent to NextCaretLocation + WhiteSpaceBoundary + WordBoundary
return inner.getTrailingTextBoundaryAt( | ||
outer.getTrailingTextBoundaryAt(position), | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's sad. Can we assume the TextRange's start position is always downstream and end position upstream?
return inner.getTrailingTextBoundaryAt( | ||
outer.getTrailingTextBoundaryAt(position), | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well if the TextRange is collapsed we'll lose information. Maybe we should make TextPosition
a subclass of TextRange
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. IMO it's better to make _PushTextPosition a regular TextBoundary so you could do _PushTextPosition + Whitespace + WordBoundary
, instead of letting it handle composition itself.
f2c5893
to
0f54bd9
Compare
More public classes
fixes #111751
Pre-launch Checklist
///
).If you need help, consider asking for advice on the #hackers-new channel on Discord.