better documentation

This commit is contained in:
ggurdin 2025-11-06 11:28:37 -05:00
parent 14b89594c2
commit ab8387c522
No known key found for this signature in database
GPG key ID: A01CB41737CBB478
5 changed files with 67 additions and 95 deletions

View file

@ -407,7 +407,7 @@ class Choreographer extends ChangeNotifier {
void clearMatches(Object error) {
MatrixState.pAnyState.closeAllOverlays();
igcController.clearMatches();
igcController.clearIGCMatches();
errorService.setError(ChoreoError(raw: error));
}

View file

@ -1,5 +1,4 @@
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
import 'package:fluffychat/pangea/choreographer/controllers/extensions/choregrapher_user_settings_extension.dart';
import 'package:fluffychat/pangea/choreographer/enums/assistance_state_enum.dart';
import 'package:fluffychat/pangea/choreographer/enums/choreo_mode.dart';
@ -28,38 +27,4 @@ extension ChoregrapherUserSettingsExtension on Choreographer {
if (!igcController.hasIGCTextData) return AssistanceState.notFetched;
return AssistanceState.complete;
}
bool get canSendMessage {
// if there's an error, let them send. we don't want to block them from sending in this case
if (errorService.isError ||
l2Lang == null ||
l1Lang == null ||
timesClicked > 1) {
return true;
}
// if they're in IT mode, don't let them send
if (itEnabled && isRunningIT) return false;
// if they've turned off IGC then let them send the message when they want
if (!isAutoIGCEnabled) return true;
// if we're in the middle of fetching results, don't let them send
if (isFetching.value) return false;
// they're supposed to run IGC but haven't yet, don't let them send
if (!igcController.hasIGCTextData) {
return itController.dismissed;
}
// if they have relevant matches, don't let them send
final hasITMatches = igcController.hasOpenITMatches;
final hasIGCMatches = igcController.hasOpenIGCMatches;
if ((itEnabled && hasITMatches) || (igcEnabled && hasIGCMatches)) {
return false;
}
// otherwise, let them send
return true;
}
}

View file

@ -23,10 +23,8 @@ class IgcController {
String? get currentText => _igcTextData?.currentText;
bool get hasOpenMatches => _igcTextData?.hasOpenMatches == true;
bool get hasOpenITMatches => _igcTextData?.hasOpenITMatches == true;
bool get hasOpenIGCMatches => _igcTextData?.hasOpenIGCMatches == true;
PangeaMatchState? get openMatch => _igcTextData?.openMatch;
PangeaMatchState? get currentlyOpenMatch => _igcTextData?.currentlyOpenMatch;
PangeaMatchState? get firstOpenMatch => _igcTextData?.firstOpenMatch;
List<PangeaMatchState>? get openMatches => _igcTextData?.openMatches;
List<PangeaMatchState>? get recentNormalizationMatches =>
@ -43,10 +41,10 @@ class IgcController {
MatrixState.pAnyState.closeAllOverlays();
}
void clearMatches() => _igcTextData?.clearMatches();
void clearIGCMatches() => _igcTextData?.clearIGCMatches();
PangeaMatchState? getMatchByOffset(int offset) =>
_igcTextData?.getMatchByOffset(offset);
_igcTextData?.getOpenMatchByOffset(offset);
PangeaMatch acceptReplacement(
PangeaMatchState match,
@ -55,7 +53,7 @@ class IgcController {
if (_igcTextData == null) {
throw "acceptReplacement called with null igcTextData";
}
final updateMatch = _igcTextData!.acceptReplacement(match, status);
final updateMatch = _igcTextData!.makeAcceptedMatchUpdates(match, status);
return updateMatch;
}
@ -64,14 +62,14 @@ class IgcController {
if (_igcTextData == null) {
throw "should not be in onIgnoreMatch with null igcTextData";
}
return _igcTextData!.ignoreReplacement(match);
return _igcTextData!.makeIgnoredMatchUpdates(match);
}
void undoReplacement(PangeaMatchState match) {
if (_igcTextData == null) {
throw "undoReplacement called with null igcTextData";
}
_igcTextData!.undoReplacement(match);
_igcTextData!.removeMatchUpdates(match);
}
Future<void> getIGCTextData(

View file

@ -201,7 +201,7 @@ class PangeaTextController extends TextEditingController {
}
final openMatch =
choreographer.igcController.openMatch?.updatedMatch.match;
choreographer.igcController.currentlyOpenMatch?.updatedMatch.match;
final style = _textStyle(
match.updatedMatch,
defaultStyle,

View file

@ -9,45 +9,47 @@ import 'package:fluffychat/pangea/choreographer/models/span_data.dart';
import 'package:fluffychat/pangea/choreographer/repo/igc_repo.dart';
import 'package:fluffychat/widgets/matrix.dart';
/// A model representing the mutable text and match state used by
/// Interactive Grammar Correction (IGC).
///
/// This class tracks the original input text, the current working text,
/// and the states of open and closed grammar matches as the user accepts,
/// ignores, or reverses suggested corrections.
class IGCTextData {
/// The user's original text before any corrections or replacements.
final String _originalInput;
/// The full list of detected matches from the initial grammar analysis.
final List<PangeaMatch> _matches;
String _currentText;
/// Matches currently pending user action (neither accepted nor ignored).
final List<PangeaMatchState> _openMatches = [];
/// Matches that have been resolved by either accepting or ignoring them.
final List<PangeaMatchState> _closedMatches = [];
/// The current text content after applying all accepted corrections.
String _currentText;
IGCTextData({
required String originalInput,
required List<PangeaMatch> matches,
}) : _currentText = originalInput,
_originalInput = originalInput,
_matches = matches {
_openMatches.addAll(
matches
.where((match) => match.status == PangeaMatchStatus.open)
.map((match) {
return PangeaMatchState(
match: match.match,
status: match.status,
original: match,
);
}),
);
_closedMatches.addAll(
matches
.where((match) => match.status != PangeaMatchStatus.open)
.map((match) {
return PangeaMatchState(
match: match.match,
status: match.status,
original: match,
);
}),
);
_filterIgnoredMatches();
for (final match in matches) {
final matchState = PangeaMatchState(
match: match.match,
status: PangeaMatchStatus.open,
original: match,
);
if (match.status == PangeaMatchStatus.open) {
_openMatches.add(matchState);
} else {
_closedMatches.add(matchState);
}
}
_filterPreviouslyIgnoredMatches();
}
Map<String, dynamic> toJson() => {
@ -59,35 +61,33 @@ class IGCTextData {
List<PangeaMatchState> get openMatches => _openMatches;
List<PangeaMatchState> get closedMatches => _closedMatches;
bool get hasOpenMatches => _openMatches.isNotEmpty;
PangeaMatchState? get firstOpenMatch => _openMatches.firstOrNull;
/// Normalization matches that have been closed in the last choreo step(s).
/// Used to display automatic corrections made by the IGC system.
List<PangeaMatchState> get recentNormalizationMatches =>
closedMatches.reversed
_closedMatches.reversed
.takeWhile(
(m) => m.updatedMatch.status == PangeaMatchStatus.automatic,
)
.toList();
/// Convenience getter for open normalization error matches.
/// Used for auto-correction of normalization errors.
List<PangeaMatchState> get openNormalizationMatches => _openMatches
.where((match) => match.updatedMatch.match.isNormalizationError())
.toList();
bool get hasOpenMatches => _openMatches.isNotEmpty;
bool get hasOpenITMatches =>
_openMatches.any((match) => match.updatedMatch.isITStart);
bool get hasOpenIGCMatches =>
_openMatches.any((match) => !match.updatedMatch.isITStart);
PangeaMatchState? get firstOpenMatch => _openMatches.firstOrNull;
PangeaMatchState? getMatchByOffset(int offset) =>
/// Returns the open match that contains the given text offset, if any.
PangeaMatchState? getOpenMatchByOffset(int offset) =>
_openMatches.firstWhereOrNull(
(match) => match.updatedMatch.match.isOffsetInMatchSpan(offset),
);
PangeaMatchState? get openMatch {
/// Returns the match whose span card overlay is currently open, if any.
PangeaMatchState? get currentlyOpenMatch {
final RegExp pattern = RegExp(r'span_card_overlay_.+');
final String? matchingKeys =
MatrixState.pAnyState.getMatchingOverlayKeys(pattern).firstOrNull;
@ -105,19 +105,23 @@ class IGCTextData {
);
}
void clearMatches() {
/// Clears all matches from the IGC text data.
/// Call on error that make continuing IGC processing invalid.
void clearIGCMatches() {
_openMatches.clear();
_closedMatches.clear();
}
void _filterIgnoredMatches() {
/// Filters out previously ignored matches from the open matches list.
void _filterPreviouslyIgnoredMatches() {
for (final match in _openMatches) {
if (IgcRepo.isIgnored(match.updatedMatch)) {
ignoreReplacement(match);
makeIgnoredMatchUpdates(match);
}
}
}
/// Replaces the span data for a given match.
void setSpanData(PangeaMatchState match, SpanData spanData) {
final openMatch = _openMatches.firstWhereOrNull(
(m) => m.originalMatch == match.originalMatch,
@ -128,7 +132,9 @@ class IGCTextData {
_openMatches.add(match);
}
PangeaMatch acceptReplacement(
/// Accepts the specified [match] and updates both the open/closed match lists
/// and the [_currentText] to include the chosen replacement text.
PangeaMatch makeAcceptedMatchUpdates(
PangeaMatchState match,
PangeaMatchStatus status,
) {
@ -158,11 +164,12 @@ class IGCTextData {
return match.updatedMatch;
}
PangeaMatch ignoreReplacement(PangeaMatchState match) {
/// Ignores a given match and updates the IGC text data state accordingly.
PangeaMatch makeIgnoredMatchUpdates(PangeaMatchState match) {
final openMatch = _openMatches.firstWhere(
(m) => m.originalMatch == match.originalMatch,
orElse: () => throw Exception(
'No open match found for ignoreReplacement',
'No open match found for makeIgnoredMatchUpdates',
),
);
@ -172,21 +179,22 @@ class IGCTextData {
return match.updatedMatch;
}
void undoReplacement(PangeaMatchState match) {
/// Removes a given match from the closed match history and undoes the
/// changes to igc text data state caused by accepting the match.
void removeMatchUpdates(PangeaMatchState match) {
final closedMatch = _closedMatches.firstWhere(
(m) => m.originalMatch == match.originalMatch,
orElse: () => throw Exception(
'No closed match found for undoReplacement',
'No closed match found for removeMatchUpdates',
),
);
_closedMatches.remove(closedMatch);
final choice = match.updatedMatch.match.selectedChoice?.value;
if (choice == null) {
throw Exception(
"match.match.selectedChoice is null in undoReplacement",
"match.match.selectedChoice is null in removeMatchUpdates",
);
}
@ -204,6 +212,7 @@ class IGCTextData {
);
}
/// Runs a text replacement and updates match offsets / current text accordingly.
void _runReplacement(
int offset,
int length,