insertNewlineAndIndent function
Replace the selection with a newline and compute smart indentation.
If the current line consists only of whitespace, this will also delete that whitespace. When the cursor is between matching brackets, an additional newline will be inserted after the cursor.
Implementation
bool insertNewlineAndIndent(StateCommandTarget target) {
print('insertNewlineAndIndent called! pos=${target.state.selection.main.head}');
if (target.state.isReadOnly) return false;
final state = target.state;
final result = state.changeByRange((range) {
var from = range.from;
var to = range.to;
final line = state.doc.lineAt(from);
// Check if cursor is between brackets like { | } or ( | )
final explode = from == to ? _isBetweenBrackets(state, from) : null;
// Create indent context with simulated break
final cx = IndentContext(
state,
options: IndentContextOptions(
simulateBreak: from,
simulateDoubleBreak: explode != null,
),
);
// Get smart indentation
var indent = getIndentation(cx, from);
if (indent == null) {
// Fall back to current line's indentation
final lineIndentMatch = RegExp(r'^\s*').firstMatch(line.text);
indent = countColumn(lineIndentMatch?.group(0) ?? '', state.tabSize);
}
// Delete trailing whitespace on current line (with bounds check)
while (to < line.to && (to - line.from) < line.text.length) {
final char = line.text[to - line.from];
if (char != ' ' && char != '\t') break;
to++;
}
// If cursor is in explosion zone, use that range
if (explode != null) {
from = explode.from;
to = explode.to;
} else if (from > line.from && from < line.from + 100 && (from - line.from) <= line.text.length) {
// Delete leading whitespace on whitespace-only lines
final beforeCursor = line.text.substring(0, from - line.from);
if (!RegExp(r'\S').hasMatch(beforeCursor)) {
from = line.from;
}
}
// Build the insert text
final indentStr = indentString(state, indent);
String insertText;
int cursorPos;
if (explode != null) {
// Between brackets: add two newlines with proper indentation
int outerIndentCol;
try {
final cx = IndentContext(state);
outerIndentCol = cx.lineIndent(line.from, -1);
} catch (_) {
outerIndentCol = 0;
}
final outerIndent = indentString(state, outerIndentCol);
insertText = '${state.lineBreak}$indentStr${state.lineBreak}$outerIndent';
cursorPos = from + 1 + indentStr.length;
} else {
insertText = '${state.lineBreak}$indentStr';
cursorPos = from + 1 + indentStr.length;
}
return ChangeByRangeResult(
changes: [ChangeSpec(from: from, to: to, insert: insertText)],
range: EditorSelection.cursor(cursorPos),
);
});
target.dispatch(state.update([
TransactionSpec(
changes: result.changes,
selection: result.selection,
scrollIntoView: true,
userEvent: 'input',
),
]));
return true;
}