Skip to content

Commit 6fae309

Browse files
committed
feat: Update version to 9.3.0, enhance multiple highlight grammars support, and update CHANGELOG and README
1 parent 020cc0f commit 6fae309

8 files changed

Lines changed: 305 additions & 39 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,4 +358,8 @@ This release establishes **CodeForge** as a powerful, production-ready code edit
358358

359359
## 9.2.0
360360
- FEATURE: [#51](https://github.com/heckmon/code_forge/issues/51)
361-
- FIX: [#53](https://github.com/heckmon/code_forge/issues/53)
361+
- FIX: [#53](https://github.com/heckmon/code_forge/issues/53)
362+
363+
## 9.3.0
364+
- FIX: [#54](https://github.com/heckmon/code_forge/issues/54)
365+
- FEATURE: Multiple highlight grammars for a single editor instance.

README.md

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@
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 9.2.0
42-
- FEATURE: [#51](https://github.com/heckmon/code_forge/issues/51)
43-
- FIX: [#53](https://github.com/heckmon/code_forge/issues/53)
41+
### What's new in 9.3.0
42+
- FIX: [#54](https://github.com/heckmon/code_forge/issues/54)
43+
- FEATURE: Multiple highlight grammars for a single editor instance.
4444

4545
## Why CodeForge?
4646
**Feature demos:** [CodeForge Features Showcase](https://heckmon.github.io/code_forge_demo/)
@@ -115,7 +115,7 @@ Add CodeForge to your `pubspec.yaml`:
115115

116116
```yaml
117117
dependencies:
118-
code_forge: ^9.1.0
118+
code_forge: ^9.3.0
119119
```
120120
121121
Then run:
@@ -467,6 +467,7 @@ CodeForge(
467467
| `enableGutterDivider` | `bool` | Show gutter divider |
468468
| `enableSuggestions` | `bool` | Enable autocomplete suggestions |
469469
| `enableKeyboardSuggestions` | `bool` | Show auto completions in OS virtual keyboard |
470+
| `extraLanguages` | `List<Mode>` | Useful for languages that embed other grammars (for example, TSX using XML/HTML sub-languages). |
470471
| `keyboardType` | `TextInputType` | Type of virtual keyboard |
471472
| `customCodeSnippets` | `List<CustomCodeSnippet>?` | Custom code snippets shown in the suggestion popup |
472473
| `deleteFoldRangeOnDeletingFirstLine` | `bool` | When true, deleting the first line of a folded block removes the entire block |
@@ -743,15 +744,6 @@ This project is licensed under the MIT License - see the [LICENSE](https://githu
743744

744745
---
745746

746-
## In Memory
747-
This project is dedicated to my friend [@laraholand](https://github.com/laraholand) (Rafi Ahmed).
748-
749-
For a month he opened issues, shared ideas, and helped improve this project almost every day.<br>
750-
His encouragement and enthusiasm meant a lot to me and this project.
751-
<br><br>
752-
Thank you for everything. Rest in peace brother🌹.
753-
<br>
754-
<br>
755747
<p align="center">
756748
<strong>Built with ❤️ for the Flutter community</strong>
757749
</p>

example/pubspec.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,5 +501,5 @@ packages:
501501
source: hosted
502502
version: "1.1.0"
503503
sdks:
504-
dart: ">=3.11.1 <4.0.0"
504+
dart: ">=3.11.3 <4.0.0"
505505
flutter: ">=3.35.0"

example/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ publish_to: 'none'
55
version: 1.0.0+1
66

77
environment:
8-
sdk: ^3.11.1
8+
sdk: ^3.11.3
99

1010
dependencies:
1111
flutter:

lib/code_forge/code_area.dart

Lines changed: 90 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ import 'dart:io';
33
import 'dart:math';
44
import 'dart:ui' as ui;
55

6-
import 'package:flutter/foundation.dart';
7-
86
import '../LSP/lsp.dart';
97
import 'controller.dart';
108
import 'find_controller.dart';
@@ -17,6 +15,7 @@ import 'package:re_highlight/re_highlight.dart';
1715
import 'package:re_highlight/styles/vs2015.dart';
1816
import 'package:re_highlight/languages/dart.dart';
1917
import 'package:markdown_widget/markdown_widget.dart';
18+
import 'package:flutter/foundation.dart';
2019
import 'package:flutter/gestures.dart';
2120
import 'package:flutter/material.dart';
2221
import 'package:flutter/rendering.dart';
@@ -80,6 +79,12 @@ class CodeForge extends StatefulWidget {
8079
/// by default if not specified.
8180
final Mode? language;
8281

82+
/// Additional language modes registered in the same highlighter instance.
83+
///
84+
/// Useful for languages that embed other grammars (for example, TSX using
85+
/// XML/HTML sub-languages).
86+
final List<Mode> extraLanguages;
87+
8388
/// The focus node for managing keyboard focus.
8489
///
8590
/// If not provided, an internal focus node will be created.
@@ -233,6 +238,7 @@ class CodeForge extends StatefulWidget {
233238
this.undoController,
234239
this.editorTheme,
235240
this.language,
241+
this.extraLanguages = const [],
236242
this.ghostTextStyle,
237243
this.filePath,
238244
this.initialText,
@@ -2211,6 +2217,8 @@ class _CodeForgeState extends State<CodeForge> with TickerProviderStateMixin {
22112217
controller: _controller,
22122218
editorTheme: _editorTheme,
22132219
language: _language,
2220+
extraLanguages:
2221+
widget.extraLanguages,
22142222
languageId: _controller
22152223
.lspConfig
22162224
?.languageId,
@@ -3671,6 +3679,7 @@ class _CodeField extends LeafRenderObjectWidget {
36713679
final CodeForgeController controller;
36723680
final Map<String, TextStyle> editorTheme;
36733681
final Mode language;
3682+
final List<Mode> extraLanguages;
36743683
final String? languageId;
36753684
final LspConfig? lspConfig;
36763685
final List<LspSemanticToken>? semanticTokens;
@@ -3705,6 +3714,7 @@ class _CodeField extends LeafRenderObjectWidget {
37053714
required this.controller,
37063715
required this.editorTheme,
37073716
required this.language,
3717+
required this.extraLanguages,
37083718
required this.vscrollController,
37093719
required this.hscrollController,
37103720
required this.focusNode,
@@ -3753,6 +3763,7 @@ class _CodeField extends LeafRenderObjectWidget {
37533763
controller: controller,
37543764
editorTheme: editorTheme,
37553765
language: language,
3766+
extraLanguages: extraLanguages,
37563767
languageId: languageId,
37573768
lspConfig: lspConfig,
37583769
innerPadding: innerPadding,
@@ -3809,6 +3820,7 @@ class _CodeField extends LeafRenderObjectWidget {
38093820
..updateDiagnostics(diagnostics)
38103821
..editorTheme = editorTheme
38113822
..language = language
3823+
..extraLanguages = extraLanguages
38123824
..textStyle = textStyle
38133825
..innerPadding = innerPadding
38143826
..readOnly = readOnly
@@ -3881,6 +3893,7 @@ class _CodeFieldRenderer extends RenderBox implements MouseTrackerAnnotation {
38813893
TextStyle? _ghostTextStyle;
38823894
Map<String, TextStyle> _editorTheme;
38833895
Mode _language;
3896+
List<Mode> _extraLanguages;
38843897
EdgeInsets? _innerPadding;
38853898
double _rightPaddingWidth = 0, _bottomPaddingHeight = 0;
38863899
TextStyle? _textStyle;
@@ -4188,6 +4201,7 @@ class _CodeFieldRenderer extends RenderBox implements MouseTrackerAnnotation {
41884201
required bool lineWrap,
41894202
required Map<String, TextStyle> editorTheme,
41904203
required Mode language,
4204+
required List<Mode> extraLanguages,
41914205
required bool readOnly,
41924206
required bool enableFolding,
41934207
required bool enableGuideLines,
@@ -4208,6 +4222,7 @@ class _CodeFieldRenderer extends RenderBox implements MouseTrackerAnnotation {
42084222
}) : _editorTheme = editorTheme,
42094223
_ghostTextStyle = ghostTextStyle,
42104224
_language = language,
4225+
_extraLanguages = extraLanguages,
42114226
_readOnly = readOnly,
42124227
_enableFolding = enableFolding,
42134228
_enableGuideLines = enableGuideLines,
@@ -4231,6 +4246,7 @@ class _CodeFieldRenderer extends RenderBox implements MouseTrackerAnnotation {
42314246

42324247
_syntaxHighlighter = SyntaxHighlighter(
42334248
language: _language,
4249+
extraLanguages: _extraLanguages,
42344250
editorTheme: _editorTheme,
42354251
baseTextStyle: _textStyle,
42364252
languageId: languageId,
@@ -4464,8 +4480,10 @@ class _CodeFieldRenderer extends RenderBox implements MouseTrackerAnnotation {
44644480
}
44654481
_syntaxHighlighter = SyntaxHighlighter(
44664482
language: language,
4483+
extraLanguages: _extraLanguages,
44674484
editorTheme: theme,
44684485
baseTextStyle: textStyle,
4486+
languageId: languageId,
44694487
);
44704488
_paragraphCache.clear();
44714489
_bracketCache.clear();
@@ -4481,8 +4499,10 @@ class _CodeFieldRenderer extends RenderBox implements MouseTrackerAnnotation {
44814499
} catch (_) {}
44824500
_syntaxHighlighter = SyntaxHighlighter(
44834501
language: lang,
4502+
extraLanguages: _extraLanguages,
44844503
editorTheme: editorTheme,
44854504
baseTextStyle: textStyle,
4505+
languageId: languageId,
44864506
);
44874507
_paragraphCache.clear();
44884508
_bracketCache.clear();
@@ -4504,8 +4524,10 @@ class _CodeFieldRenderer extends RenderBox implements MouseTrackerAnnotation {
45044524
} catch (_) {}
45054525
_syntaxHighlighter = SyntaxHighlighter(
45064526
language: language,
4527+
extraLanguages: _extraLanguages,
45074528
editorTheme: editorTheme,
45084529
baseTextStyle: style,
4530+
languageId: languageId,
45094531
);
45104532

45114533
_paragraphCache.clear();
@@ -4534,6 +4556,25 @@ class _CodeFieldRenderer extends RenderBox implements MouseTrackerAnnotation {
45344556
markNeedsPaint();
45354557
}
45364558

4559+
set extraLanguages(List<Mode> value) {
4560+
if (listEquals(value, _extraLanguages)) return;
4561+
_extraLanguages = value;
4562+
try {
4563+
_syntaxHighlighter.dispose();
4564+
} catch (_) {}
4565+
_syntaxHighlighter = SyntaxHighlighter(
4566+
language: language,
4567+
extraLanguages: _extraLanguages,
4568+
editorTheme: editorTheme,
4569+
baseTextStyle: textStyle,
4570+
languageId: languageId,
4571+
);
4572+
_paragraphCache.clear();
4573+
_bracketCache.clear();
4574+
markNeedsLayout();
4575+
markNeedsPaint();
4576+
}
4577+
45374578
set readOnly(bool value) {
45384579
if (_readOnly == value) return;
45394580
_readOnly = value;
@@ -4852,6 +4893,13 @@ class _CodeFieldRenderer extends RenderBox implements MouseTrackerAnnotation {
48524893

48534894
final newText = controller.text;
48544895
final previousText = _lastProcessedText ?? newText;
4896+
final textChanged = newText != previousText;
4897+
4898+
if (textChanged) {
4899+
_indentGuideCache.clear();
4900+
_indentEndLineCache.clear();
4901+
_lineIndentCache.clear();
4902+
}
48554903

48564904
final dirtyRange = controller.dirtyRegion;
48574905
if (dirtyRange != null) {
@@ -5483,7 +5531,9 @@ class _CodeFieldRenderer extends RenderBox implements MouseTrackerAnnotation {
54835531

54845532
String? _extractOpeningTagName(String lineText) {
54855533
final trimmed = lineText.trimRight();
5486-
final openTagMatch = RegExp(r'<(\w+)(?:\s[^>]*)?>$').firstMatch(trimmed);
5534+
final openTagMatch = RegExp(
5535+
r'<([A-Za-z][A-Za-z0-9:_-]*)(?:\s[^>]*)?>$',
5536+
).firstMatch(trimmed);
54875537
if (openTagMatch != null) {
54885538
final tagName = openTagMatch.group(1);
54895539
if (!trimmed.endsWith('/>') && !trimmed.endsWith('-->')) {
@@ -7482,6 +7532,12 @@ class _CodeFieldRenderer extends RenderBox implements MouseTrackerAnnotation {
74827532
void processLine(int i) {
74837533
if (hasActiveFolds && _isLineFolded(i)) return;
74847534

7535+
final cachedBlocks = _indentGuideCache[i];
7536+
if (cachedBlocks != null) {
7537+
blocks.addAll(cachedBlocks);
7538+
return;
7539+
}
7540+
74857541
String lineText;
74867542
if (_lineTextCache.containsKey(i)) {
74877543
lineText = _lineTextCache[i]!;
@@ -7498,15 +7554,21 @@ class _CodeFieldRenderer extends RenderBox implements MouseTrackerAnnotation {
74987554
trimmed.endsWith(':');
74997555

75007556
final openingTagName = _extractOpeningTagName(lineText);
7557+
final normalizedLangId = languageId?.toLowerCase() ?? '';
75017558
final isTagBasedLanguage =
75027559
_language.name == 'html' ||
75037560
_language.name == 'xml' ||
75047561
(_language.aliases?.contains('html') ?? false) ||
75057562
(_language.aliases?.contains('xml') ?? false) ||
7563+
(_language.aliases?.contains('tsx') ?? false) ||
7564+
(_language.aliases?.contains('jsx') ?? false) ||
75067565
(languageId?.contains('html') ?? false) ||
7507-
(languageId?.contains('xml') ?? false);
7566+
(languageId?.contains('xml') ?? false) ||
7567+
normalizedLangId.contains('tsx') ||
7568+
normalizedLangId.contains('jsx');
75087569

75097570
if (!endsWithBracket && (!isTagBasedLanguage || openingTagName == null)) {
7571+
_indentGuideCache[i] = const [];
75107572
return;
75117573
}
75127574

@@ -7531,19 +7593,25 @@ class _CodeFieldRenderer extends RenderBox implements MouseTrackerAnnotation {
75317593
endLine = _findIndentBasedEndLine(i, leadingSpaces, hasActiveFolds);
75327594
}
75337595
} else if (openingTagName != null && isTagBasedLanguage) {
7534-
final matchLine = _findMatchingClosingTagLine(openingTagName, i);
7535-
if (matchLine != null) {
7536-
endLine = matchLine + 1;
7596+
final lspFold = controller.lspFoldRanges?[i];
7597+
if (lspFold != null && lspFold.endIndex > i) {
7598+
endLine = lspFold.endIndex + 1;
75377599
} else {
7538-
endLine = _findIndentBasedEndLine(i, leadingSpaces, hasActiveFolds);
7600+
final matchLine = _findMatchingClosingTagLine(openingTagName, i);
7601+
if (matchLine != null) {
7602+
endLine = matchLine + 1;
7603+
} else {
7604+
endLine = _findIndentBasedEndLine(i, leadingSpaces, hasActiveFolds);
7605+
}
75397606
}
75407607
} else {
75417608
endLine = _findIndentBasedEndLine(i, leadingSpaces, hasActiveFolds);
75427609
}
75437610

7544-
if (endLine <= i + 1) return;
7545-
7546-
if (endLine < firstVisibleLine) return;
7611+
if (endLine <= i + 1) {
7612+
_indentGuideCache[i] = const [];
7613+
return;
7614+
}
75477615

75487616
double guideX = 0;
75497617
if (leadingSpaces > 0) {
@@ -7585,14 +7653,22 @@ class _CodeFieldRenderer extends RenderBox implements MouseTrackerAnnotation {
75857653
}
75867654
}
75877655

7588-
if (wouldPassThroughText) return;
7656+
if (wouldPassThroughText) {
7657+
_indentGuideCache[i] = const [];
7658+
return;
7659+
}
75897660

7590-
blocks.add((
7661+
final block = (
75917662
startLine: i,
75927663
endLine: endLine,
75937664
indentLevel: indentLevel,
75947665
guideX: guideX,
7595-
));
7666+
);
7667+
7668+
_indentGuideCache[i] = [block];
7669+
if (endLine >= firstVisibleLine) {
7670+
blocks.add(block);
7671+
}
75967672
}
75977673

75987674
final scanBackLimit = 500;

lib/code_forge/controller.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3523,11 +3523,11 @@ class CodeForgeController implements DeltaTextInputClient {
35233523
case 26:
35243524
return 'typeParameter';
35253525
case 252:
3526-
return 'type'; // Type alias
3526+
return 'type';
35273527
case 253:
35283528
return 'parameter';
35293529
case 254:
3530-
return 'variable'; // StaticMethod
3530+
return 'variable';
35313531
case 255:
35323532
return 'macro';
35333533
default:

0 commit comments

Comments
 (0)