-
Notifications
You must be signed in to change notification settings - Fork 6k
fix voice control delete line command does not delete line correctly #24831
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -378,6 +378,73 @@ - (BOOL)isEqualTo:(FlutterTextRange*)other { | |
| } | ||
| @end | ||
|
|
||
| #pragma mark - FlutterTokenizer | ||
|
|
||
| @interface FlutterTokenizer () | ||
|
|
||
| @property(nonatomic, assign) FlutterTextInputView* textInputView; | ||
|
|
||
| @end | ||
|
|
||
| @implementation FlutterTokenizer | ||
|
|
||
| - (instancetype)initWithTextInput:(UIResponder<UITextInput>*)textInput { | ||
| NSAssert([textInput isKindOfClass:[FlutterTextInputView class]], | ||
| @"The FlutterTokenizer can only be used in a FlutterTextInputView"); | ||
| self = [super initWithTextInput:textInput]; | ||
| if (self) { | ||
| _textInputView = (FlutterTextInputView*)textInput; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why cast here? Why not just pass in a FlutterTextInputView? If you want to keep the cast you should have an assert that it a valid cast (
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes i have assert it in the beginning, line 392. This is an override of https://developer.apple.com/documentation/uikit/uitextinputstringtokenizer/1614469-initwithtextinput?language=objc
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought i saw it somewhere, thanks. |
||
| } | ||
| return self; | ||
| } | ||
|
|
||
| - (UITextRange*)rangeEnclosingPosition:(UITextPosition*)position | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you just pass in the FlutterTextInputView as an argument to this method you wouldn't have to keep an instance variable around. The instance variable is a bit problematic because if the class is used differently it can cause a crash.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is an override. https://developer.apple.com/documentation/uikit/uitextinputtokenizer/1614464-rangeenclosingposition?language=objc I can't change the function signature
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I figured it is quite hard to know whether this is an override or private method for a reviewer. is there a best practice way to annotate an override method?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You almost never use inheritance in objc. I didn't see this was inheriting UITextInputStringTokenizer. Is there a reason it has to inherit from UITextInputStringTokenizer? The documentation says this for subclassing UITextInputString: Sounds like it might be a problem if you aren't calling super. edit:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it does not seems to be a hard requirement? a little background on this change. If you have text = "how are you" it returns The result is completely unusable. That is why i have to complete rewrite the line logic.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yea, maybe you are right. I have a hard time understanding if that warning in the documentation is actually applicable. We do have tests I guess. |
||
| withGranularity:(UITextGranularity)granularity | ||
| inDirection:(UITextDirection)direction { | ||
| UITextRange* result; | ||
| switch (granularity) { | ||
| case UITextGranularityLine: | ||
| // The default UITextInputStringTokenizer does not handle line granularity | ||
| // correctly. We need to implement our own line tokenizer. | ||
| result = [self lineEnclosingPosition:position]; | ||
| break; | ||
| case UITextGranularityCharacter: | ||
| case UITextGranularityWord: | ||
| case UITextGranularitySentence: | ||
| case UITextGranularityParagraph: | ||
| case UITextGranularityDocument: | ||
chunhtai marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // The UITextInputStringTokenizer can handle all these cases correctly. | ||
| result = [super rangeEnclosingPosition:position | ||
| withGranularity:granularity | ||
| inDirection:direction]; | ||
| break; | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| - (UITextRange*)lineEnclosingPosition:(UITextPosition*)position { | ||
| // Gets the first line break position after the input position. | ||
| NSString* textAfter = [_textInputView | ||
| textInRange:[_textInputView textRangeFromPosition:position | ||
| toPosition:[_textInputView endOfDocument]]]; | ||
| NSArray<NSString*>* linesAfter = [textAfter componentsSeparatedByString:@"\n"]; | ||
| NSInteger offSetToLineBreak = [linesAfter firstObject].length; | ||
| UITextPosition* lineBreakAfter = [_textInputView positionFromPosition:position | ||
| offset:offSetToLineBreak]; | ||
| // Gets the first line break position before the input position. | ||
| NSString* textBefore = [_textInputView | ||
| textInRange:[_textInputView textRangeFromPosition:[_textInputView beginningOfDocument] | ||
| toPosition:position]]; | ||
| NSArray<NSString*>* linesBefore = [textBefore componentsSeparatedByString:@"\n"]; | ||
| NSInteger offSetFromLineBreak = [linesBefore lastObject].length; | ||
| UITextPosition* lineBreakBefore = [_textInputView positionFromPosition:position | ||
| offset:-offSetFromLineBreak]; | ||
|
|
||
| return [_textInputView textRangeFromPosition:lineBreakBefore toPosition:lineBreakAfter]; | ||
| } | ||
|
|
||
| @end | ||
|
|
||
| // A FlutterTextInputView that masquerades as a UITextField, and forwards | ||
| // selectors it can't respond to to a shared UITextField instance. | ||
| // | ||
|
|
@@ -629,7 +696,7 @@ - (BOOL)canBecomeFirstResponder { | |
|
|
||
| - (id<UITextInputTokenizer>)tokenizer { | ||
| if (_tokenizer == nil) { | ||
| _tokenizer = [[UITextInputStringTokenizer alloc] initWithTextInput:self]; | ||
| _tokenizer = [[FlutterTokenizer alloc] initWithTextInput:self]; | ||
| } | ||
| return _tokenizer; | ||
| } | ||
|
|
||
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.
s/assign/weak for more modern objc and forward compatibility with ARC
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 tried to use weak, but it complains this file is MRC and can't use weak