Repository: akvelon/flutter-code-editor
Pull request #75 (2022-09-30 12:54:41)

issue 47. tests for scala foldable blocks


Patch:
diff --git a/lib/src/folding/foldable_block.dart b/lib/src/folding/foldable_block.dart
index 51f5532..f87c8d0 100644
--- a/lib/src/folding/foldable_block.dart
+++ b/lib/src/folding/foldable_block.dart
@@ -1,3 +1,5 @@
+import 'dart:math';
+
 import 'package:equatable/equatable.dart';
 
 import 'foldable_block_type.dart';
@@ -29,4 +31,28 @@ extension FoldableBlockList on List<FoldableBlock> {
   void sortByStartLine() {
     sort((a, b) => a.startLine - b.startLine);
   }
+
+  /// Joins intersecting blocks in list.
+  /// Expected that list is sorted by start line.
+  void joinIntersecting() {
+    if (length < 2) {
+      return;
+    }
+
+    for (int i = 1; i < length; i++) {
+      final currentBlock = this[i];
+      final previousBlock = this[i - 1];
+      final areIntersected = previousBlock.endLine >= currentBlock.startLine &&
+          previousBlock.endLine < currentBlock.endLine;
+      if (areIntersected) {
+        this[i - 1] = FoldableBlock(
+          startLine: previousBlock.startLine,
+          endLine: currentBlock.endLine,
+          type: FoldableBlockType.union,
+        );
+        removeAt(i);
+        i = max(i - 2, 0);
+      }
+    }
+  }
 }
diff --git a/lib/src/folding/foldable_block_type.dart b/lib/src/folding/foldable_block_type.dart
index 75bf965..a165c3b 100644
--- a/lib/src/folding/foldable_block_type.dart
+++ b/lib/src/folding/foldable_block_type.dart
@@ -14,4 +14,5 @@ enum FoldableBlockType {
   multilineComment,
   singleLineComment,
   imports,
+  union,
 }
diff --git a/lib/src/folding/parsers/abstract.dart b/lib/src/folding/parsers/abstract.dart
index ada51f6..38d9c7f 100644
--- a/lib/src/folding/parsers/abstract.dart
+++ b/lib/src/folding/parsers/abstract.dart
@@ -227,6 +227,7 @@ abstract class AbstractFoldableBlockParser {
     _startedBlocks.clear();
 
     blocks.sortByStartLine();
+    blocks.joinIntersecting();
     invalidBlocks.sortByStartOrEndLine();
   }
 }


Test patch:
diff --git a/test/src/folding/foldable_block_test.dart b/test/src/folding/foldable_block_test.dart
new file mode 100644
index 0000000..fff1ce5
--- /dev/null
+++ b/test/src/folding/foldable_block_test.dart
@@ -0,0 +1,119 @@
+import 'package:flutter_code_editor/src/folding/foldable_block.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'parsers/test_executor.dart';
+
+void main() {
+  group('FoldableBlockList.joinIntersecting', () {
+    test('Empty -> Empty', () {
+      const blocks = <FoldableBlock>[];
+
+      final actual = [...blocks]..joinIntersecting();
+
+      expect(actual, blocks);
+    });
+
+    test('Single -> Single', () {
+      const blocks = [
+        FoldableBlock(startLine: 0, endLine: 1, type: FBT.braces),
+      ];
+
+      final actual = [...blocks]..joinIntersecting();
+
+      expect(actual, blocks);
+    });
+
+    test('Not intersected blocks do not join', () {
+      const blocks = [
+        FB(startLine: 0, endLine: 3, type: FBT.braces),
+        FB(startLine: 4, endLine: 5, type: FBT.imports),
+        FB(startLine: 8, endLine: 10, type: FBT.parentheses),
+      ];
+
+      final actual = [...blocks]..joinIntersecting();
+
+      expect(actual, blocks);
+    });
+
+    test('Mixed blocks join correctly', () {
+      const blocks = [
+        FB(startLine: 0, endLine: 3, type: FBT.braces),
+        FB(startLine: 2, endLine: 5, type: FBT.imports),
+        FB(startLine: 6, endLine: 10, type: FBT.parentheses),
+      ];
+      const expected = [
+        FB(startLine: 0, endLine: 5, type: FBT.union),
+        FB(startLine: 6, endLine: 10, type: FBT.parentheses),
+      ];
+
+      final actual = [...blocks]..joinIntersecting();
+
+      expect(actual, expected);
+    });
+
+    test('Multiple intersected blocks will join in single', () {
+      const blocks = [
+        FB(startLine: 0, endLine: 3, type: FBT.braces),
+        FB(startLine: 2, endLine: 5, type: FBT.imports),
+        FB(startLine: 4, endLine: 10, type: FBT.parentheses),
+      ];
+      const expected = [
+        FB(startLine: 0, endLine: 10, type: FBT.union),
+      ];
+
+      final actual = [...blocks]..joinIntersecting();
+
+      expect(actual, expected);
+    });
+
+    test('Nested intersected blocks will join', () {
+      const blocks = [
+        FB(startLine: 0, endLine: 5, type: FBT.braces),
+        FB(startLine: 1, endLine: 3, type: FBT.imports),
+        FB(startLine: 3, endLine: 5, type: FBT.parentheses),
+      ];
+      const expected = [
+        FB(startLine: 0, endLine: 5, type: FBT.braces),
+        FB(startLine: 1, endLine: 5, type: FBT.union),
+      ];
+
+      final actual = [...blocks]..joinIntersecting();
+
+      expect(actual, expected);
+    });
+
+    // TODO(Malarg): fix this. It's not desired behavior
+    // This test represents situation, shown on diagram below:
+    //    0 1 2 3 4 5
+    //  0 |
+    //  1 | |
+    //  2 | |
+    //  3 | |
+    //  4 |   |
+    //  5 |   |
+    //  6 |     |
+    //  7       | |
+    //  8       | |
+    //  9       | |
+    // 10       |   |
+    // 11       |   |
+    // 12       |
+    //
+    // blocks[0] and blocks[3] should be joined, but they aren't.
+    test('Containing children blocks will not join', () {
+      const blocks = [
+        FB(startLine: 0, endLine: 6, type: FBT.braces), //        0
+        FB(startLine: 1, endLine: 3, type: FBT.parentheses), //   1
+        FB(startLine: 4, endLine: 5, type: FBT.parentheses), //   2
+        FB(startLine: 6, endLine: 12, type: FBT.braces), //       3
+        FB(startLine: 7, endLine: 9, type: FBT.parentheses), //   4
+        FB(startLine: 10, endLine: 11, type: FBT.parentheses), // 5
+      ];
+
+      // 0 intersects with 3, not detected.
+      final actual = [...blocks]..joinIntersecting();
+
+      expect(actual, blocks);
+    });
+  });
+}
diff --git a/test/src/folding/parsers/highlight_parser_java_test.dart b/test/src/folding/parsers/highlight_parser_java_test.dart
new file mode 100644
index 0000000..19b72f6
--- /dev/null
+++ b/test/src/folding/parsers/highlight_parser_java_test.dart
@@ -0,0 +1,184 @@
+import 'package:flutter_code_editor/src/folding/invalid_foldable_block.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:highlight/languages/java.dart';
+
+import 'test_executor.dart';
+
+typedef _Tester = HighlightParserTestExecutor;
+
+void main() {
+  group('HighlightFoldableBlockParser for Java.', () {
+    test('Empty string.', () {
+      _Tester.parseAndCheck(mode: java, code: '', expected: []);
+    });
+
+    test('Multiline pair-character blocks', () {
+      const code = '''
+public class MyClass {
+public static void main(
+  String[
+  ] args) {
+  }
+}''';
+      const expectedBlocks = [
+        FB(startLine: 0, endLine: 5, type: FBT.braces),
+        FB(startLine: 1, endLine: 4, type: FBT.union),
+      ];
+      _Tester.parseAndCheck(mode: java, code: code, expected: expectedBlocks);
+    });
+
+    test('Single-line pair-character blocks are ignored.', () {
+      const code = '''
+public class MyClass {
+    public static void main(String[] args) {}
+}''';
+      const expected = [
+        FB(startLine: 0, endLine: 2, type: FBT.braces),
+      ];
+      _Tester.parseAndCheck(mode: java, code: code, expected: expected);
+    });
+
+    test('Blocks with missing pair characters.', () {
+      const code = '''
+public class MyClass }
+public static void main)String][ args( }
+  String][ str;
+{
+{''';
+      final expectedInvalid = [
+        InvalidFoldableBlock(endLine: 0, type: FBT.braces),
+        InvalidFoldableBlock(endLine: 1, type: FBT.parentheses),
+        InvalidFoldableBlock(endLine: 1, type: FBT.brackets),
+        InvalidFoldableBlock(endLine: 1, type: FBT.braces),
+        InvalidFoldableBlock(startLine: 1, type: FBT.brackets),
+        InvalidFoldableBlock(startLine: 1, type: FBT.parentheses),
+        InvalidFoldableBlock(endLine: 2, type: FBT.brackets),
+        InvalidFoldableBlock(startLine: 2, type: FBT.brackets),
+        InvalidFoldableBlock(startLine: 3, type: FBT.braces),
+        InvalidFoldableBlock(startLine: 4, type: FBT.braces),
+      ];
+      _Tester.parseAndCheck(
+        mode: java,
+        code: code,
+        expected: [],
+        invalid: expectedInvalid,
+      );
+    });
+
+    test('Pair characters in literals are ignored.', () {
+      const code = '''
+String a = "{[(";
+String b = ")]}";''';
+      _Tester.parseAndCheck(mode: java, code: code, expected: []);
+    });
+
+    test('Pair characters in comments are ignored.', () {
+      const code = '''
+public class MyClass {
+/// {[(                  1
+/*                       2
+  )]}                    3
+*/                       4
+/// {                    5
+}''';
+      const expected = [
+        FB(startLine: 0, endLine: 6, type: FBT.braces),
+        FB(startLine: 2, endLine: 4, type: FBT.multilineComment),
+      ];
+      _Tester.parseAndCheck(mode: java, code: code, expected: expected);
+    });
+
+    test('Single line comments in a row.', () {
+      const code = '''
+// License                                            0
+// License                                            1
+public class MyClass {
+
+/// Method                                          4
+
+/// Method                                          6
+
+void method() {} // Not the only thing in the line  8
+// Single                                           9
+void method2 // Not the only thing in the line      10
+// Single                                           11
+() {}
+}
+// Single                                             14
+// Single                                             15''';
+      const expected = [
+        FB(startLine: 0, endLine: 1, type: FBT.singleLineComment),
+        FB(startLine: 2, endLine: 13, type: FBT.braces),
+        FB(startLine: 4, endLine: 6, type: FBT.singleLineComment),
+        FB(startLine: 14, endLine: 15, type: FBT.singleLineComment),
+      ];
+      _Tester.parseAndCheck(mode: java, code: code, expected: expected);
+    });
+
+    test('Imports at beginning.', () {
+      const code = '''
+package com.akvelon.temp;
+
+import java.util.Arrays;
+
+/* */
+/* */
+import java.util.Date;
+
+/*
+*/
+import java.lang.Math;
+
+public class MyClass {}''';
+      const expected = [
+        FB(startLine: 0, endLine: 6, type: FBT.imports),
+        FB(startLine: 4, endLine: 5, type: FBT.singleLineComment),
+        FB(startLine: 8, endLine: 9, type: FBT.multilineComment),
+      ];
+      _Tester.parseAndCheck(mode: java, code: code, expected: expected);
+    });
+
+    test('Imports at mid and the end, no newline in the end.', () {
+      final mode = java;
+      const code = '''
+//
+import java.util.Arrays;
+import java.util.Date;
+public class MyClass {}
+import java.lang.Math;
+import java.lang.Exception;''';
+      const expected = [
+        FB(startLine: 1, endLine: 2, type: FBT.imports),
+        FB(startLine: 4, endLine: 5, type: FBT.imports),
+      ];
+      _Tester.parseAndCheck(mode: mode, code: code, expected: expected);
+    });
+
+    test('Comment after a closing brace.', () {
+      const code = '''
+class MyClass {
+void method() {
+}// comment
+// comment
+}''';
+      const expected = [
+        FB(startLine: 0, endLine: 4, type: FBT.braces),
+        FB(startLine: 1, endLine: 2, type: FBT.braces),
+      ];
+      _Tester.parseAndCheck(mode: java, code: code, expected: expected);
+    });
+
+    test('Service comment sequences do not form a foldable block.', () {
+      final mode = java;
+      const code = '''
+class MyClass {
+// [START section1]
+// [END section2]
+}''';
+      const expected = [
+        FB(startLine: 0, endLine: 3, type: FBT.braces),
+      ];
+      _Tester.parseAndCheck(mode: mode, code: code, expected: expected);
+    });
+  });
+}
diff --git a/test/src/folding/parsers/highlight_parser_scala_test.dart b/test/src/folding/parsers/highlight_parser_scala_test.dart
new file mode 100644
index 0000000..aea88fa
--- /dev/null
+++ b/test/src/folding/parsers/highlight_parser_scala_test.dart
@@ -0,0 +1,242 @@
+import 'package:flutter_code_editor/src/folding/invalid_foldable_block.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:highlight/languages/scala.dart';
+
+import 'test_executor.dart';
+
+typedef _Tester = HighlightParserTestExecutor;
+
+void main() {
+  group('HighlightFoldableBlockParser for Scala', () {
+    test('Empty file.', () {
+      _Tester.parseAndCheck(mode: scala, code: '', expected: []);
+    });
+
+    test('Multiline pair-character block', () {
+      const code = '''
+class MyClass(var myVar1: Int,
+              var myVar2: String
+             ) {
+  def main(main1: Int,
+           main2: Int
+  ) = {
+    println("h")
+  }
+}''';
+      const expected = [
+        FB(startLine: 0, endLine: 8, type: FBT.union),
+        FB(startLine: 3, endLine: 7, type: FBT.union),
+      ];
+      _Tester.parseAndCheck(mode: scala, code: code, expected: expected);
+    });
+
+    test('Single-line pair-character blocks are ignored.', () {
+      const code = '''
+class MyClass() {
+  def main() = {}
+}''';
+      const expected = [
+        FB(startLine: 0, endLine: 2, type: FBT.braces),
+      ];
+      _Tester.parseAndCheck(mode: scala, code: code, expected: expected);
+    });
+
+    test('Blocks with missing pair character.', () {
+      const code = '''
+class MyClass() }
+''';
+      final expectedInvalid = [
+        InvalidFoldableBlock(endLine: 0, type: FBT.braces),
+      ];
+      _Tester.parseAndCheck(
+        mode: scala,
+        code: code,
+        expected: [],
+        invalid: expectedInvalid,
+      );
+    });
+
+    test('Pair characters in literals are ignored.', () {
+      const code = '''
+val a = "{[("
+val b = ")]}"''';
+      _Tester.parseAndCheck(mode: scala, code: code, expected: []);
+    });
+
+    test('Pair characters in comments are ignored.', () {
+      const code = '''
+class MyClass() {
+/// {[(                  1
+/*                       2
+  )]}                    3
+*/                       4
+/// {                    5
+}''';
+      const expected = [
+        FB(startLine: 0, endLine: 6, type: FBT.braces),
+        FB(startLine: 2, endLine: 4, type: FBT.multilineComment),
+      ];
+      _Tester.parseAndCheck(mode: scala, code: code, expected: expected);
+    });
+
+    test('Single line comments in a row.', () {
+      const code = '''
+// License                                            0
+// License                                            1
+class MyClass() {
+
+/// Method                                            4
+
+/// Method                                            6
+
+def method() = {} // Not the only thing in the line   8
+// Single                                             9
+def method2 // Not the only thing in the line        10
+// Single                                            11
+() = {}
+}
+// Single                                            14
+// Single                                            15''';
+      const expected = [
+        FB(startLine: 0, endLine: 1, type: FBT.singleLineComment),
+        FB(startLine: 2, endLine: 13, type: FBT.braces),
+        FB(startLine: 4, endLine: 6, type: FBT.singleLineComment),
+        FB(startLine: 14, endLine: 15, type: FBT.singleLineComment),
+      ];
+      _Tester.parseAndCheck(mode: scala, code: code, expected: expected);
+    });
+
+    test('Imports at the beginning.', () {
+      const code = '''
+package users
+/* */
+import users._  // import everything from the users package                      2
+/*                                                                               3
+  awesome multiline comment                                                      4
+*/
+import users.User  // import the class User                                      6
+import users.{User, UserPreferences}  // Only imports selected members           7
+import users.{UserPreferences => UPrefs} // import and rename for convenience    8
+
+class MyClass() {} //                                                           10 ''';
+      const expected = [
+        FB(startLine: 0, endLine: 2, type: FBT.imports),
+        FB(startLine: 3, endLine: 5, type: FBT.multilineComment),
+        FB(startLine: 6, endLine: 8, type: FBT.imports),
+      ];
+      _Tester.parseAndCheck(mode: scala, code: code, expected: expected);
+    });
+
+    test('Imports at mid and the end, no newline in the end.', () {
+      const code = '''
+//
+package users
+import users._  // import everything from the users package
+class MyClass() {}
+import users.User  // import the class User
+import users.{ User, UserPreferences}  // Only imports selected members''';
+      const expected = [
+        FB(startLine: 1, endLine: 2, type: FBT.imports),
+        FB(startLine: 4, endLine: 5, type: FBT.imports),
+      ];
+      _Tester.parseAndCheck(mode: scala, code: code, expected: expected);
+    });
+
+    test('Multiline imports form braces foldable block', () {
+      //TODO(Malarg): handle scala multiline blocks
+      //https://github.com/akvelon/flutter-code-editor/issues/78.
+      const code = '''
+import users.User  // import the class User
+import users.{ 
+  User, 
+  UserPreferences
+}  // Only imports selected members''';
+      const expected = [
+        FB(startLine: 0, endLine: 4, type: FBT.union),
+      ];
+      _Tester.parseAndCheck(mode: scala, code: code, expected: expected);
+    });
+
+    test('Comment after a closing brace.', () {
+      const code = '''
+class MyClass() {
+def method() = {
+}// comment
+// comment
+}''';
+      const expected = [
+        FB(startLine: 0, endLine: 4, type: FBT.braces),
+        FB(startLine: 1, endLine: 2, type: FBT.braces),
+      ];
+      _Tester.parseAndCheck(mode: scala, code: code, expected: expected);
+    });
+
+    test('Service comment sequences do not form a foldable block.', () {
+      const code = '''
+class MyClass() {
+// [START section1]
+// [END section2]
+}''';
+      const expected = [
+        FB(startLine: 0, endLine: 3, type: FBT.braces),
+      ];
+      _Tester.parseAndCheck(mode: scala, code: code, expected: expected);
+    });
+
+    test('Objects form block.', () {
+      const code = '''
+object IdFactory {
+  private var counter = 0
+  def create(): Int = {
+    counter += 1
+    counter
+  }
+}''';
+      const expected = [
+        FB(startLine: 0, endLine: 6, type: FBT.braces),
+        FB(startLine: 2, endLine: 5, type: FBT.braces),
+      ];
+      _Tester.parseAndCheck(mode: scala, code: code, expected: expected);
+    });
+
+    test('One line functions do not form block.', () {
+      const code = '''
+object Main {
+  def main(args: Array[String]): Unit =
+    println("Hello, Scala developer!")
+}''';
+      const expected = [
+        FB(startLine: 0, endLine: 3, type: FBT.braces),
+      ];
+      _Tester.parseAndCheck(mode: scala, code: code, expected: expected);
+    });
+
+    test('Multiline lists form block.', () {
+      const code = '''
+val list: List[Any] = List(
+  "a string",
+  732,  // an integer
+  'c',  // a character
+  true, // a boolean value
+  () => "an anonymous function returning a string"
+)''';
+      const expected = [
+        FB(startLine: 0, endLine: 6, type: FBT.parentheses),
+      ];
+      _Tester.parseAndCheck(mode: scala, code: code, expected: expected);
+    });
+
+    test('Match functions form block.', () {
+      const code = '''
+def matchTest(x: Int): String = x match {
+  case 1 => "one"
+  case 2 => "two"
+  case _ => "other"
+}''';
+      const expected = [
+        FB(startLine: 0, endLine: 4, type: FBT.braces),
+      ];
+      _Tester.parseAndCheck(mode: scala, code: code, expected: expected);
+    });
+  });
+}
diff --git a/test/src/folding/parsers/highlight_parser_test.dart b/test/src/folding/parsers/highlight_parser_test.dart
deleted file mode 100644
index facb4e5..0000000
--- a/test/src/folding/parsers/highlight_parser_test.dart
+++ /dev/null
@@ -1,278 +0,0 @@
-import 'package:flutter_code_editor/src/folding/foldable_block.dart';
-import 'package:flutter_code_editor/src/folding/foldable_block_type.dart';
-import 'package:flutter_code_editor/src/folding/invalid_foldable_block.dart';
-import 'package:flutter_code_editor/src/folding/parsers/highlight.dart';
-import 'package:flutter_code_editor/src/named_sections/parsers/brackets_start_end.dart';
-import 'package:flutter_code_editor/src/service_comment_filter/service_comment_filter.dart';
-import 'package:flutter_code_editor/src/single_line_comments/parser/single_line_comment_parser.dart';
-import 'package:flutter_code_editor/src/single_line_comments/parser/single_line_comments.dart';
-import 'package:flutter_code_editor/src/single_line_comments/single_line_comment.dart';
-import 'package:flutter_test/flutter_test.dart';
-import 'package:highlight/highlight_core.dart';
-import 'package:highlight/languages/java.dart';
-
-void main() {
-  group('HighlightFoldableBlockParser', () {
-    test('parses', () {
-      final examples = [
-        //
-        _Example(
-          'Java. Empty string',
-          code: '',
-          mode: java,
-          expected: const [],
-          expectedInvalid: const [],
-        ),
-
-        _Example(
-          'Java. Multiline pair-character blocks',
-          code: '''
-public class MyClass {
-  public static void main(
-    String[
-    ] args) {
-    }
-}
-''',
-          mode: java,
-          expected: const [
-            _FB(startLine: 0, endLine: 5, type: _T.braces),
-            _FB(startLine: 1, endLine: 3, type: _T.parentheses),
-            _FB(startLine: 2, endLine: 3, type: _T.brackets),
-            _FB(startLine: 3, endLine: 4, type: _T.braces),
-          ],
-          expectedInvalid: const [],
-        ),
-
-        _Example(
-          'Java. Single-line pair-character blocks are ignored',
-          code: '''
-public class MyClass {
-  public static void main(String[] args) {}
-}
-''',
-          mode: java,
-          expected: const [
-            _FB(startLine: 0, endLine: 2, type: _T.braces),
-          ],
-          expectedInvalid: const [],
-        ),
-
-        _Example(
-          'Java. Blocks with missing pair characters',
-          code: '''
-public class MyClass }
-  public static void main)String][ args( }
-    String][ str;
-  {
-{
-''',
-          mode: java,
-          expected: const [],
-          expectedInvalid: [
-            InvalidFoldableBlock(endLine: 0, type: _T.braces),
-            InvalidFoldableBlock(endLine: 1, type: _T.parentheses),
-            InvalidFoldableBlock(endLine: 1, type: _T.brackets),
-            InvalidFoldableBlock(endLine: 1, type: _T.braces),
-            InvalidFoldableBlock(startLine: 1, type: _T.brackets),
-            InvalidFoldableBlock(startLine: 1, type: _T.parentheses),
-            InvalidFoldableBlock(endLine: 2, type: _T.brackets),
-            InvalidFoldableBlock(startLine: 2, type: _T.brackets),
-            InvalidFoldableBlock(startLine: 3, type: _T.braces),
-            InvalidFoldableBlock(startLine: 4, type: _T.braces),
-          ],
-        ),
-
-        _Example(
-          'Java. Pair characters in literals are ignored',
-          code: '''
-String a = "{[(";
-String b = ")]}";
-''',
-          mode: java,
-          expected: const [],
-          expectedInvalid: const [],
-        ),
-
-        _Example(
-          'Java. Pair characters in comments are ignored',
-          code: '''
-public class MyClass {
-  /// {[(                  1
-  /*                       2
-    )]}                    3
-  */                       4
-  /// {                    5
-}
-''',
-          mode: java,
-          expected: const [
-            _FB(startLine: 0, endLine: 6, type: _T.braces),
-            _FB(startLine: 2, endLine: 4, type: _T.multilineComment),
-          ],
-          expectedInvalid: const [],
-        ),
-
-        _Example(
-          'Java. Single line comments in a row',
-          code: '''
-// License                                            0
-// License                                            1
-public class MyClass {
-
-  /// Method                                          4
-
-  /// Method                                          6
-
-  void method() {} // Not the only thing in the line  8
-  // Single                                           9
-  void method2 // Not the only thing in the line      10
-  // Single                                           11
-  () {}
-}
-// Single                                             14
-// Single                                             15
-''',
-          mode: java,
-          expected: const [
-            _FB(startLine: 0, endLine: 1, type: _T.singleLineComment),
-            _FB(startLine: 2, endLine: 13, type: _T.braces),
-            _FB(startLine: 4, endLine: 6, type: _T.singleLineComment),
-            _FB(startLine: 14, endLine: 15, type: _T.singleLineComment),
-          ],
-          expectedInvalid: const [],
-        ),
-
-        _Example(
-          'Java. Imports at beginning',
-          code: '''
-package com.akvelon.temp;
-
-import java.util.Arrays;
-
-/* */
-/* */
-import java.util.Date;
-
-/*
-*/
-import java.lang.Math;
-
-public class MyClass {}
-''',
-          mode: java,
-          expected: const [
-            _FB(startLine: 0, endLine: 6, type: _T.imports),
-            _FB(startLine: 4, endLine: 5, type: _T.singleLineComment),
-            _FB(startLine: 8, endLine: 9, type: _T.multilineComment),
-          ],
-          expectedInvalid: const [],
-        ),
-
-        _Example(
-          'Java. Imports at mid and the end, no newline in the end',
-          code: '''
-//
-import java.util.Arrays;
-import java.util.Date;
-public class MyClass {}
-import java.lang.Math;
-import java.lang.Exception;''',
-          mode: java,
-          expected: const [
-            _FB(startLine: 1, endLine: 2, type: _T.imports),
-            _FB(startLine: 4, endLine: 5, type: _T.imports),
-          ],
-          expectedInvalid: const [],
-        ),
-
-        _Example(
-          'Java. Comment after a closing brace',
-          code: '''
-class MyClass {
-  void method() {
-  }// comment
-  // comment
-}''',
-          mode: java,
-          expected: const [
-            _FB(startLine: 0, endLine: 4, type: _T.braces),
-            _FB(startLine: 1, endLine: 2, type: _T.braces),
-          ],
-          expectedInvalid: const [],
-        ),
-
-        _Example(
-          'Java. Service comment sequences do not form a foldable block.',
-          code: '''
-class MyClass {
-  // [START section1]
-  // [END section2]
-}''',
-          mode: java,
-          expected: const [
-            _FB(startLine: 0, endLine: 3, type: _T.braces),
-          ],
-          expectedInvalid: const [],
-        ),
-      ];
-
-      for (final example in examples) {
-        highlight.registerLanguage('language', example.mode);
-        final highlighted = highlight.parse(example.code, language: 'language');
-        final parser = HighlightFoldableBlockParser();
-
-        final sequences = SingleLineComments.byMode[example.mode] ?? [];
-
-        final commentParser = SingleLineCommentParser.parseHighlighted(
-          text: example.code,
-          highlighted: highlighted,
-          singleLineCommentSequences: sequences,
-        );
-
-        final serviceComments = ServiceCommentFilter.filter(
-          commentParser.comments,
-          namedSectionParser: const BracketsStartEndNamedSectionParser(),
-        );
-
-        parser.parse(
-          highlighted: highlighted,
-          serviceCommentsSources: serviceComments.sources,
-        );
-
-        expect(
-          parser.blocks,
-          example.expected,
-          reason: '${example.name}, valid blocks',
-        );
-        expect(
-          parser.invalidBlocks,
-          example.expectedInvalid,
-          reason: '${example.name}, invalid blocks',
-        );
-      }
-    });
-  });
-}
-
-class _Example {
-  final String name;
-  final String code;
-  final Mode mode;
-  final List<FoldableBlock> expected;
-  final List<InvalidFoldableBlock> expectedInvalid;
-
-  const _Example(
-    this.name, {
-    required this.code,
-    required this.mode,
-    required this.expected,
-    required this.expectedInvalid,
-  });
-}
-
-/// Shorter alias for [FoldableBlock] to avoid line breaks.
-typedef _FB = FoldableBlock;
-
-/// Shorter alias for [FoldableBlockType] to avoid line breaks.
-typedef _T = FoldableBlockType;
diff --git a/test/src/folding/parsers/python_parser_test.dart b/test/src/folding/parsers/python_parser_test.dart
index b3ef89a..8ea845a 100644
--- a/test/src/folding/parsers/python_parser_test.dart
+++ b/test/src/folding/parsers/python_parser_test.dart
@@ -231,15 +231,14 @@ print("The value of pi is : ",pie)    # 11''',
         ),
 
         _Example(
-          'Python. Nested highlight blocks',
+          'Python. Nested blocks opened at the same row joined into one',
           code: '''
 a = [[
   5
   ]
 ]''',
           expected: [
-            _FB(startLine: 0, endLine: 2, type: _T.brackets),
-            _FB(startLine: 0, endLine: 3, type: _T.brackets),
+            _FB(startLine: 0, endLine: 3, type: _T.union),
           ],
         ),
       ];
diff --git a/test/src/folding/parsers/test_executor.dart b/test/src/folding/parsers/test_executor.dart
new file mode 100644
index 0000000..7c7dbe2
--- /dev/null
+++ b/test/src/folding/parsers/test_executor.dart
@@ -0,0 +1,51 @@
+import 'package:flutter_code_editor/src/folding/foldable_block.dart';
+import 'package:flutter_code_editor/src/folding/foldable_block_type.dart';
+import 'package:flutter_code_editor/src/folding/invalid_foldable_block.dart';
+import 'package:flutter_code_editor/src/folding/parsers/highlight.dart';
+import 'package:flutter_code_editor/src/named_sections/parsers/brackets_start_end.dart';
+import 'package:flutter_code_editor/src/service_comment_filter/service_comment_filter.dart';
+import 'package:flutter_code_editor/src/single_line_comments/parser/single_line_comment_parser.dart';
+import 'package:flutter_code_editor/src/single_line_comments/parser/single_line_comments.dart';
+import 'package:flutter_code_editor/src/single_line_comments/single_line_comment.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:highlight/highlight_core.dart';
+
+/// Shorter alias for [FoldableBlock] to avoid line breaks.
+typedef FB = FoldableBlock;
+
+/// Shorter alias for [FoldableBlockType] to avoid line breaks.
+typedef FBT = FoldableBlockType;
+
+class HighlightParserTestExecutor {
+  static void parseAndCheck({
+    required Mode mode,
+    required String code,
+    required List<FoldableBlock> expected,
+    List<InvalidFoldableBlock> invalid = const [],
+  }) {
+    highlight.registerLanguage('language', mode);
+    final highlighted = highlight.parse(code, language: 'language');
+    final parser = HighlightFoldableBlockParser();
+
+    final sequences = SingleLineComments.byMode[mode] ?? [];
+
+    final commentParser = SingleLineCommentParser.parseHighlighted(
+      text: code,
+      highlighted: highlighted,
+      singleLineCommentSequences: sequences,
+    );
+
+    final serviceComments = ServiceCommentFilter.filter(
+      commentParser.comments,
+      namedSectionParser: const BracketsStartEndNamedSectionParser(),
+    );
+
+    parser.parse(
+      highlighted: highlighted,
+      serviceCommentsSources: serviceComments.sources,
+    );
+
+    expect(parser.blocks, expected);
+    expect(parser.invalidBlocks, invalid);
+  }
+}