Skip to content
Open
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
## 1.0.1

- Fix: recheck text on changing language
- Fixes a bug where changing text programmatically can sometimes throw a RangeError
- Fixes notifying listeners after async gap when disposed

## 1.0.0

Expand Down
67 changes: 42 additions & 25 deletions lib/src/core/controllers/language_tool_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import 'package:languagetool_textfield/src/utils/keep_latest_response_service.da
/// marked TextSpans with tap recognizer
class LanguageToolController extends TextEditingController {
bool _isEnabled;
bool _isDisposed = false;

/// Color scheme to highlight mistakes
final HighlightStyle highlightStyle;
Expand Down Expand Up @@ -59,7 +60,7 @@ class LanguageToolController extends TextEditingController {
bool get isEnabled => _isEnabled;

set isEnabled(bool value) {
if (value == _isEnabled) return;
if (value == _isEnabled || _isDisposed) return;

_isEnabled = value;

Expand Down Expand Up @@ -126,9 +127,11 @@ class LanguageToolController extends TextEditingController {
}) {
final languageToolService = LanguageToolService(languageToolClient);

// false positive, the same variable is used beyond the return statement
// ignore: avoid_unnecessary_return_variable
if (delay == Duration.zero) return languageToolService;
if (delay == Duration.zero) {
// false positive, the variable might be used after the if statement
// ignore: avoid_unnecessary_return_variable
return languageToolService;
}

switch (delayType) {
case DelayType.debouncing:
Expand Down Expand Up @@ -168,6 +171,7 @@ class LanguageToolController extends TextEditingController {

@override
void dispose() {
_isDisposed = true;
_languageCheckService?.dispose();
super.dispose();
}
Expand Down Expand Up @@ -198,8 +202,7 @@ class LanguageToolController extends TextEditingController {
///set value triggers each time, even when cursor changes its location
///so this check avoid cleaning Mistake list when text wasn't really changed
if (spellCheckSameText || newText != text && newText.isNotEmpty) {
final filteredMistakes = _filterMistakesOnChanged(newText);
_mistakes = filteredMistakes.toList();
_mistakes = _filterMistakesOnChanged(_mistakes, newText);

// If we have a text change and we have a popup on hold
// it will close the popup
Expand All @@ -215,7 +218,11 @@ class LanguageToolController extends TextEditingController {
() =>
_languageCheckService?.findMistakes(newText) ?? Future(() => null),
);
if (mistakesWrapper == null || !mistakesWrapper.hasResult) return;
if (mistakesWrapper == null ||
!mistakesWrapper.hasResult ||
_isDisposed) {
return;
}

final mistakes = mistakesWrapper.result();
_fetchError = mistakesWrapper.error;
Expand Down Expand Up @@ -310,27 +317,37 @@ class LanguageToolController extends TextEditingController {
);
}

/// Filters the list of mistakes based on the changes
/// in the text when it is changed.
Iterable<Mistake> _filterMistakesOnChanged(String newText) sync* {
List<Mistake> _filterMistakesOnChanged(
List<Mistake> mistakes,
String newText,
) {
if (!selection.isValid) {
return mistakes
.where((mistake) => mistake.endOffset <= newText.length)
.toList();
}

final isSelectionRangeEmpty = selection.end == selection.start;
final lengthDiscrepancy = newText.length - text.length;

for (final mistake in _mistakes) {
Mistake? newMistake;

newMistake = isSelectionRangeEmpty
? _adjustMistakeOffsetWithCaretCursor(
mistake: mistake,
lengthDiscrepancy: lengthDiscrepancy,
)
: _adjustMistakeOffsetWithSelectionRange(
mistake: mistake,
lengthDiscrepancy: lengthDiscrepancy,
);

if (newMistake != null) yield newMistake;
}
return mistakes
.map(
(mistake) => isSelectionRangeEmpty
? _adjustMistakeOffsetWithCaretCursor(
mistake: mistake,
lengthDiscrepancy: lengthDiscrepancy,
)
: _adjustMistakeOffsetWithSelectionRange(
mistake: mistake,
lengthDiscrepancy: lengthDiscrepancy,
),
)
.nonNulls
.where(
(mistake) =>
mistake.offset >= 0 && mistake.endOffset <= newText.length,
)
.toList();
}
Comment thread
andrew-bekhiet-solid marked this conversation as resolved.

/// Adjusts the mistake offset when the selection is a caret cursor.
Expand Down
1 change: 1 addition & 0 deletions lib/src/utils/mistake_popup.dart
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ class LanguageToolMistakePopup extends StatelessWidget {
required this.mistake,
required this.controller,
required this.mistakePosition,
super.key,
this.maxWidth = _defaultMaxWidth,
this.maxHeight = double.infinity,
this.horizontalMargin = _defaultHorizontalMargin,
Expand Down
Loading