Skip to content
Merged
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
14 changes: 2 additions & 12 deletions lib/code_forge/code_area.dart
Original file line numberDiff line numberDiff line change
Expand Up@@ -1036,19 +1036,9 @@ class _CodeForgeState extends State<CodeForge> with TickerProviderStateMixin {
void didUpdateWidget(covariant CodeForge oldWidget) {
super.didUpdateWidget(oldWidget);

final oldTabSize = oldWidget.tabSize ?? (oldWidget.useSpaceAsTab ? 2 : 1);
final newTabSize = widget.tabSize ?? (widget.useSpaceAsTab ? 2 : 1);
final newUseSpaceAsTab = widget.useSpaceAsTab;

if (oldWidget._tabSize != widget._tabSize ||
oldWidget.useSpaceAsTab != widget.useSpaceAsTab) {
_controller.tabSize = newTabSize;
_controller.useSpaceAsTab = newUseSpaceAsTab;
_controller.reindentDocument(
oldTabSize: oldTabSize,
newTabSize: newTabSize,
);
}
_controller.useSpaceAsTab = widget.useSpaceAsTab;
_controller.tabSize = newTabSize;
}

@override
Expand Down
154 changes: 104 additions & 50 deletions lib/code_forge/controller.dart
Original file line numberDiff line numberDiff line change
Expand Up@@ -4076,8 +4076,12 @@ class CodeForgeController implements DeltaTextInputClient {
notifyListeners();
}

void reindentDocument({required int oldTabSize, required int newTabSize}) {
// Handel Tab Size
void reindentDocument({required int newTabSize}) {
final oldTabSize = detectIndentation();

if (oldTabSize <= 0 || newTabSize <= 0 || oldTabSize == newTabSize) {
tabSize = newTabSize;
return;
}

Expand All@@ -4092,91 +4096,141 @@ class CodeForgeController implements DeltaTextInputClient {
final newLines = <String>[];

for (final line in originalLines) {
final leadingWhitespace =
RegExp(r"^[ \t]+", multiLine: true).stringMatch(line) ?? "";
final match = RegExp(r"^[ \t]+").firstMatch(line);
if (match == null) {
newLines.add(line);
continue;
}

final leadingWhitespace = match.group(0)!;
final content = line.substring(leadingWhitespace.length);
final indentLength = leadingWhitespace
.replaceAll("\t", " " * oldTabSize)
.length;
final indentLevels = indentLength ~/ oldTabSize;
final newIndentLength = indentLevels * newTabSize;
newLines.add("${" " * newIndentLength}$content");

int visualSpaces = 0;
for (int i = 0; i < leadingWhitespace.length; i++) {
if (leadingWhitespace[i] == '\t') {
visualSpaces += oldTabSize - (visualSpaces % oldTabSize);
} else {
visualSpaces += 1;
}
}

final indentLevels = visualSpaces ~/ oldTabSize;
final remainder = visualSpaces % oldTabSize;
final newIndentLength = (indentLevels * newTabSize) + remainder;

newLines.add("${' ' * newIndentLength}$content");
}

final newText = newLines.join("\n");
if (newText == originalText) {
tabSize = newTabSize;
return;
}
tabSize = newTabSize;

if (newText == originalText) return;

final newLineStarts = _lineStarts(newText);
final originalLineStarts = _lineStarts(originalText);

final newSelection = TextSelection(
final mappedSelection = TextSelection(
baseOffset: _mapOffsetForReindent(
originalOffset: originalSelection.start,
originalOffset: originalSelection.baseOffset,
originalText: originalText,
newText: newText,
oldTabSize: oldTabSize,
newTabSize: newTabSize,
originalLineStarts: originalLineStarts,
newLineStarts: newLineStarts,
),
extentOffset: _mapOffsetForReindent(
originalOffset: originalSelection.end,
originalOffset: originalSelection.extentOffset,
originalText: originalText,
newText: newText,
oldTabSize: oldTabSize,
newTabSize: newTabSize,
originalLineStarts: originalLineStarts,
newLineStarts: newLineStarts,
),
);

text = newText;
selection = newSelection;
tabSize = newTabSize;
selection = mappedSelection;
}

// Detect File Tab Size
int detectIndentation() {
final lines = text.split("\n");
final diffCount = <int, int>{};
int previousIndent = 0;

for (final line in lines) {
if (line.trim().isEmpty) continue;

final match = RegExp(r"^( +)\S").firstMatch(line);
final currentIndent = match?.group(1)?.length ?? 0;

final diff = (currentIndent - previousIndent).abs();
if (diff >= 1 && diff <= 32) {
diffCount[diff] = (diffCount[diff] ?? 0) + 1;
}

previousIndent = currentIndent;
}

if (diffCount.isEmpty) return tabSize > 0 ? tabSize : 2;

int bestIndent = tabSize;
int maxOccurrences = 0;

diffCount.forEach((indent, count) {
if (count > maxOccurrences) {
maxOccurrences = count;
bestIndent = indent;
}
});

return bestIndent;
}

int _mapOffsetForReindent({
required int originalOffset,
required String originalText,
required String newText,
required int oldTabSize,
required int newTabSize,
required List<int> originalLineStarts,
required List<int> newLineStarts,
}) {
if (originalOffset <= 0) return 0;
if (originalOffset >= originalText.length) return newText.length;

final lineIndex = _findLineIndex(originalLineStarts, originalOffset);
final lineStart = originalLineStarts[lineIndex];
final lineText = originalText.substring(
lineStart,
lineIndex + 1 < originalLineStarts.length
? originalLineStarts[lineIndex + 1] - 1
: originalText.length,
final originalLineStart = originalLineStarts[lineIndex];
final originalNextLineStart = lineIndex + 1 < originalLineStarts.length
? originalLineStarts[lineIndex + 1]
: originalText.length;

final originalLineText = originalText.substring(
originalLineStart,
originalNextLineStart,
);
final leadingWhitespace =
RegExp(r"^[ \t]+", multiLine: true).stringMatch(lineText) ?? "";
final oldIndentLength = leadingWhitespace
.replaceAll("\t", " " * oldTabSize)
.length;
final newIndentLength = (oldIndentLength ~/ oldTabSize) * newTabSize;
final column = originalOffset - lineStart;

if (newLineStarts.length <= lineIndex) {
return newText.length;
}

final newLineStart = newLineStarts[lineIndex];
final newColumn = column <= oldIndentLength
? (oldIndentLength == 0
? 0
: ((column * newIndentLength) / oldIndentLength).round())
: newIndentLength + (column - oldIndentLength);

return newLineStart + newColumn.clamp(0, newText.length - newLineStart);
final originalWhitespaceLen =
RegExp(r"^[ \t]+").firstMatch(originalLineText)?.group(0)?.length ?? 0;

final newLineStart = lineIndex < newLineStarts.length
? newLineStarts[lineIndex]
: newText.length;
final newNextLineStart = lineIndex + 1 < newLineStarts.length
? newLineStarts[lineIndex + 1]
: newText.length;

final newLineText = newText.substring(newLineStart, newNextLineStart);
final newWhitespaceLen =
RegExp(r"^[ \t]+").firstMatch(newLineText)?.group(0)?.length ?? 0;

final column = originalOffset - originalLineStart;

int newColumn;
if (column <= originalWhitespaceLen) {
newColumn = originalWhitespaceLen == 0
? 0
: ((column / originalWhitespaceLen) * newWhitespaceLen).round();
} else {
newColumn = newWhitespaceLen + (column - originalWhitespaceLen);
}

return (newLineStart + newColumn).clamp(0, newText.length);
}

List<int> _lineStarts(String value) {
Expand Down