Repository: seaofvoices/darklua (rust)

Issue:
Add a method removing a statement for Block
Currently, there is no way to simply mutate & remove the specific statements of `Block`. Some temporary works for this:
1. Use `block.filter_statements()`
2. Regenerate the `Block` through the vector excluding the statements you want to remove.

So, `block.remove_statement(index: usize)` would be cooler to remove a statement in a specific index of `Block`.

Pull request:
Add `remove_statement` method to `Block`
Closes #252, Closes #253

I re-opened this PR because I couldn't push to the branch a few more tests and the changelog entry

- [x] add entry to the changelog


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 917a833..0922c58 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,6 @@
 # Changelog
 
+* add `remove_statement(index)` method to `Block` ([#254](https://github.com/seaofvoices/darklua/pull/254))
 * fix floating point number representation ([#251](https://github.com/seaofvoices/darklua/pull/251))
 * read Luau configuration files (`.luaurc`) to get path aliases ([#246](https://github.com/seaofvoices/darklua/pull/246))
 * support Luau types when bundling ([#249](https://github.com/seaofvoices/darklua/pull/249))
diff --git a/src/nodes/block.rs b/src/nodes/block.rs
index 6306c33..0dbad80 100644
--- a/src/nodes/block.rs
+++ b/src/nodes/block.rs
@@ -59,6 +59,19 @@ impl Block {
         self.statements.push(statement.into());
     }
 
+    pub fn remove_statement(&mut self, index: usize) {
+        let statements_len = self.statements.len();
+        if index < statements_len {
+            self.statements.remove(index);
+
+            if let Some(tokens) = &mut self.tokens {
+                if tokens.semicolons.len() == statements_len {
+                    tokens.semicolons.remove(index);
+                }
+            }
+        }
+    }
+
     pub fn with_statement<T: Into<Statement>>(mut self, statement: T) -> Self {
         self.statements.push(statement.into());
         self
@@ -408,7 +421,45 @@ mod test {
     }
 
     #[test]
-    fn clean_removes_semicolon_tokens() {
+    fn attempt_to_remove_statement_from_empty_block() {
+        let mut block = parse_block_with_tokens("");
+
+        block.remove_statement(0);
+
+        assert!(block.is_empty());
+    }
+
+    #[test]
+    fn remove_first_and_only_statement() {
+        let mut block = parse_block_with_tokens("while true do end");
+
+        block.remove_statement(0);
+
+        assert!(block.is_empty());
+    }
+
+    #[test]
+    fn remove_first_statement() {
+        let mut block = parse_block_with_tokens("while true do end ; do end");
+
+        block.remove_statement(0);
+
+        insta::assert_debug_snapshot!("remove_first_statement", block);
+    }
+
+    #[test]
+    fn attempt_to_remove_statement_out_of_bounds() {
+        let mut block = parse_block_with_tokens("while true do end");
+        let original = block.clone();
+
+        block.remove_statement(1);
+        block.remove_statement(2);
+
+        assert_eq!(block, original);
+    }
+
+    #[test]
+    fn clear_removes_semicolon_tokens() {
         let mut block = Block::default()
             .with_statement(DoStatement::default())
             .with_tokens(BlockTokens {
@@ -422,7 +473,7 @@ mod test {
     }
 
     #[test]
-    fn clean_removes_last_semicolon_token() {
+    fn clear_removes_last_semicolon_token() {
         let mut block = Block::default()
             .with_last_statement(LastStatement::new_break())
             .with_tokens(BlockTokens {


Test patch:
diff --git a/src/nodes/snapshots/darklua_core__nodes__block__test__remove_first_statement.snap b/src/nodes/snapshots/darklua_core__nodes__block__test__remove_first_statement.snap
new file mode 100644
index 0000000..e5bb3c8
--- /dev/null
+++ b/src/nodes/snapshots/darklua_core__nodes__block__test__remove_first_statement.snap
@@ -0,0 +1,64 @@
+---
+source: src/nodes/block.rs
+expression: block
+---
+Block {
+    statements: [
+        Do(
+            DoStatement {
+                block: Block {
+                    statements: [],
+                    last_statement: None,
+                    tokens: Some(
+                        BlockTokens {
+                            semicolons: [],
+                            last_semicolon: None,
+                            final_token: None,
+                        },
+                    ),
+                },
+                tokens: Some(
+                    DoTokens {
+                        do: Token {
+                            position: LineNumberReference {
+                                start: 20,
+                                end: 22,
+                                line_number: 1,
+                            },
+                            leading_trivia: [],
+                            trailing_trivia: [
+                                Trivia {
+                                    position: LineNumberReference {
+                                        start: 22,
+                                        end: 23,
+                                        line_number: 1,
+                                    },
+                                    kind: Whitespace,
+                                },
+                            ],
+                        },
+                        end: Token {
+                            position: LineNumberReference {
+                                start: 23,
+                                end: 26,
+                                line_number: 1,
+                            },
+                            leading_trivia: [],
+                            trailing_trivia: [],
+                        },
+                    },
+                ),
+            },
+        ),
+    ],
+    last_statement: None,
+    tokens: Some(
+        BlockTokens {
+            semicolons: [
+                None,
+            ],
+            last_semicolon: None,
+            final_token: None,
+        },
+    ),
+}