update method
Updates the component state in response to a message.
Returns the updated component (often this) and an optional command.
Implementation
@override
(TextAreaModel, Cmd?) update(Msg msg) {
return _runEditFrame(() {
switch (msg) {
case TextAreaPasteMsg(:final content):
_beginHistoryAction(_TextAreaHistoryAction.paste, breakChain: true);
return (this, _pasteContent(content));
case PasteMsg(:final content):
_beginHistoryAction(_TextAreaHistoryAction.paste, breakChain: true);
return (this, _pasteContent(content));
case PasteTextMsg(:final content):
_beginHistoryAction(_TextAreaHistoryAction.paste, breakChain: true);
return (this, _pasteContent(content));
case _TextAreaPasteChunkMsg():
_beginHistoryAction(_TextAreaHistoryAction.paste);
_applyNextPasteChunk();
if (_pasteController.hasPendingChunkedPaste) {
return (this, _schedulePasteChunk());
}
return (this, null);
case TextAreaCompletionMsg(:final result):
if (result == null) return (this, null);
final maxItems = workAssessment.maxCompletionItems;
final items = result.items.length > maxItems
? result.items.take(maxItems).toList(growable: false)
: result.items;
_completionItems = List<EditorCompletionItem>.unmodifiable(items);
_completionIndex = _completionItems.isEmpty ? -1 : 0;
_completionError = null;
return (this, null);
case TextAreaCompletionErrorMsg(:final error):
_completionItems = const [];
_completionIndex = -1;
_completionError = error;
return (this, null);
case TextAreaCodeActionsMsg(:final actions):
if (actions == null) return (this, null);
_codeActions = List<EditorCodeAction>.unmodifiable(actions);
_codeActionIndex = _codeActions.isEmpty ? -1 : 0;
_codeActionError = null;
return (this, null);
case TextAreaCodeActionsErrorMsg(:final error):
_codeActions = const [];
_codeActionIndex = -1;
_codeActionError = error;
return (this, null);
case KeyMsg(key: final key):
if (completionVisible) {
switch (key.type) {
case KeyType.up:
moveCompletionSelection(-1);
return (this, null);
case KeyType.down:
moveCompletionSelection(1);
return (this, null);
case KeyType.tab || KeyType.enter:
acceptCompletion();
return (this, null);
case KeyType.escape:
cancelCompletions();
return (this, null);
default:
break;
}
}
if (key.matchesSingle(keyMap.undo)) {
executeCommand(EditorCommandIds.undo);
return (this, null);
}
if (key.matchesSingle(keyMap.redo)) {
executeCommand(EditorCommandIds.redo);
return (this, null);
}
if (key.matchesSingle(keyMap.selectAll)) {
executeCommand(EditorCommandIds.selectAll);
return (this, null);
}
if (key.matchesSingle(keyMap.selectLine)) {
executeCommand(EditorCommandIds.selectLine);
return (this, null);
}
// deletion
if (key.matchesSingle(keyMap.deleteBeforeCursor)) {
executeCommand(EditorCommandIds.deleteLeft);
return (this, null);
}
if (key.matchesSingle(keyMap.deleteCharacterForward)) {
executeCommand(EditorCommandIds.deleteRight);
return (this, null);
}
if (key.matchesSingle(keyMap.deleteWordBackward)) {
executeCommand(EditorCommandIds.deleteWordLeft);
return (this, null);
}
if (key.matchesSingle(keyMap.deleteWordForward)) {
executeCommand(EditorCommandIds.deleteWordRight);
return (this, null);
}
if (key.matchesSingle(keyMap.deleteToLineStart)) {
executeCommand(EditorCommandIds.deleteLineLeft);
return (this, null);
}
if (key.matchesSingle(keyMap.deleteToLineEnd)) {
executeCommand(EditorCommandIds.deleteLineRight);
return (this, null);
}
if (key.matchesSingle(keyMap.deleteAfterCursor)) {
executeCommand(EditorCommandIds.deleteLineRight);
return (this, null);
}
// navigation
if (_matchesMovementBinding(key, keyMap.wordForward)) {
executeCommand(
key.shift
? EditorCommandIds.selectWordRight
: EditorCommandIds.cursorWordRight,
);
return (this, null);
}
if (_matchesMovementBinding(key, keyMap.wordBackward)) {
executeCommand(
key.shift
? EditorCommandIds.selectWordLeft
: EditorCommandIds.cursorWordLeft,
);
return (this, null);
}
if (_matchesMovementBinding(key, keyMap.lineStart)) {
final visual = key.type == KeyType.home;
late final String commandId;
if (key.shift) {
commandId = visual
? EditorCommandIds.selectVisualLineStart
: EditorCommandIds.selectLineStart;
} else {
commandId = visual
? EditorCommandIds.cursorVisualLineStart
: EditorCommandIds.cursorLineStart;
}
executeCommand(commandId);
return (this, null);
}
if (_matchesMovementBinding(key, keyMap.lineEnd)) {
final visual = key.type == KeyType.end;
late final String commandId;
if (key.shift) {
commandId = visual
? EditorCommandIds.selectVisualLineEnd
: EditorCommandIds.selectLineEnd;
} else {
commandId = visual
? EditorCommandIds.cursorVisualLineEnd
: EditorCommandIds.cursorLineEnd;
}
executeCommand(commandId);
return (this, null);
}
if (_matchesMovementBinding(key, keyMap.inputBegin)) {
executeCommand(
key.shift
? EditorCommandIds.selectDocumentStart
: EditorCommandIds.cursorDocumentStart,
);
return (this, null);
}
if (_matchesMovementBinding(key, keyMap.inputEnd)) {
executeCommand(
key.shift
? EditorCommandIds.selectDocumentEnd
: EditorCommandIds.cursorDocumentEnd,
);
return (this, null);
}
if (_matchesMovementBinding(key, keyMap.characterForward)) {
executeCommand(
key.shift
? EditorCommandIds.selectRight
: EditorCommandIds.cursorRight,
);
return (this, null);
}
if (_matchesMovementBinding(key, keyMap.characterBackward)) {
executeCommand(
key.shift
? EditorCommandIds.selectLeft
: EditorCommandIds.cursorLeft,
);
return (this, null);
}
if (_matchesMovementBinding(key, keyMap.lineNext)) {
executeCommand(
key.shift
? EditorCommandIds.selectDown
: EditorCommandIds.cursorDown,
);
return (this, null);
}
if (_matchesMovementBinding(key, keyMap.linePrevious)) {
executeCommand(
key.shift ? EditorCommandIds.selectUp : EditorCommandIds.cursorUp,
);
return (this, null);
}
if (_matchesMovementBinding(key, keyMap.pageUp)) {
executeCommand(
key.shift
? EditorCommandIds.selectPageUp
: EditorCommandIds.cursorPageUp,
);
return (this, null);
}
if (_matchesMovementBinding(key, keyMap.pageDown)) {
executeCommand(
key.shift
? EditorCommandIds.selectPageDown
: EditorCommandIds.cursorPageDown,
);
return (this, null);
}
if (key.matchesSingle(keyMap.transposeCharacterBackward)) {
executeCommand(EditorCommandIds.transposeCharacters);
return (this, null);
}
if (key.matchesSingle(keyMap.uppercaseWordForward)) {
executeCommand(EditorCommandIds.uppercaseWord);
return (this, null);
}
if (key.matchesSingle(keyMap.lowercaseWordForward)) {
executeCommand(EditorCommandIds.lowercaseWord);
return (this, null);
}
if (key.matchesSingle(keyMap.capitalizeWordForward)) {
executeCommand(EditorCommandIds.capitalizeWord);
return (this, null);
}
if (key.matchesSingle(keyMap.copy)) {
final text = getSelectedText();
if (text.isNotEmpty) {
return (this, Cmd.setClipboardBestEffort(text));
}
}
// Fallback direct modifier checks for common combos.
if (key.type == KeyType.delete && key.alt) {
executeCommand(EditorCommandIds.deleteWordRight);
return (this, null);
}
if (key.ctrl && key.type == KeyType.runes && key.runes.isNotEmpty) {
final r = key.runes.first;
if (r == 0x74) {
// ctrl+t
executeCommand(EditorCommandIds.transposeCharacters);
return (this, null);
}
}
if (key.alt && key.type == KeyType.runes && key.runes.isNotEmpty) {
final r = key.runes.first;
if (r == 0x75) {
executeCommand(EditorCommandIds.uppercaseWord);
return (this, null);
}
if (r == 0x6c) {
executeCommand(EditorCommandIds.lowercaseWord);
return (this, null);
}
if (r == 0x63) {
executeCommand(EditorCommandIds.capitalizeWord);
return (this, null);
}
}
if (key.type == KeyType.tab) {
if (key.shift) {
executeCommand(EditorCommandIds.outdentLines);
} else {
executeCommand(EditorCommandIds.indentLines);
}
return (this, null);
}
if (key.type == KeyType.space) {
executeCommand(EditorCommandIds.insertText, argument: ' ');
return (this, null);
}
if (key.type == KeyType.enter && keyMap.insertNewline.enabled) {
executeCommand(EditorCommandIds.insertLineBreak);
return (this, null);
}
if (key.type == KeyType.runes && key.runes.isNotEmpty) {
final rune = key.runes.first;
if (rune == 0x0a) {
executeCommand(EditorCommandIds.insertLineBreak);
} else {
executeCommand(
EditorCommandIds.insertText,
argument: String.fromCharCode(rune),
);
}
return (this, null);
}
}
if (msg is MouseMsg) {
final lineNumberDigits = _lineNumberDigits;
final displayLines = _softWrappedLines(lineNumberDigits);
final action = msg.action;
final button = msg.button;
final x = msg.x;
final y = msg.y;
if (y < 0 || y >= displayLines.length) {
if (action == MouseAction.press && button == MouseButton.left) {
_mouseSelecting = false;
_clearLineSelection();
_focused = false;
_syncCoreState();
}
if (action == MouseAction.release && button == MouseButton.left) {
_mouseSelecting = false;
if (!_hasSelection()) {
_clearLineSelection();
_syncCoreState();
}
}
return (this, null);
}
if (action == MouseAction.press && button == MouseButton.left) {
_focused = true;
final promptW = _getPromptWidth(y);
final lineNumberW = showLineNumbers ? (lineNumberDigits + 1) : 0;
final displayLine = displayLines[y];
final inLineNumberGutter =
showLineNumbers &&
x >= promptW &&
x < promptW + lineNumberW &&
displayLine.charOffset == 0;
if (inLineNumberGutter &&
selectDiagnosticAtLine(displayLine.rowIndex)) {
_mouseSelecting = false;
return (this, null);
}
final hit = _textView.hitTestContent(
_document,
_editorState,
localX: x - promptW - lineNumberW,
visualRow: y,
);
if (hit == null) {
_mouseSelecting = false;
return (this, null);
}
final contentX = hit.column;
final contentY = hit.line;
final now = _nowProvider();
final clickCount =
_lastClickTime != null &&
now.difference(_lastClickTime!) <
const Duration(milliseconds: 500) &&
_lastClickPos == (contentX, contentY)
? (_lastClickCount + 1).clamp(1, 3)
: 1;
_lastClickTime = now;
_lastClickPos = (contentX, contentY);
_lastClickCount = clickCount;
if (clickCount == 2) {
_mouseSelecting = false;
final (start, end) = _findWordAt(contentX, contentY);
_selectLineState(
base: TextPosition(line: contentY, column: start),
extent: TextPosition(line: contentY, column: end),
);
_syncCoreState();
return (this, null);
}
if (clickCount >= 3) {
_mouseSelecting = false;
_selectLineState(
base: TextPosition(line: contentY, column: 0),
extent: TextPosition(
line: contentY,
column: _document.lineLength(contentY),
),
);
_syncCoreState();
return (this, null);
}
// Start selection
_mouseSelecting = true;
_selectLineState(
base: TextPosition(line: contentY, column: contentX),
extent: TextPosition(line: contentY, column: contentX),
preserveCollapsedSelection: true,
);
_syncCoreState();
return (this, null);
}
if (action == MouseAction.motion &&
_mouseSelecting &&
_selectionStart != null) {
final promptW = _getPromptWidth(y);
final lineNumberW = showLineNumbers ? (lineNumberDigits + 1) : 0;
final hit = _textView.hitTestContent(
_document,
_editorState,
localX: x - promptW - lineNumberW,
visualRow: y,
);
if (hit == null) {
return (this, null);
}
final contentX = hit.column;
final contentY = hit.line;
_selectLineState(
base: TextPosition(
line: _selectionStart!.$2,
column: _selectionStart!.$1,
),
extent: TextPosition(line: contentY, column: contentX),
);
_syncCoreState();
return (this, null);
}
if (action == MouseAction.release && button == MouseButton.left) {
_mouseSelecting = false;
if (!_hasSelection()) {
_clearLineSelection();
_syncCoreState();
}
return (this, null);
}
}
return (this, null);
});
}