Merge pull request #453 from pangeachat/spans-from-matches

use match data to construct input underlines, without using tokens
This commit is contained in:
ggurdin 2024-07-09 15:29:57 -04:00 committed by GitHub
commit f59372a5e2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 61 additions and 138 deletions

View file

@ -180,18 +180,9 @@ class Choreographer {
return;
}
if ([
EditType.igc,
].contains(_textController.editType)) {
// this may be unnecessary now that tokens are not used
// to allow click of words in the input field and we're getting this at the end
// TODO - turn it off and tested that this is fine
igc.justGetTokensAndAddThemToIGCTextData();
// we set editType to keyboard here because that is the default for it
// and we want to make sure that the next change is treated as a keyboard change
// unless the system explicity sets it to something else. this
textController.editType = EditType.keyboard;
if (_textController.editType == EditType.igc) {
_lastChecked = _textController.text;
_textController.editType = EditType.keyboard;
return;
}

View file

@ -7,11 +7,9 @@ import 'package:fluffychat/pangea/choreographer/controllers/span_data_controller
import 'package:fluffychat/pangea/models/igc_text_data_model.dart';
import 'package:fluffychat/pangea/models/pangea_match_model.dart';
import 'package:fluffychat/pangea/repo/igc_repo.dart';
import 'package:fluffychat/pangea/repo/tokens_repo.dart';
import 'package:fluffychat/pangea/widgets/igc/span_card.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import '../../models/span_card_model.dart';
import '../../utils/error_handler.dart';
@ -83,62 +81,6 @@ class IgcController {
}
}
Future<void> justGetTokensAndAddThemToIGCTextData() async {
try {
if (igcTextData == null) {
debugger(when: kDebugMode);
choreographer.getLanguageHelp();
return;
}
igcTextData!.loading = true;
choreographer.startLoading();
if (igcTextData!.originalInput != choreographer.textController.text) {
debugger(when: kDebugMode);
ErrorHandler.logError(
m: "igcTextData fullText does not match current text",
s: StackTrace.current,
data: igcTextData!.toJson(),
);
}
if (choreographer.l1LangCode == null ||
choreographer.l2LangCode == null) {
debugger(when: kDebugMode);
ErrorHandler.logError(
m: "l1LangCode and/or l2LangCode is null",
s: StackTrace.current,
data: {
"l1LangCode": choreographer.l1LangCode,
"l2LangCode": choreographer.l2LangCode,
},
);
return;
}
final TokensResponseModel res = await TokensRepo.tokenize(
await choreographer.pangeaController.userController.accessToken,
TokensRequestModel(
fullText: igcTextData!.originalInput,
userL1: choreographer.l1LangCode!,
userL2: choreographer.l2LangCode!,
),
);
igcTextData?.tokens = res.tokens;
} catch (err, stack) {
debugger(when: kDebugMode);
choreographer.errorService.setError(
ChoreoError(type: ChoreoErrorType.unknown, raw: err),
);
Sentry.addBreadcrumb(
Breadcrumb.fromJson({"igctextDdata": igcTextData?.toJson()}),
);
ErrorHandler.logError(e: err, s: stack);
} finally {
igcTextData?.loading = false;
choreographer.stopLoading();
}
}
void showFirstMatch(BuildContext context) {
if (igcTextData == null || igcTextData!.matches.isEmpty) {
debugger(when: kDebugMode);

View file

@ -1,14 +1,12 @@
import 'dart:developer';
import 'package:collection/collection.dart';
import 'package:fluffychat/pangea/controllers/language_detection_controller.dart';
import 'package:fluffychat/pangea/models/pangea_match_model.dart';
import 'package:fluffychat/pangea/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/models/span_card_model.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:fluffychat/pangea/utils/overlay.dart';
import 'package:fluffychat/pangea/widgets/igc/span_card.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
@ -262,7 +260,20 @@ class IGCTextData {
return matchTokens;
}
TextSpan getSpanItem({
required int start,
required int end,
TextStyle? style,
}) {
return TextSpan(
text: originalInput.characters.getRange(start, end).toString(),
style: style,
);
}
//PTODO - handle multitoken spans
/// Returns a list of [TextSpan]s used to display the text in the input field
/// with the appropriate styling for each error match.
List<TextSpan> constructTokenSpan({
required BuildContext context,
TextStyle? defaultStyle,
@ -282,79 +293,58 @@ class IGCTextData {
];
}
final List<MatchToken> matchTokens = getMatchTokens();
final List<List<int>> matchRanges = matches
.map(
(match) => [
match.match.offset,
match.match.length + match.match.offset,
],
)
.toList();
for (int tokenIndex = 0; tokenIndex < matchTokens.length; tokenIndex++) {
final MatchToken matchToken = matchTokens[tokenIndex];
final Widget? cardToShow =
matchToken.match != null && spanCardModel != null
? SpanCard(scm: spanCardModel)
: null;
int nextTokenIndex = matchTokens.indexWhere(
(e) => matchToken.match != null
? e.match != matchToken.match
: e.match != null,
tokenIndex,
// create a pointer to the current index in the original input
// and iterate until the pointer has reached the end of the input
int currentIndex = 0;
while (currentIndex < originalInput.characters.length - 1) {
// check if the pointer is at a match, and if so, get the index of the match
final int matchIndex = matchRanges.indexWhere(
(range) => currentIndex >= range[0] && currentIndex < range[1],
);
final bool inMatch = matchIndex != -1;
if (nextTokenIndex < 0) {
nextTokenIndex = matchTokens.length;
}
String matchText;
try {
final int start = matchTokens[tokenIndex].token.text.offset;
final int end = matchTokens[nextTokenIndex - 1].token.end;
matchText = originalInput.characters.getRange(start, end).toString();
} catch (err) {
return [
TextSpan(
text: originalInput,
style: defaultStyle,
),
];
}
items.add(
TextSpan(
text: matchText,
style: matchTokens[tokenIndex].match?.textStyle(defaultStyle) ??
defaultStyle,
recognizer: handleClick && cardToShow != null
? (TapGestureRecognizer()
..onTapDown = (details) => OverlayUtil.showPositionedCard(
context: context,
cardToShow: cardToShow,
cardSize:
matchTokens[tokenIndex].match?.isITStart ?? false
? const Size(350, 220)
: const Size(350, 400),
transformTargetId: transformTargetId,
))
: null,
),
);
final String beforeNextToken = originalInput.characters
.getRange(
matchTokens[nextTokenIndex - 1].token.end,
nextTokenIndex < matchTokens.length
? matchTokens[nextTokenIndex].token.text.offset
: originalInput.length,
)
.toString();
if (beforeNextToken.isNotEmpty) {
if (inMatch) {
// if the pointer is in a match, then add that match to items
// and then move the pointer to the end of the match range
final PangeaMatch match = matches[matchIndex];
items.add(
TextSpan(
text: beforeNextToken,
getSpanItem(
start: match.match.offset,
end: match.match.offset + match.match.length,
style: match.textStyle(defaultStyle),
),
);
currentIndex = match.match.offset + match.match.length;
} else {
// otherwise, if the pointer is not at a match, then add all the text
// until the next match (or, if there is not next match, the end of the
// text) to items and move the pointer to the start of the next match
final int nextIndex = matchRanges
.firstWhereOrNull(
(range) => range[0] > currentIndex,
)
?.first ??
originalInput.characters.length;
items.add(
getSpanItem(
start: currentIndex,
end: nextIndex,
style: defaultStyle,
),
);
currentIndex = nextIndex;
}
tokenIndex = nextTokenIndex - 1;
}
return items;