Skip to content

Commit 119a1f9

Browse files
committed
feat: Implement multi-cursor support and virtual removed blocks
- Added functionality for multi-cursor editing, allowing users to add and manage multiple cursors. - Implemented methods for inserting and deleting text at all cursor positions. - Introduced VirtualRemovedBlock class to display removed lines in the editor, enhancing git diff visualization. - Updated styling and decorations to accommodate virtual removed lines. - Bumped version to 9.0.0 and updated Dart SDK constraint to ^3.11.0.
1 parent 571b215 commit 119a1f9

9 files changed

Lines changed: 1173 additions & 190 deletions

File tree

CHANGELOG.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,4 +334,20 @@ This release establishes **CodeForge** as a powerful, production-ready code edit
334334
- FIX: [#41](https://github.com/heckmon/code_forge/issues/41)
335335

336336
## 8.5.1
337-
- FEATURE: Enhanced Mac keyboard support
337+
- FEATURE: Enhanced Mac keyboard support
338+
339+
## 9.0.0
340+
- #### FEATURE: Multi-cursor
341+
- Alt + Click to add multiple cursors in the editor.
342+
- APIs:
343+
```dart
344+
// Multi-cursor operations
345+
controller.addMultiCursor(int line, int character);
346+
controller.clearMultiCursor();
347+
controller.backspaceAtAllCursors();
348+
controller.insertAtAllCursors(String textToInsert);
349+
350+
```
351+
- #### FIX: [#43](https://github.com/heckmon/code_forge/issues/43)
352+
- #### ENHANCEMENT: Virtual lines for git diff removed ranges.
353+
- #### Added `customCodeSnippets` parameter on the editor to add external code snippets on the suggestions as requested in [#46](https://github.com/heckmon/code_forge/issues/46)

README.md

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,12 @@
3838
> code_forge does **not** support Flutter web, as it relies on `dart:io` for core functionality. Use [code_forge_web](https://pub.dev/packages/code_forge_web) for web support.
3939
4040

41-
### What's new in 8.5.1
42-
- FIX: [#47](https://github.com/heckmon/code_forge/issues/47)
43-
- FIX: [#41](https://github.com/heckmon/code_forge/issues/41)
44-
- FEATURE: Enhanced mac keyboard support.
41+
### What's new in 9.0.0
42+
- #### FEATURE: Multi-cursor
43+
- Alt + Click to add multiple cursors in the editor.
44+
- #### FIX: [#43](https://github.com/heckmon/code_forge/issues/43)
45+
- #### ENHANCEMENT: Virtual lines for git diff removed ranges.
46+
- #### Added `customCodeSnippets` parameter on the editor to add external code snippets on the suggestions as requested in [#46](https://github.com/heckmon/code_forge/issues/46)
4547

4648
## Why CodeForge?
4749
**Feature demos:** [CodeForge Features Showcase](https://heckmon.github.io/code_forge_demo/)
@@ -116,7 +118,7 @@ Add CodeForge to your `pubspec.yaml`:
116118

117119
```yaml
118120
dependencies:
119-
code_forge: ^8.5.1
121+
code_forge: ^9.0.0
120122
```
121123
122124
Then run:
@@ -469,6 +471,7 @@ CodeForge(
469471
| `enableSuggestions` | `bool` | Enable autocomplete suggestions |
470472
| `enableKeyboardSuggestions` | `bool` | Show auto completions in OS virtual keyboard |
471473
| `keyboardType` | `TextInputType` | Type of virtual keyboard |
474+
| `customSnippets` | `CustomSnippets` | Add custom code snippets to the suggestions |
472475
| `deleteFoldRangeOnDeletingFirstLine` | `bool` | When true, deleting the first line of a folded block removes the entire block |
473476
| `finderBuilder` | `PreferredSizeWidget Function(FindController findController)?` | Builder for custom Finder widget |
474477

@@ -572,6 +575,12 @@ controller.pressDocumentEndKey(isShiftPressed: false);
572575
controller.pressWordLeftArrowKey(isShiftPressed: false);
573576
controller.pressWordRightArrowKey(isShiftPressed: false);
574577
578+
// Multi-cursor operations
579+
controller.addMultiCursor(int line, int character);
580+
controller.clearMultiCursor();
581+
controller.backspaceAtAllCursors();
582+
controller.insertAtAllCursors(String textToInsert);
583+
575584
```
576585
There are more methods available in the CodeForgeController API. You can see the complete list [here](https://pub.dev/documentation/code_forge/latest/code_forge_controller/CodeForgeController-class.html#instance-methods)
577586

@@ -684,6 +693,7 @@ CodeForge supports a variety of keyboard shortcuts for efficient editing:
684693
- **Ctrl+Y** — Redo last action.
685694
- **Ctrl+Backspace** — Delete word backward.
686695
- **Ctrl+Delete** — Delete word forward.
696+
- **Alt + Click** — Multi-cursor.
687697

688698
### Navigation
689699
- **Ctrl+Arrow Left** — Move cursor to previous word.

assets/icons/snippet.ttf

-1.11 KB
Binary file not shown.

example/lib/main.dart

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,15 @@ class _MyAppState extends State<MyApp> {
4747
onPressed: () {
4848
codeController?.setGitDiffDecorations(
4949
addedRanges: [(1, 5), (10, 25)],
50-
removedRanges: [(30, 37)],
50+
removedRanges: [
51+
(
52+
afterLine: 29,
53+
content:
54+
'final x = 10;\nfinal y = 20;\nprint("removed line");',
55+
),
56+
],
5157
);
52-
codeController?.scrollToLine(100);
58+
codeController?.scrollToLine(30);
5359
},
5460
),
5561
body: SafeArea(
@@ -87,6 +93,12 @@ class _MyAppState extends State<MyApp> {
8793
),
8894
finderBuilder: (c, controller) =>
8995
FindPanelView(controller: controller),
96+
customCodeSnippets: CustomCodeSnippets(
97+
snippets: {
98+
"if": "if(condition){\n\n}",
99+
"if-else": "if(condition){\n\n} else {\n\n}",
100+
},
101+
),
90102
);
91103
},
92104
),

lib/LSP/lsp.dart

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,7 +1088,7 @@ Map<CompletionItemType, Icon> completionItemIcons = {
10881088
CompletionItemType.value_: Icon(Icons.numbers, color: Colors.grey),
10891089
CompletionItemType.enum_: Icon(Icons.notes, color: const Color(0xffee9d28)),
10901090
CompletionItemType.keyword: Icon(Icons.wysiwyg_rounded, color: Colors.grey),
1091-
CompletionItemType.snippet: Icon(CustomIcons.snippet, color: Colors.grey),
1091+
CompletionItemType.snippet: Icon(Icons.rounded_corner, color: Colors.grey),
10921092
CompletionItemType.color: Icon(Icons.color_lens, color: Colors.grey),
10931093
CompletionItemType.file: Icon(Icons.insert_drive_file, color: Colors.grey),
10941094
CompletionItemType.reference: Icon(CustomIcons.reference, color: Colors.grey),
@@ -1125,7 +1125,6 @@ class CustomIcons {
11251125
static const IconData event = IconData(0x900, fontFamily: 'Event');
11261126
static const IconData operator = IconData(0x900, fontFamily: 'Operator');
11271127
static const IconData parameter = IconData(0x900, fontFamily: 'Parameter');
1128-
static const IconData snippet = IconData(0x900, fontFamily: 'Snippet');
11291128
static const IconData interface = IconData(0x900, fontFamily: 'Interface');
11301129
static const IconData field = IconData(0x900, fontFamily: 'Field');
11311130

@@ -1141,7 +1140,6 @@ class CustomIcons {
11411140
'Event': 'assets/icons/event.ttf',
11421141
'Operator': 'assets/icons/operator.ttf',
11431142
'Parameter': 'assets/icons/parameter.ttf',
1144-
'Snippet': 'assets/icons/snippet.ttf',
11451143
'Interface': 'assets/icons/interface.ttf',
11461144
'Field': 'assets/icons/field.ttf',
11471145
};
@@ -1495,3 +1493,57 @@ Map<String, List<String>> getSemanticMapping(String languageId) {
14951493

14961494
return baseMap;
14971495
}
1496+
1497+
/// Represents a custom code snippet configuration for a specific language.
1498+
///
1499+
/// This class encapsulates a collection of code snippets associated with a particular
1500+
/// file extension. It provides a way to define reusable code templates for languages
1501+
/// used in the code editor.
1502+
///
1503+
/// and [snippets] is a map where keys are snippet names and values are the snippet code.
1504+
///
1505+
/// Example usage with [fromJson]:
1506+
/// ```dart
1507+
/// const String jsonString = '''{
1508+
/// "snippets": {
1509+
/// "class": "class MyClass {\\n \\n}",
1510+
/// "function": "void myFunction() {\\n \\n}",
1511+
/// "main": "void main() {\\n print('Hello, World!');\\n}"
1512+
/// }
1513+
/// }''';
1514+
///
1515+
/// final snippet = CustomCodeSnippets.fromJson(jsonString);
1516+
/// if (snippet != null) {
1517+
/// print(snippet.fileExtension); // Output: .dart
1518+
/// print(snippet.snippets['class']); // Output: class MyClass { ... }
1519+
/// }
1520+
/// ```
1521+
///
1522+
/// Required JSON fields:
1523+
/// - **snippets** (Map&lt;String, String&gt;): A map of snippet names to their code content
1524+
class CustomCodeSnippets {
1525+
/// A map of snippet names to their code content.
1526+
///
1527+
/// Keys are user-friendly snippet identifiers (e.g., "class", "function"),
1528+
/// and values are the corresponding code templates with proper formatting.
1529+
///
1530+
/// Example:
1531+
/// ```dart
1532+
/// {
1533+
/// "class": "class MyClass {\n \n}",
1534+
/// "function": "void myFunction() {\n \n}",
1535+
/// "main": "void main() {\n print('Hello, World!');\n}"
1536+
/// }
1537+
/// ```
1538+
final Map<String, String> snippets;
1539+
1540+
CustomCodeSnippets({required this.snippets});
1541+
1542+
static CustomCodeSnippets? fromJson(String json) {
1543+
final Map<String, Map<String, String>>? decoded = jsonDecode(json);
1544+
if (decoded == null || decoded.isEmpty) return null;
1545+
if (!(decoded.containsKey("snippets"))) return null;
1546+
1547+
return CustomCodeSnippets(snippets: decoded["snippets"] ?? {});
1548+
}
1549+
}

0 commit comments

Comments
 (0)