Repository: akvelon/flutter-code-editor (dart)

Issue:
Indent after pressing Enter after ':' in Python

Pull request description:
- Resolves #135

Patch:
diff --git a/lib/src/code_modifiers/indent_code_modifier.dart b/lib/src/code_modifiers/indent_code_modifier.dart
index 38c461a1..6ad610df 100644
--- a/lib/src/code_modifiers/indent_code_modifier.dart
+++ b/lib/src/code_modifiers/indent_code_modifier.dart
@@ -19,7 +19,7 @@ class IndentModifier extends CodeModifier {
     EditorParams params,
   ) {
     var spacesCount = 0;
-    var braceCount = 0;
+    String? lastChar;
 
     for (var k = min(sel.start, text.length) - 1; k >= 0; k--) {
       if (text[k] == '\n') {
@@ -29,17 +29,12 @@ class IndentModifier extends CodeModifier {
       if (text[k] == ' ') {
         spacesCount += 1;
       } else {
+        lastChar ??= text[k];
         spacesCount = 0;
       }
-
-      if (text[k] == '{') {
-        braceCount += 1;
-      } else if (text[k] == '}') {
-        braceCount -= 1;
-      }
     }
 
-    if (braceCount > 0) {
+    if (lastChar == ':' || lastChar == '{') {
       spacesCount += params.tabSpaces;
     }
 


Test patch:
diff --git a/test/src/code_modifiers/indent_modifier_test.dart b/test/src/code_modifiers/indent_modifier_test.dart
new file mode 100644
index 00000000..27802586
--- /dev/null
+++ b/test/src/code_modifiers/indent_modifier_test.dart
@@ -0,0 +1,195 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_code_editor/flutter_code_editor.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+  test('Indent modifier test', () {
+    const examples = [
+      //
+      _Example(
+        'Preserves indentation of the previous line',
+        initialValue: TextEditingValue(
+          text: '__a',
+          //        \ cursor
+          selection: TextSelection.collapsed(offset: 3),
+        ),
+        editedValue: TextEditingValue(
+          text: '__a\n',
+          //          \ cursor
+          selection: TextSelection.collapsed(offset: 4),
+        ),
+        expected: TextEditingValue(
+          text: '__a\n__',
+          //            \ cursor
+          selection: TextSelection.collapsed(offset: 6),
+        ),
+      ),
+
+      _Example(
+        '`:` adds indentation to the previous indentation',
+        initialValue: TextEditingValue(
+          text: '__a:',
+          //         \ cursor
+          selection: TextSelection.collapsed(offset: 4),
+        ),
+        editedValue: TextEditingValue(
+          text: '__a:\n',
+          //           \ cursor
+          selection: TextSelection.collapsed(offset: 5),
+        ),
+        expected: TextEditingValue(
+          text: '__a:\n____',
+          //               \ cursor
+          selection: TextSelection.collapsed(offset: 9),
+        ),
+      ),
+
+      _Example(
+        '`{` adds indentation to the previous indentation',
+        initialValue: TextEditingValue(
+          text: '__a{',
+          //         \ cursor
+          selection: TextSelection.collapsed(offset: 4),
+        ),
+        editedValue: TextEditingValue(
+          text: '__a{\n',
+          //           \ cursor
+          selection: TextSelection.collapsed(offset: 5),
+        ),
+        expected: TextEditingValue(
+          text: '__a{\n____',
+          //               \ cursor
+          selection: TextSelection.collapsed(offset: 9),
+        ),
+      ),
+
+      _Example(
+        '`:` is in the middle of the line',
+        initialValue: TextEditingValue(
+          text: '__a:a',
+          //          \ cursor
+          selection: TextSelection.collapsed(offset: 5),
+        ),
+        editedValue: TextEditingValue(
+          text: '__a:a\n',
+          //            \ cursor
+          selection: TextSelection.collapsed(offset: 6),
+        ),
+        expected: TextEditingValue(
+          text: '__a:a\n__',
+          //              \ cursor
+          selection: TextSelection.collapsed(offset: 8),
+        ),
+      ),
+
+      _Example(
+        '`{` is in the middle of the line',
+        initialValue: TextEditingValue(
+          text: '__a{a',
+          //          \ cursor
+          selection: TextSelection.collapsed(offset: 5),
+        ),
+        editedValue: TextEditingValue(
+          text: '__a{a\n',
+          //            \ cursor
+          selection: TextSelection.collapsed(offset: 6),
+        ),
+        expected: TextEditingValue(
+          text: '__a{a\n__',
+          //              \ cursor
+          selection: TextSelection.collapsed(offset: 8),
+        ),
+      ),
+
+      _Example(
+        'There are spaces after `:`',
+        initialValue: TextEditingValue(
+          text: '__a:__',
+          //           \ cursor
+          selection: TextSelection.collapsed(offset: 6),
+        ),
+        editedValue: TextEditingValue(
+          text: '__a:__\n',
+          //             \ cursor
+          selection: TextSelection.collapsed(offset: 7),
+        ),
+        expected: TextEditingValue(
+          text: '__a:__\n____',
+          //                 \ cursor
+          selection: TextSelection.collapsed(offset: 11),
+        ),
+      ),
+
+      _Example(
+        'There are spaces after `{`',
+        initialValue: TextEditingValue(
+          text: '__a{__',
+          //           \ cursor
+          selection: TextSelection.collapsed(offset: 6),
+        ),
+        editedValue: TextEditingValue(
+          text: '__a{__\n',
+          //             \ cursor
+          selection: TextSelection.collapsed(offset: 7),
+        ),
+        expected: TextEditingValue(
+          text: '__a{__\n____',
+          //                 \ cursor
+          selection: TextSelection.collapsed(offset: 11),
+        ),
+      ),
+
+      _Example(
+        r'`:` and `{` are on the same line `\n`',
+        initialValue: TextEditingValue(
+          text: 'a{:',
+          //        \ cursor
+          selection: TextSelection.collapsed(offset: 3),
+        ),
+        editedValue: TextEditingValue(
+          text: 'a{:\n',
+          //          \ cursor
+          selection: TextSelection.collapsed(offset: 4),
+        ),
+        expected: TextEditingValue(
+          text: 'a{:\n__',
+          //            \ cursor
+          selection: TextSelection.collapsed(offset: 6),
+        ),
+      ),
+    ];
+
+    for (final example in examples) {
+      final controller = CodeController();
+      controller.value = example.initialValue.copyWith(
+        text: example.initialValue.text.replaceAll('_', ' '),
+      );
+
+      controller.value = example.editedValue.copyWith(
+        text: example.editedValue.text.replaceAll('_', ' '),
+      );
+
+      expect(
+        controller.value,
+        example.expected.copyWith(
+          text: example.expected.text.replaceAll('_', ' '),
+        ),
+        reason: example.name,
+      );
+    }
+  });
+}
+
+class _Example {
+  final String name;
+  final TextEditingValue initialValue;
+  final TextEditingValue editedValue;
+  final TextEditingValue expected;
+
+  const _Example(
+    this.name, {
+    required this.initialValue,
+    required this.editedValue,
+    required this.expected,
+  });
+}