@@ -3,8 +3,6 @@ import 'dart:io';
33import 'dart:math' ;
44import 'dart:ui' as ui;
55
6- import 'package:flutter/foundation.dart' ;
7-
86import '../LSP/lsp.dart' ;
97import 'controller.dart' ;
108import 'find_controller.dart' ;
@@ -17,6 +15,7 @@ import 'package:re_highlight/re_highlight.dart';
1715import 'package:re_highlight/styles/vs2015.dart' ;
1816import 'package:re_highlight/languages/dart.dart' ;
1917import 'package:markdown_widget/markdown_widget.dart' ;
18+ import 'package:flutter/foundation.dart' ;
2019import 'package:flutter/gestures.dart' ;
2120import 'package:flutter/material.dart' ;
2221import '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 ;
0 commit comments