From 5c1bf06a60281c4c52ab74f6725deef79d2fc1d9 Mon Sep 17 00:00:00 2001 From: Steve Gehrman Date: Mon, 5 May 2025 12:12:26 +0900 Subject: [PATCH 01/10] linting --- .vscode/settings.json | 53 ++++++ analysis_options.yaml | 7 +- example/lib/main.dart | 131 +++++++-------- example/pubspec.lock | 34 ++-- lib/src/_compile_keywords.dart | 26 +-- lib/src/_compiler_extensions.dart | 12 +- lib/src/_html_renderer.dart | 19 +-- lib/src/_mode_compiler.dart | 114 ++++++------- lib/src/_regex.dart | 62 +++---- lib/src/_response.dart | 6 +- lib/src/_text_span_render.dart | 55 ++----- lib/src/ext/_multi_class.dart | 54 +++--- lib/src/exts/_before_match.dart | 14 +- lib/src/generated/modes.dart | 145 +++++++++------- lib/src/highlight.dart | 229 ++++++++++++++------------ lib/src/mode.dart | 21 ++- lib/src/plugins/fast_json_plugin.dart | 14 +- lib/src/re_highlight.dart | 9 +- lib/src/renderer.dart | 6 +- lib/src/text_span_render.dart | 10 +- lib/src/token_tree.dart | 25 +-- pubspec.yaml | 15 +- 22 files changed, 561 insertions(+), 500 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6b6bcc8 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,53 @@ +{ + "workbench.editor.scrollToSwitchTabs": true, + "workbench.editor.limit.enabled": true, + "dart.closingLabels": false, + // copied from: https://dartcode.org/docs/recommended-settings/ + // Causes the debug view to automatically appear when a breakpoint is hit. This + // setting is global and not configurable per-language. + "debug.openDebug": "openOnDebugBreak", + "[yaml]": { + "editor.formatOnSave": true + }, + "[dart]": { + // "editor.formatOnType": true, + // Disables built-in highlighting of words that match your selection. Without + // this, all instances of the selected text will be highlighted, interfering + // with Dart's ability to highlight only exact references to the selected variable. + "editor.selectionHighlight": false, + // By default, VS Code prevents code completion from popping open when in + // "snippet mode" (editing placeholders in inserted code). Setting this option + // to `false` stops that and allows completion to open as normal, as if you + // weren't in a snippet placeholder. + "editor.suggest.snippetsPreventQuickSuggestions": false, + // By default, VS Code will pre-select the most recently used item from code + // completion. This is usually not the most relevant item. + // + // "first" will always select top item + // "recentlyUsedByPrefix" will filter the recently used items based on the + // text immediately preceeding where completion was invoked. + "editor.suggestSelection": "first", + // Allows pressing to complete snippets such as `for` even when the + // completion list is not visible. + "editor.tabCompletion": "onlySnippets", + // By default, VS Code will populate code completion with words found in the + // current file when a language service does not provide its own completions. + // This results in code completion suggesting words when editing comments and + // strings. This setting will prevent that. + "editor.wordBasedSuggestions": "off" + }, + "editor.bracketPairColorization.enabled": true, + "editor.codeActionsOnSave": { + "source.fixAll": "explicit", + "source.organizeImports": "explicit", + "quickfix.insertSemicolon": "explicit", + "source.fixAll.eslint": "explicit" + }, + "editor.formatOnSave": true, + "prettier.singleQuote": true, + "prettier.trailingComma": "all", + "dart.runPubGetOnPubspecChanges": "never", + "xmlTools.splitXmlnsOnFormat": false, + "xmlTools.splitAttributesOnFormat": true, + "arb-editor.suppressedWarnings": "all" +} diff --git a/analysis_options.yaml b/analysis_options.yaml index a5744c1..b7c2ee6 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,4 +1,7 @@ include: package:flutter_lints/flutter.yaml -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options +analyzer: + exclude: + - lib/languages/*.dart + - lib/styles/*.dart + - lib/styles/**/*.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index 40dc293..34ed1e2 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -6,6 +6,8 @@ import 'package:re_highlight/styles/all.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { + const MyApp({super.key}); + @override Widget build(BuildContext context) { return MaterialApp( @@ -15,14 +17,13 @@ class MyApp extends StatelessWidget { } class MyHomePage extends StatefulWidget { + const MyHomePage({super.key}); @override - _MyHomePageState createState() => _MyHomePageState(); - + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { - String? _language; String? _theme; @@ -36,77 +37,66 @@ class _MyHomePageState extends State { ), body: Padding( padding: const EdgeInsets.all(10), - child: Column( - children: [ - Row( - children: [ - SizedBox( - width: 100, - child: Text('Language'), - ), - DropdownButton( - items: builtinAllLanguages.keys.map( - (language) => DropdownMenuItem( - value: language, - child: Text(language) - ) - ).toList(), + child: Column(children: [ + Row( + children: [ + SizedBox( + width: 100, + child: Text('Language'), + ), + DropdownButton( + items: builtinAllLanguages.keys + .map((language) => DropdownMenuItem( + value: language, child: Text(language))) + .toList(), value: _language, onChanged: (value) { setState(() { _language = value; - _controller.languages = value == null ? const [] : [value]; + _controller.languages = + value == null ? const [] : [value]; }); - } - ), - ], - ), - Row( - children: [ - SizedBox( - width: 100, - child: Text('Theme'), - ), - DropdownButton( - items: builtinAllThemes.keys.map( - (theme) => DropdownMenuItem( - value: theme, - child: Text(theme) - ) - ).toList(), + }), + ], + ), + Row( + children: [ + SizedBox( + width: 100, + child: Text('Theme'), + ), + DropdownButton( + items: builtinAllThemes.keys + .map((theme) => DropdownMenuItem( + value: theme, child: Text(theme))) + .toList(), value: _theme, onChanged: (value) { setState(() { _theme = value; - _controller.theme = value == null ? const {} : builtinAllThemes[value] ?? const {}; + _controller.theme = value == null + ? const {} + : builtinAllThemes[value] ?? const {}; }); - } - ), - ], - ), - Expanded( - child: Container( - child: TextField( - controller: _controller, - maxLines: null, - expands: true, - textAlign: TextAlign.start, - textAlignVertical: TextAlignVertical.top, - decoration: InputDecoration( - border: OutlineInputBorder() - ) - ), - ) - ) - ] - ), + }), + ], + ), + Expanded( + child: TextField( + controller: _controller, + maxLines: null, + expands: true, + textAlign: TextAlign.start, + textAlignVertical: TextAlignVertical.top, + decoration: InputDecoration(border: OutlineInputBorder())), + ) + ]), ), ); } } class CodeThemeController extends TextEditingController { - List languages; Map theme; @@ -122,26 +112,19 @@ class CodeThemeController extends TextEditingController { } @override - TextSpan buildTextSpan({ - required BuildContext context, - TextStyle? style, - required bool withComposing - }) { + TextSpan buildTextSpan( + {required BuildContext context, + TextStyle? style, + required bool withComposing}) { if (languages.isEmpty || theme.isEmpty) { return super.buildTextSpan( - context: context, - style: style, - withComposing: withComposing - ); + context: context, style: style, withComposing: withComposing); } final HighlightResult result = _highlight.highlightAuto(text, languages); final TextSpanRenderer renderer = TextSpanRenderer(style, theme); result.render(renderer); - return renderer.span ?? super.buildTextSpan( - context: context, - style: style, - withComposing: withComposing - ); + return renderer.span ?? + super.buildTextSpan( + context: context, style: style, withComposing: withComposing); } - -} \ No newline at end of file +} diff --git a/example/pubspec.lock b/example/pubspec.lock index 393c553..bcd08f4 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" collection: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.1" flutter: dependency: "direct main" description: flutter @@ -26,38 +26,38 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.16.0" path: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" re_highlight: dependency: "direct main" description: path: ".." relative: true source: path - version: "0.0.1" + version: "0.0.3" sky_engine: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" vector_math: dependency: transitive description: @@ -66,14 +66,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - web: - dependency: transitive - description: - name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 - url: "https://pub.dev" - source: hosted - version: "0.3.0" sdks: - dart: ">=3.2.0-194.0.dev <4.0.0" + dart: ">=3.7.2 <4.0.0" flutter: ">=1.17.0" diff --git a/lib/src/_compile_keywords.dart b/lib/src/_compile_keywords.dart index fa2d65b..1313741 100644 --- a/lib/src/_compile_keywords.dart +++ b/lib/src/_compile_keywords.dart @@ -1,4 +1,4 @@ -part of re_highlight; +part of 're_highlight.dart'; const String _kDefaultKeywordScope = "keyword"; // keywords that should have no default relevance value @@ -13,7 +13,7 @@ const List _kCommonKeywords = [ 'then', 'parent', // common variable name 'list', // common variable name - 'value' // common variable name + 'value', // common variable name ]; class _KeywordData { @@ -33,19 +33,21 @@ class _KeywordData { if (other is! _KeywordData) { return false; } - return other.scopeName == scopeName - && other.score == score; + return other.scopeName == scopeName && other.score == score; } @override String toString() { return '[$scopeName, $score]'; } - } /// Given raw keywords from a language definition, compile them. -Map _compileKeywords(dynamic rawKeywords, bool? caseInsensitive, [scopeName = _kDefaultKeywordScope]) { +Map _compileKeywords( + dynamic rawKeywords, + bool? caseInsensitive, [ + scopeName = _kDefaultKeywordScope, +]) { final Map compiledKeywords = {}; /// Compiles an individual list of keywords @@ -56,7 +58,10 @@ Map _compileKeywords(dynamic rawKeywords, bool? caseInsens } for (final String keyword in keywordList) { final List pair = keyword.split('|'); - compiledKeywords[pair[0]] = _KeywordData(scopeName, _scoreForKeyword(pair[0], pair.length > 1 ? pair[1] : null)); + compiledKeywords[pair[0]] = _KeywordData( + scopeName, + _scoreForKeyword(pair[0], pair.length > 1 ? pair[1] : null), + ); } } @@ -68,11 +73,12 @@ Map _compileKeywords(dynamic rawKeywords, bool? caseInsens compileList(scopeName, rawKeywords); } else if (rawKeywords is Map) { for (final MapEntry entry in rawKeywords.entries) { - compiledKeywords.addAll(_compileKeywords(entry.value, caseInsensitive, entry.key)); + compiledKeywords.addAll( + _compileKeywords(entry.value, caseInsensitive, entry.key), + ); } } return compiledKeywords; - } /// Returns the proper score for a given keyword @@ -91,4 +97,4 @@ double _scoreForKeyword(String keyword, String? providedScore) { /// Determines if a given keyword is common or not bool _commonKeyword(String keyword) { return _kCommonKeywords.contains(keyword.toLowerCase()); -} \ No newline at end of file +} diff --git a/lib/src/_compiler_extensions.dart b/lib/src/_compiler_extensions.dart index beef9e1..84506a8 100644 --- a/lib/src/_compiler_extensions.dart +++ b/lib/src/_compiler_extensions.dart @@ -1,4 +1,4 @@ -part of re_highlight; +part of 're_highlight.dart'; typedef CompilerExt = void Function(Mode mode, Mode? parent); @@ -10,7 +10,10 @@ typedef CompilerExt = void Function(Mode mode, Mode? parent); /// `bob.keyword.do()`. The mode compiler automatically wires this up as a /// special _internal_ 'on:begin' callback for modes with `beginKeywords` /// -void _skipIfHasPrecedingDot(EnhancedMatch match, ModeCallbackResponse response) { +void _skipIfHasPrecedingDot( + EnhancedMatch match, + ModeCallbackResponse response, +) { final String? before = match.start > 0 ? match.input[match.start - 1] : null; if (before == '.') { response.ignoreMatch(); @@ -43,7 +46,8 @@ void _beginKeywords(Mode mode, Mode? parent) { // or whitespace - this does no harm in any case since our keyword engine // doesn't allow spaces in keywords anyways and we still check for the boundary // first - mode.begin = '\\b(${mode.beginKeywords!.split(' ').join('|')})(?!\\.)(?=\\b|\\s)'; + mode.begin = + '\\b(${mode.beginKeywords!.split(' ').join('|')})(?!\\.)(?=\\b|\\s)'; mode.beforeBegin = _skipIfHasPrecedingDot; mode.keywords = mode.keywords ?? mode.beginKeywords; mode.beginKeywords = null; @@ -75,4 +79,4 @@ void _compileMatch(Mode mode, Mode? parent) { void _compileRelevance(Mode mode, Mode? parent) { // eslint-disable-next-line no-undefined mode.relevance ??= 1; -} \ No newline at end of file +} diff --git a/lib/src/_html_renderer.dart b/lib/src/_html_renderer.dart index 08dba2c..fbfe0a5 100644 --- a/lib/src/_html_renderer.dart +++ b/lib/src/_html_renderer.dart @@ -1,13 +1,14 @@ -part of re_highlight; +part of 're_highlight.dart'; const String _kSpanClose = ''; String _escapeHTML(String value) { - return value.replaceAll(r'&', '&') - .replaceAll(RegExp(r'<'), '<') - .replaceAll(RegExp(r'>'), '>') - .replaceAll(RegExp(r'"'), '"') - .replaceAll(RegExp(r"'"), '''); + return value + .replaceAll(r'&', '&') + .replaceAll(RegExp(r'<'), '<') + .replaceAll(RegExp(r'>'), '>') + .replaceAll(RegExp(r'"'), '"') + .replaceAll(RegExp(r"'"), '''); } /// Determines if a node needs to be wrapped in @@ -25,7 +26,7 @@ String scopeToCSSClass(String name, String prefix) { final List pieces = name.split('.'); return [ '$prefix${pieces.removeAt(0)}', - ...(pieces.mapIndexed((i, x) => '$x${'_' * (i + 1)}')) + ...(pieces.mapIndexed((i, x) => '$x${'_' * (i + 1)}')), ].join(' '); } // simple scope @@ -33,7 +34,6 @@ String scopeToCSSClass(String name, String prefix) { } class _HTMLRenderer implements HighlightRenderer { - final _TokenTree parseTree; final String classPrefix; @@ -83,5 +83,4 @@ class _HTMLRenderer implements HighlightRenderer { void span(String className) { buffer += ''; } - -} \ No newline at end of file +} diff --git a/lib/src/_mode_compiler.dart b/lib/src/_mode_compiler.dart index 6fc1cb4..f2912cf 100644 --- a/lib/src/_mode_compiler.dart +++ b/lib/src/_mode_compiler.dart @@ -1,4 +1,4 @@ -part of re_highlight; +part of 're_highlight.dart'; /// Builds a regex with the case sensitivity of the current language RegExp _langRe(Mode language, String value, bool global) { @@ -6,7 +6,7 @@ RegExp _langRe(Mode language, String value, bool global) { value, multiLine: true, caseSensitive: !(language.caseInsensitive ?? false), - unicode: language.unicodeRegex ?? false + unicode: language.unicodeRegex ?? false, ); } @@ -14,7 +14,6 @@ RegExp _langRe(Mode language, String value, bool global) { /// Given the raw result of a language definition (Language), compiles this so /// that it is ready for highlighting code. Mode _compileLanguage(Mode language) { - RegExp langRe(String value, [bool global = false]) { return _langRe(language, value, global); } @@ -22,20 +21,15 @@ Mode _compileLanguage(Mode language) { _ResumableMultiRegex buildModeRegex(Mode language, Mode mode) { final _ResumableMultiRegex mm = _ResumableMultiRegex(language); - mode.contains?.forEach((term) => mm.addRule(term.begin, _RegexOption( - rule: term, - type: 'begin' - ))); + mode.contains?.forEach( + (term) => mm.addRule(term.begin, _RegexOption(rule: term, type: 'begin')), + ); if (mode.terminatorEnd != null) { - mm.addRule(mode.terminatorEnd!, _RegexOption( - type: 'end' - )); + mm.addRule(mode.terminatorEnd!, _RegexOption(type: 'end')); } if (mode.illegal != null) { - mm.addRule(mode.illegal, _RegexOption( - type: 'illegal' - )); + mm.addRule(mode.illegal, _RegexOption(type: 'illegal')); } return mm; @@ -56,11 +50,12 @@ Mode _compileLanguage(Mode language) { } void extractRef(Mode mode, Mode? parent) { - mode.contains = mode.contains?.cast().map((m) { - m = findRef(m); - m.variants = m.variants?.cast().map(findRef).toList(); - return m; - }).toList(); + mode.contains = + mode.contains?.cast().map((m) { + m = findRef(m); + m.variants = m.variants?.cast().map(findRef).toList(); + return m; + }).toList(); mode.variants = mode.variants?.cast().map(findRef).toList(); mode.starts = mode.starts != null ? findRef(mode.starts!) : null; } @@ -77,7 +72,7 @@ Mode _compileLanguage(Mode language) { // the distinction between match/begin _compileMatch, _multiClass, - _beforeMatchExt + _beforeMatchExt, ]) { ext(mode, parent); } @@ -93,7 +88,7 @@ Mode _compileLanguage(Mode language) { // raw array if they wanted to perhaps manipulate it, etc. _compileIllegal, // default to 1 relevance if not specified - _compileRelevance + _compileRelevance, ]) { ext(mode, parent); } @@ -105,7 +100,8 @@ Mode _compileLanguage(Mode language) { mode.isCompiled = true; String? keywordPattern; - if (mode.keywords is Map && mode.keywords['\$pattern'] != null) { + if (mode.keywords is Map && + mode.keywords['\$pattern'] != null) { // we need a copy because keywords might be compiled multiple times // so we can't go deleting $pattern from the original on the first // pass @@ -131,7 +127,10 @@ Mode _compileLanguage(Mode language) { } cmode.terminatorEnd = cmode.end ?? ''; if (mode.endsWithParent == true && parent.terminatorEnd != null) { - cmode.terminatorEnd = cmode.terminatorEnd! + (mode.end != null ? '|' : '') + parent.terminatorEnd!; + cmode.terminatorEnd = + cmode.terminatorEnd! + + (mode.end != null ? '|' : '') + + parent.terminatorEnd!; } } if (mode.illegal != null) { @@ -156,15 +155,17 @@ Mode _compileLanguage(Mode language) { language.compilerExtensions ??= []; // self is not valid at the top-level - if (language.contains != null && language.contains!.indexWhere((element) => element.self == true) != -1) { - throw AssertionError('ERR: contains `self` is not supported at the top-level of a language. See documentation.'); + if (language.contains != null && + language.contains!.indexWhere((element) => element.self == true) != -1) { + throw AssertionError( + 'ERR: contains `self` is not supported at the top-level of a language. See documentation.', + ); } // we need a null object, which inherit will guarantee language.classNameAliases = Map.of(language.classNameAliases ?? {}); return compileMode(language); - } /// Determines if a mode has a dependency on it's parent or not @@ -187,9 +188,14 @@ bool dependencyOnParent(Mode? mode) { /// exploded into their own individual modes at compile time. List _expandOrCloneMode(Mode mode) { if (mode.variants != null && mode.cachedVariants == null) { - mode.cachedVariants = mode.variants!.cast().map((variant) { - return mode.copyWith(variant)..variants = null; - }).toList().cast(); + mode.cachedVariants = + mode.variants! + .cast() + .map((variant) { + return mode.copyWith(variant)..variants = null; + }) + .toList() + .cast(); } // EXPAND @@ -222,7 +228,6 @@ List _expandOrCloneMode(Mode mode) { /// This is how we keep track of which mode matched, and what type of rule /// (`illegal`, `begin`, end, etc). class _MultiRegex { - final Mode language; final Map matchIndexes; final List> regexes; @@ -231,11 +236,11 @@ class _MultiRegex { int? lastIndex; _MatcherRe? matcherRe; - _MultiRegex(this.language) : - matchIndexes = {}, - regexes = [], - matchAt = 1, - position = 0; + _MultiRegex(this.language) + : matchIndexes = {}, + regexes = [], + matchAt = 1, + position = 0; // @ts-ignore void addRule(String re, _RegexOption opts) { @@ -247,8 +252,15 @@ class _MultiRegex { } void compile() { - List terminators = regexes.map((e) => e.last).cast().toList(); - matcherRe = _MatcherRe(_langRe(language, _rewriteBackreferences(terminators, joinWith: '|'), true)); + List terminators = + regexes.map((e) => e.last).cast().toList(); + matcherRe = _MatcherRe( + _langRe( + language, + _rewriteBackreferences(terminators, joinWith: '|'), + true, + ), + ); lastIndex = 0; } @@ -279,7 +291,7 @@ class _MultiRegex { end: match.end, rule: option.rule, type: option.type, - position: option.position + position: option.position, ); } } @@ -314,7 +326,6 @@ class _MultiRegex { /// /// MOST of the time the parser will be setting startAt manually to 0. class _ResumableMultiRegex { - final Mode language; final Map multiRegexes; final List> rules; @@ -322,12 +333,12 @@ class _ResumableMultiRegex { int lastIndex; int regexIndex; - _ResumableMultiRegex(this.language) : - multiRegexes = {}, - rules = [], - count = 0, - lastIndex = 0, - regexIndex = 0; + _ResumableMultiRegex(this.language) + : multiRegexes = {}, + rules = [], + count = 0, + lastIndex = 0, + regexIndex = 0; _MultiRegex getMatcher(int index) { if (multiRegexes[index] != null) { @@ -396,7 +407,8 @@ class _ResumableMultiRegex { if (result != null && result.index == lastIndex) { // result is position +0 and therefore a valid // "resume" match so result stays result - } else { // use the second matcher result + } else { + // use the second matcher result final _MultiRegex m2 = getMatcher(0); m2.lastIndex = lastIndex + 1; result = m2.exec(s); @@ -412,20 +424,14 @@ class _ResumableMultiRegex { } return result; } - } class _RegexOption { - final Mode? rule; final String type; late int position; - _RegexOption({ - this.rule, - required this.type - }); - + _RegexOption({this.rule, required this.type}); } class EnhancedMatch extends _RegexOption { @@ -454,7 +460,6 @@ class EnhancedMatch extends _RegexOption { } class _MatcherRe { - RegExp regex; int? lastIndex; @@ -463,5 +468,4 @@ class _MatcherRe { RegExpMatch? exec(String input) { return regex.allMatches(input, lastIndex ?? 0).firstOrNull; } - -} \ No newline at end of file +} diff --git a/lib/src/_regex.dart b/lib/src/_regex.dart index 4fdf00e..558836b 100644 --- a/lib/src/_regex.dart +++ b/lib/src/_regex.dart @@ -1,4 +1,4 @@ -part of re_highlight; +part of 're_highlight.dart'; // BACKREF_RE matches an open parenthesis or backreference. To avoid // an incorrect parse, it additionally matches the following: @@ -33,34 +33,38 @@ int _countMatchGroups(String re) { // it also places each individual regular expression into it's own // match group, keeping track of the sequencing of those match groups // is currently an exercise for the caller. :-) -String _rewriteBackreferences(List regexps, { - required String joinWith +String _rewriteBackreferences( + List regexps, { + required String joinWith, }) { int numCaptures = 0; - return regexps.map((regex) { - numCaptures += 1; - final int offset = numCaptures; - String out = ''; - while (regex.isNotEmpty) { - final RegExpMatch? match = RegExp(_backrefRe).firstMatch(regex); - if (match == null) { - out += regex; - break; - } - final String match0 = match[0]!; - final String? match1 = match[1]; - out += regex.substring(0, match.start); - regex = regex.substring(match.start +match0.length); - if (match0[0] == '\\' && match1 != null) { - // Adjust the backreference. - out += '\\${int.parse(match1) + offset}'; - } else { - out += match0; - if (match0 == '(') { - numCaptures++; + return regexps + .map((regex) { + numCaptures += 1; + final int offset = numCaptures; + String out = ''; + while (regex.isNotEmpty) { + final RegExpMatch? match = RegExp(_backrefRe).firstMatch(regex); + if (match == null) { + out += regex; + break; + } + final String match0 = match[0]!; + final String? match1 = match[1]; + out += regex.substring(0, match.start); + regex = regex.substring(match.start + match0.length); + if (match0[0] == '\\' && match1 != null) { + // Adjust the backreference. + out += '\\${int.parse(match1) + offset}'; + } else { + out += match0; + if (match0 == '(') { + numCaptures++; + } + } } - } - } - return out; - }).map((re) => '($re)').join(joinWith); -} \ No newline at end of file + return out; + }) + .map((re) => '($re)') + .join(joinWith); +} diff --git a/lib/src/_response.dart b/lib/src/_response.dart index 4276ed6..bcb4795 100644 --- a/lib/src/_response.dart +++ b/lib/src/_response.dart @@ -1,7 +1,6 @@ -part of re_highlight; +part of 're_highlight.dart'; class ModeCallbackResponse { - late final Map data; late bool isMatchIgnored; @@ -13,5 +12,4 @@ class ModeCallbackResponse { void ignoreMatch() { isMatchIgnored = true; } - -} \ No newline at end of file +} diff --git a/lib/src/_text_span_render.dart b/lib/src/_text_span_render.dart index 819bd72..6936fad 100644 --- a/lib/src/_text_span_render.dart +++ b/lib/src/_text_span_render.dart @@ -1,15 +1,12 @@ -part of re_highlight; +part of 're_highlight.dart'; class _TextSpanRenderer implements TextSpanRenderer { - final TextStyle? _base; final Map _theme; final List<_HighlighTextSpan> _stack; final List _results; - _TextSpanRenderer(this._base, this._theme) : - _stack = [], - _results = []; + _TextSpanRenderer(this._base, this._theme) : _stack = [], _results = []; @override void addText(String text) { @@ -18,15 +15,13 @@ class _TextSpanRenderer implements TextSpanRenderer { } else { final _HighlighTextSpan top = _stack.removeLast(); if (top.node.scope != null) { - _stack.add(top.addSpan(TextSpan( - text: text, - style: _theme[top.node.scope] ?? _base - ))); + _stack.add( + top.addSpan( + TextSpan(text: text, style: _theme[top.node.scope] ?? _base), + ), + ); } else { - _stack.add(top.addSpan(TextSpan( - text: text, - style: _base - ))); + _stack.add(top.addSpan(TextSpan(text: text, style: _base))); } } } @@ -35,7 +30,7 @@ class _TextSpanRenderer implements TextSpanRenderer { void closeNode(DataNode node) { final TextSpan top = _stack.removeLast(); if (_stack.isNotEmpty) { - _stack.last = _stack.last.addSpan(top); + _stack.last = _stack.last.addSpan(top); } else { _results.add(top); } @@ -43,10 +38,7 @@ class _TextSpanRenderer implements TextSpanRenderer { @override void openNode(DataNode node) { - _stack.add(_HighlighTextSpan( - node: node, - style: _base - )); + _stack.add(_HighlighTextSpan(node: node, style: _base)); } @override @@ -57,38 +49,25 @@ class _TextSpanRenderer implements TextSpanRenderer { if (_results.length == 1) { return _results.first; } - return TextSpan( - children: _results, - style: _base - ); + return TextSpan(children: _results, style: _base); } - } class _HighlighTextSpan extends TextSpan { - final DataNode node; const _HighlighTextSpan({ required this.node, - String? text, - List? children, - TextStyle? style - }) : super( - text: text, - children: children, - style: style - ); + super.text, + super.children, + super.style, + }); _HighlighTextSpan addSpan(TextSpan span) { return _HighlighTextSpan( node: node, style: style, - children: [ - ...children ?? [], - span - ] + children: [...children ?? [], span], ); } - -} \ No newline at end of file +} diff --git a/lib/src/ext/_multi_class.dart b/lib/src/ext/_multi_class.dart index cd42916..52b4e67 100644 --- a/lib/src/ext/_multi_class.dart +++ b/lib/src/ext/_multi_class.dart @@ -1,4 +1,4 @@ -part of re_highlight; +part of '../re_highlight.dart'; class _CompiledScope { Map? positions; @@ -6,13 +6,7 @@ class _CompiledScope { bool? multi; String? wrap; - _CompiledScope({ - this.positions, - this.emit, - this.multi, - this.wrap - }); - + _CompiledScope({this.positions, this.emit, this.multi, this.wrap}); } /// Renumbers labeled scope names to account for additional inner match @@ -37,7 +31,11 @@ class _CompiledScope { /// /// We also need to know that the ONLY groups that should be output /// are 1, 2, and 5. This function handles this behavior. -_CompiledScope _remapScopeNames(Mode mode, List regexes, Map scope) { +_CompiledScope _remapScopeNames( + Mode mode, + List regexes, + Map scope, +) { int offset = 0; final Map emit = {}; final Map positions = {}; @@ -48,28 +46,26 @@ _CompiledScope _remapScopeNames(Mode mode, List regexes, Map) { return; } - if (mode.skip != null || mode.excludeBegin != null || mode.returnBegin != null) { - throw AssertionError('skip, excludeBegin, returnBegin not compatible with beginScope: {}'); + if (mode.skip != null || + mode.excludeBegin != null || + mode.returnBegin != null) { + throw AssertionError( + 'skip, excludeBegin, returnBegin not compatible with beginScope: {}', + ); } - if (mode.beginScope is! Map || mode.beginScope == null) { + if (mode.beginScope is! Map || mode.beginScope == null) { throw AssertionError('beginScope must be Map'); } mode.beginScope = _remapScopeNames(mode, mode.begin, mode.beginScope); - mode.begin = _rewriteBackreferences(mode.begin, - joinWith: '' - ); + mode.begin = _rewriteBackreferences(mode.begin, joinWith: ''); } void _endMultiClass(Mode mode) { @@ -78,7 +74,9 @@ void _endMultiClass(Mode mode) { } if (mode.skip != null || mode.excludeEnd != null || mode.returnEnd != null) { - throw AssertionError("skip, excludeEnd, returnEnd not compatible with endScope: {}"); + throw AssertionError( + "skip, excludeEnd, returnEnd not compatible with endScope: {}", + ); } if (mode.endScope is! Map || mode.endScope == null) { @@ -86,9 +84,7 @@ void _endMultiClass(Mode mode) { } mode.endScope = _remapScopeNames(mode, mode.end, mode.endScope); - mode.end = _rewriteBackreferences(mode.end, - joinWith: '' - ); + mode.end = _rewriteBackreferences(mode.end, joinWith: ''); } /// this exists only to allow `scope: {}` to be used beside `match:` @@ -110,16 +106,12 @@ void _multiClass(Mode mode, Mode? parent) { _scopeSugar(mode); if (mode.beginScope is String) { - mode.beginScope = _CompiledScope( - wrap: mode.beginScope - ); + mode.beginScope = _CompiledScope(wrap: mode.beginScope); } if (mode.endScope is String) { - mode.endScope = _CompiledScope( - wrap: mode.endScope - ); + mode.endScope = _CompiledScope(wrap: mode.endScope); } _beginMultiClass(mode); _endMultiClass(mode); -} \ No newline at end of file +} diff --git a/lib/src/exts/_before_match.dart b/lib/src/exts/_before_match.dart index 92afed6..4ec619a 100644 --- a/lib/src/exts/_before_match.dart +++ b/lib/src/exts/_before_match.dart @@ -1,4 +1,4 @@ -part of re_highlight; +part of '../re_highlight.dart'; /// allow beforeMatch to act as a "qualifier" for the match /// the full match begin must be [beforeMatch][begin] @@ -16,13 +16,11 @@ void _beforeMatchExt(Mode mode, Mode? parent) { mode.clean(); mode.keywords = originalMode.keywords; - mode.begin = _concat([originalMode.beforeMatch, _lookahead(originalMode.begin)]); - mode.starts = Mode( - relevance: 0, - contains: [ - originalMode - ] - ); + mode.begin = _concat([ + originalMode.beforeMatch, + _lookahead(originalMode.begin), + ]); + mode.starts = Mode(relevance: 0, contains: [originalMode]); mode.relevance = 0; originalMode.endsParent = true; originalMode.beforeMatch = null; diff --git a/lib/src/generated/modes.dart b/lib/src/generated/modes.dart index a9758ea..1117449 100644 --- a/lib/src/generated/modes.dart +++ b/lib/src/generated/modes.dart @@ -1,81 +1,112 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of re_highlight; +part of '../re_highlight.dart'; final APOS_STRING_MODE = Mode( - scope: 'string', - begin: "'", - end: "'", - illegal: "\\n", - contains: [Mode(begin: "\\\\[\\s\\S]", relevance: 0)]); + scope: 'string', + begin: "'", + end: "'", + illegal: "\\n", + contains: [Mode(begin: "\\\\[\\s\\S]", relevance: 0)], +); final BACKSLASH_ESCAPE = Mode(begin: "\\\\[\\s\\S]", relevance: 0); -final BINARY_NUMBER_MODE = - Mode(scope: 'number', begin: "\\b(0b[01]+)", relevance: 0); -final C_BLOCK_COMMENT_MODE = - Mode(scope: 'comment', begin: "/\\*", end: "\\*/", contains: [ - Mode( +final BINARY_NUMBER_MODE = Mode( + scope: 'number', + begin: "\\b(0b[01]+)", + relevance: 0, +); +final C_BLOCK_COMMENT_MODE = Mode( + scope: 'comment', + begin: "/\\*", + end: "\\*/", + contains: [ + Mode( scope: 'doctag', begin: "[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)", end: "(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):", excludeBegin: true, - relevance: 0), - Mode( + relevance: 0, + ), + Mode( begin: - "[ ]+((?:I|a|is|so|us|to|at|if|in|it|on|[A-Za-z]+['](d|ve|re|ll|t|s|n)|[A-Za-z]+[-][a-z]+|[A-Za-z][a-z]{2,})[.]?[:]?([.][ ]|[ ])){3}") -]); -final C_LINE_COMMENT_MODE = - Mode(scope: 'comment', begin: "//", end: "\$", contains: [ - Mode( + "[ ]+((?:I|a|is|so|us|to|at|if|in|it|on|[A-Za-z]+['](d|ve|re|ll|t|s|n)|[A-Za-z]+[-][a-z]+|[A-Za-z][a-z]{2,})[.]?[:]?([.][ ]|[ ])){3}", + ), + ], +); +final C_LINE_COMMENT_MODE = Mode( + scope: 'comment', + begin: "//", + end: "\$", + contains: [ + Mode( scope: 'doctag', begin: "[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)", end: "(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):", excludeBegin: true, - relevance: 0), - Mode( + relevance: 0, + ), + Mode( begin: - "[ ]+((?:I|a|is|so|us|to|at|if|in|it|on|[A-Za-z]+['](d|ve|re|ll|t|s|n)|[A-Za-z]+[-][a-z]+|[A-Za-z][a-z]{2,})[.]?[:]?([.][ ]|[ ])){3}") -]); + "[ ]+((?:I|a|is|so|us|to|at|if|in|it|on|[A-Za-z]+['](d|ve|re|ll|t|s|n)|[A-Za-z]+[-][a-z]+|[A-Za-z][a-z]{2,})[.]?[:]?([.][ ]|[ ])){3}", + ), + ], +); final C_NUMBER_MODE = Mode( - scope: 'number', - begin: - "(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)", - relevance: 0); -final HASH_COMMENT_MODE = - Mode(scope: 'comment', begin: "#", end: "\$", contains: [ - Mode( + scope: 'number', + begin: + "(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)", + relevance: 0, +); +final HASH_COMMENT_MODE = Mode( + scope: 'comment', + begin: "#", + end: "\$", + contains: [ + Mode( scope: 'doctag', begin: "[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)", end: "(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):", excludeBegin: true, - relevance: 0), - Mode( + relevance: 0, + ), + Mode( begin: - "[ ]+((?:I|a|is|so|us|to|at|if|in|it|on|[A-Za-z]+['](d|ve|re|ll|t|s|n)|[A-Za-z]+[-][a-z]+|[A-Za-z][a-z]{2,})[.]?[:]?([.][ ]|[ ])){3}") -]); + "[ ]+((?:I|a|is|so|us|to|at|if|in|it|on|[A-Za-z]+['](d|ve|re|ll|t|s|n)|[A-Za-z]+[-][a-z]+|[A-Za-z][a-z]{2,})[.]?[:]?([.][ ]|[ ])){3}", + ), + ], +); final METHOD_GUARD = Mode(begin: "\\.\\s*[a-zA-Z_]\\w*", relevance: 0); -final NUMBER_MODE = - Mode(scope: 'number', begin: "\\b\\d+(\\.\\d+)?", relevance: 0); +final NUMBER_MODE = Mode( + scope: 'number', + begin: "\\b\\d+(\\.\\d+)?", + relevance: 0, +); final PHRASAL_WORDS_MODE = Mode( - begin: - "\\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\\b"); + begin: + "\\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\\b", +); final QUOTE_STRING_MODE = Mode( - scope: 'string', - begin: "\"", - end: "\"", - illegal: "\\n", - contains: [Mode(begin: "\\\\[\\s\\S]", relevance: 0)]); + scope: 'string', + begin: "\"", + end: "\"", + illegal: "\\n", + contains: [Mode(begin: "\\\\[\\s\\S]", relevance: 0)], +); final REGEXP_MODE = Mode( - scope: 'regexp', - begin: "\\/(?=[^/\\n]*\\/)", - end: "\\/[gimuy]*", - contains: [ - Mode(begin: "\\\\[\\s\\S]", relevance: 0), - Mode( - begin: "\\[", - end: "\\]", - relevance: 0, - contains: [Mode(begin: "\\\\[\\s\\S]", relevance: 0)]) - ]); + scope: 'regexp', + begin: "\\/(?=[^/\\n]*\\/)", + end: "\\/[gimuy]*", + contains: [ + Mode(begin: "\\\\[\\s\\S]", relevance: 0), + Mode( + begin: "\\[", + end: "\\]", + relevance: 0, + contains: [Mode(begin: "\\\\[\\s\\S]", relevance: 0)], + ), + ], +); final TITLE_MODE = Mode(scope: 'title', begin: "[a-zA-Z]\\w*", relevance: 0); -final UNDERSCORE_TITLE_MODE = - Mode(scope: 'title', begin: "[a-zA-Z_]\\w*", relevance: 0); +final UNDERSCORE_TITLE_MODE = Mode( + scope: 'title', + begin: "[a-zA-Z_]\\w*", + relevance: 0, +); diff --git a/lib/src/highlight.dart b/lib/src/highlight.dart index 9280140..e1bdbc4 100644 --- a/lib/src/highlight.dart +++ b/lib/src/highlight.dart @@ -1,10 +1,9 @@ -part of re_highlight; +part of 're_highlight.dart'; const int _kNoMatch = -1; const int _kMaxKeywordHits = 7; class Highlight { - final List _plugins; final Map _languages; final Map _aliases; @@ -12,10 +11,7 @@ class Highlight { // even if a single syntax or parse hits a fatal error bool _safeMode; - Highlight(): _plugins = [], - _languages = {}, - _aliases = {}, - _safeMode = true; + Highlight() : _plugins = [], _languages = {}, _aliases = {}, _safeMode = true; void debugMode() { _safeMode = false; @@ -28,11 +24,11 @@ class Highlight { HighlightResult highlight({ required String code, required String language, - bool ignoreIllegals = true + bool ignoreIllegals = true, }) { final BeforeHighlightContext context = BeforeHighlightContext( code: code, - language: language + language: language, ); // the plugin can change the desired language or the code to be highlighted // just be changing the object it was passed @@ -42,8 +38,9 @@ class Highlight { // a before plugin can usurp the result completely by providing it's own // in which case we don't even need to call highlight - final HighlightResult result = context.result ?? - _highlight(context.language, context.code, ignoreIllegals); + final HighlightResult result = + context.result ?? + _highlight(context.language, context.code, ignoreIllegals); result.code = context.code; // the plugin can change anything in result to suite it @@ -70,11 +67,7 @@ class Highlight { illegal: false, relevance: 0, emitter: _TokenTreeEmitter(), - top: Mode( - disableAutodetect: true, - name: 'Plain text', - contains: [] - ) + top: Mode(disableAutodetect: true, name: 'Plain text', contains: []), ); result._emitter.addText(code); return result; @@ -88,41 +81,48 @@ class Highlight { /// - value (an HTML string with highlighting markup) /// - secondBest (object with the same structure for second-best heuristically /// detected language, may be absent) - AutoHighlightResult highlightAuto(String code, [List? languageSubset]) { + AutoHighlightResult highlightAuto( + String code, [ + List? languageSubset, + ]) { final List languages = languageSubset ?? _languages.keys.toList(); final HighlightResult plaintext = justTextHighlightResult(code); - final List results = languages - .where((e) => getLanguage(e) != null) - .where((e) => autoDetection(e)) - .map((name) => _highlight(name, code, false)) - .toList(); + final List results = + languages + .where((e) => getLanguage(e) != null) + .where((e) => autoDetection(e)) + .map((name) => _highlight(name, code, false)) + .toList(); results.insert(0, plaintext); // plaintext is always an option // Dart's list.sort() is not stable, here we need a stable sort - mergeSort(results, compare: (a, b) { - // sort base on relevance - if (a.relevance != b.relevance) { - return b.relevance - a.relevance < 0 ? -1 : 1; - } + mergeSort( + results, + compare: (a, b) { + // sort base on relevance + if (a.relevance != b.relevance) { + return b.relevance - a.relevance < 0 ? -1 : 1; + } - // always award the tie to the base language - // ie if C++ and Arduino are tied, it's more likely to be C++ - if (a.language != null && b.language != null) { - if (getLanguage(a.language!)?.supersetOf == b.language) { - return 1; - } else if (getLanguage(b.language!)?.supersetOf == a.language) { - return -1; + // always award the tie to the base language + // ie if C++ and Arduino are tied, it's more likely to be C++ + if (a.language != null && b.language != null) { + if (getLanguage(a.language!)?.supersetOf == b.language) { + return 1; + } else if (getLanguage(b.language!)?.supersetOf == a.language) { + return -1; + } } - } - // otherwise say they are equal, which has the effect of sorting on - // relevance while preserving the original ordering - which is how ties - // have historically been settled, ie the language that comes first always - // wins in the case of a tie - return 0; - }); + // otherwise say they are equal, which has the effect of sorting on + // relevance while preserving the original ordering - which is how ties + // have historically been settled, ie the language that comes first always + // wins in the case of a tie + return 0; + }, + ); return AutoHighlightResult( best: results[0], - secondBest: results.length > 1 ? results[1]: null, + secondBest: results.length > 1 ? results[1] : null, ); } @@ -169,7 +169,12 @@ class Highlight { } /// private highlight that's used internally and does not fire callbacks - HighlightResult _highlight(String languageName, String codeToHighlight, bool ignoreIllegals, [Mode? continuation]) { + HighlightResult _highlight( + String languageName, + String codeToHighlight, + bool ignoreIllegals, [ + Mode? continuation, + ]) { final Map keywordHits = {}; final Mode? language = getLanguage(languageName); if (language == null) { @@ -184,6 +189,7 @@ class Highlight { int index = 0; int iterations = 0; bool resumeScanAtSamePosition = false; + /// Return keyword data if a match is a keyword _KeywordData? keywordData(Mode mode, String matchText) { return mode.keywords[matchText]; @@ -209,7 +215,8 @@ class Highlight { while (match != null) { buf += modeBuffer.substring(lastIndex, match.start); final String match0 = match[0]!; - final String word = language.caseInsensitive == true ? match0.toLowerCase() : match0; + final String word = + language.caseInsensitive == true ? match0.toLowerCase() : match0; final _KeywordData? data = keywordData(top, word); if (data != null) { final String kind = data.scopeName; @@ -233,9 +240,8 @@ class Highlight { buf += match0; } lastIndex = match.end; - match = top.keywordPatternRe! - .allMatches(modeBuffer, lastIndex) - .firstOrNull; + match = + top.keywordPatternRe!.allMatches(modeBuffer, lastIndex).firstOrNull; } buf += modeBuffer.substring(lastIndex); emitter.addText(buf); @@ -251,12 +257,22 @@ class Highlight { emitter.addText(modeBuffer); return; } - result = _highlight(top.subLanguage, modeBuffer, true, continuations[top.subLanguage]); + result = _highlight( + top.subLanguage, + modeBuffer, + true, + continuations[top.subLanguage], + ); continuations[top.subLanguage] = result._top; } else if (top.subLanguage is List) { - result = highlightAuto(modeBuffer, top.subLanguage.isEmpty ? null : top.subLanguage); + result = highlightAuto( + modeBuffer, + top.subLanguage.isEmpty ? null : top.subLanguage, + ); } else { - throw AssertionError('Illegal subLanguage type ${top.subLanguage.runtimeType}'); + throw AssertionError( + 'Illegal subLanguage type ${top.subLanguage.runtimeType}', + ); } // Counting embedded language score towards the host language may be disabled @@ -301,14 +317,19 @@ class Highlight { } Mode startNewMode(Mode mode, EnhancedMatch match) { - if (mode.scope != null && mode.scope is String && mode.scope!.isNotEmpty) { + if (mode.scope != null && + mode.scope is String && + mode.scope!.isNotEmpty) { emitter.openNode(language.classNameAliases?[mode.scope!] ?? mode.scope); } if (mode.beginScope != null) { final _CompiledScope scope = mode.beginScope; // beginScope just wraps the begin match itself in a scope if (scope.wrap?.isNotEmpty ?? false) { - emitKeyword(modeBuffer, language.classNameAliases?[scope.wrap!] ?? scope.wrap!); + emitKeyword( + modeBuffer, + language.classNameAliases?[scope.wrap!] ?? scope.wrap!, + ); modeBuffer = ''; } else if (scope.multi == true) { // at this point modeBuffer should just be the match @@ -316,16 +337,13 @@ class Highlight { modeBuffer = ''; } } - top = mode.copyWith( - Mode( - parent: top - ) - ); + top = mode.copyWith(Mode(parent: top)); return top; } Mode? endOfMode(Mode mode, EnhancedMatch match, String matchPlusRemainder) { - bool matched = mode.endRe != null && matchPlusRemainder.startsWith(mode.endRe!); + bool matched = + mode.endRe != null && matchPlusRemainder.startsWith(mode.endRe!); if (matched) { if (mode.onEnd != null) { final ModeCallbackResponse resp = ModeCallbackResponse(mode); @@ -374,7 +392,7 @@ class Highlight { // first internal before callbacks, then the public ones final List beforeCallbacks = [ newMode.beforeBegin, - newMode.onBegin + newMode.onBegin, ]; for (final ModeCallback? cb in beforeCallbacks) { if (cb == null) { @@ -446,7 +464,11 @@ class Highlight { void processContinuations() { final List list = []; - for (Mode? current = top; current != language; current = current?.parent) { + for ( + Mode? current = top; + current != language; + current = current?.parent + ) { if (current?.scope?.isNotEmpty ?? false) { list.insert(0, current!.scope); } @@ -473,14 +495,18 @@ class Highlight { // this happens when we have badly behaved rules that have optional matchers to the degree that // sometimes they can end up matching nothing at all // Ref: https://github.com/highlightjs/highlight.js/issues/2140 - if (lastMatch?.type == "begin" && match?.type == "end" && lastMatch?.index == match!.index && lexeme.isEmpty) { + if (lastMatch?.type == "begin" && + match?.type == "end" && + lastMatch?.index == match!.index && + lexeme.isEmpty) { // spit the "skipped" character that our regex choked on back into the output sequence modeBuffer += codeToHighlight.substring(match.index, match.index + 1); if (!_safeMode) { final AnnotatedError err = AnnotatedError( '0 width match regex ($languageName)', languageName: languageName, - badRule: lastMatch!.rule); + badRule: lastMatch!.rule, + ); throw err; } return 1; @@ -491,8 +517,9 @@ class Highlight { return doBeginMatch(match!); } else if (match?.type == "illegal" && !ignoreIllegals) { // illegal match, we do not continue processing - final err = AnnotatedError('Illegal lexeme "$lexeme" for mode "${top.scope ?? ''}"', - mode: top + final err = AnnotatedError( + 'Illegal lexeme "$lexeme" for mode "${top.scope ?? ''}"', + mode: top, ); throw err; } else if (match?.type == "end") { @@ -515,7 +542,9 @@ class Highlight { // parsing) still 3x behind our index then something is very wrong // so we bail if (iterations > 100000 && iterations > match!.index * 3) { - final AssertionError err = AssertionError('potential infinite loop, way more iterations than matches'); + final AssertionError err = AssertionError( + 'potential infinite loop, way more iterations than matches', + ); throw err; } @@ -537,7 +566,8 @@ class Highlight { (top.matcher! as _ResumableMultiRegex).considerAll(); for (;;) { - final _ResumableMultiRegex matcher = top.matcher! as _ResumableMultiRegex; + final _ResumableMultiRegex matcher = + top.matcher! as _ResumableMultiRegex; iterations++; if (resumeScanAtSamePosition) { // only regexes not matched previously will now be @@ -553,7 +583,10 @@ class Highlight { break; } - final String beforeMatch = codeToHighlight.substring(index, match.index); + final String beforeMatch = codeToHighlight.substring( + index, + match.index, + ); final int processedCount = processLexeme(beforeMatch, match); index = match.index + processedCount; } @@ -565,7 +598,7 @@ class Highlight { relevance: relevance, illegal: false, emitter: emitter, - top: top + top: top, ); } on Error catch (err) { if (err is AnnotatedError && err.message.contains('Illegal')) { @@ -577,10 +610,13 @@ class Highlight { illegalBy: IllegalData( message: err.message, index: index, - context: codeToHighlight.substring(max(0, index - 100), min(index + 100, codeToHighlight.length)), + context: codeToHighlight.substring( + max(0, index - 100), + min(index + 100, codeToHighlight.length), + ), mode: err.mode, ), - emitter: emitter + emitter: emitter, ); } else if (_safeMode) { return HighlightResult( @@ -590,22 +626,19 @@ class Highlight { relevance: 0, errorRaised: err, emitter: emitter, - top: top + top: top, ); } else { rethrow; } } } - } abstract class HLPlugin { - void afterHighlight(HighlightResult result); void beforeHighlight(BeforeHighlightContext context); - } class BeforeHighlightContext { @@ -616,12 +649,11 @@ class BeforeHighlightContext { BeforeHighlightContext({ required this.code, required this.language, - this.result + this.result, }); } class HighlightResult { - String code; double relevance; String? language; @@ -640,40 +672,36 @@ class HighlightResult { this.errorRaised, IllegalData? illegalBy, required Emitter emitter, - Mode? top + Mode? top, }) : _illegalBy = illegalBy, - _emitter = emitter, - _top = top; + _emitter = emitter, + _top = top; void render(HighlightRenderer renderer) { (_emitter as _TokenTree).walk(renderer); } String toHtml([String classPrefix = 'hljs-']) { - final String value = _HTMLRenderer(_emitter as _TokenTree, classPrefix).value(); + final String value = + _HTMLRenderer(_emitter as _TokenTree, classPrefix).value(); return value.isEmpty ? _escapeHTML(code) : value; } - } class AutoHighlightResult extends HighlightResult { - HighlightResult? secondBest; - AutoHighlightResult({ - required HighlightResult best, - this.secondBest, - }) : super( - code: best.code, - relevance: best.relevance, - language: best.language, - illegal: best.illegal, - errorRaised: best.errorRaised, - illegalBy: best._illegalBy, - emitter: best._emitter, - top: best._top - ); - + AutoHighlightResult({required HighlightResult best, this.secondBest}) + : super( + code: best.code, + relevance: best.relevance, + language: best.language, + illegal: best.illegal, + errorRaised: best.errorRaised, + illegalBy: best._illegalBy, + emitter: best._emitter, + top: best._top, + ); } class IllegalData { @@ -686,7 +714,7 @@ class IllegalData { required this.message, required this.context, required this.index, - this.mode + this.mode, }); } @@ -696,10 +724,5 @@ class AnnotatedError extends Error { final String? languageName; final Mode? badRule; - AnnotatedError(this.message, { - this.mode, - this.languageName, - this.badRule - }); - -} \ No newline at end of file + AnnotatedError(this.message, {this.mode, this.languageName, this.badRule}); +} diff --git a/lib/src/mode.dart b/lib/src/mode.dart index b69a1b5..a35d0e9 100644 --- a/lib/src/mode.dart +++ b/lib/src/mode.dart @@ -1,9 +1,11 @@ -part of re_highlight; +part of 're_highlight.dart'; -typedef ModeCallback = void Function(EnhancedMatch match, ModeCallbackResponse response); +typedef ModeCallback = + void Function(EnhancedMatch match, ModeCallbackResponse response); class Mode { String? ref; + /// Map | Map> Map? refs; @@ -19,19 +21,26 @@ class Mode { /// String | List dynamic begin; + /// String | List dynamic match; + /// String | List dynamic end; + /// String | Map @Deprecated('deprecated in favor of `scope`') dynamic className; + /// String | Map dynamic scope; + /// String | Map | _CompiledScope dynamic beginScope; + /// String | Map | _CompiledScope dynamic endScope; + /// List | Mode dynamic contains; bool? endsParent; @@ -46,20 +55,25 @@ class Mode { String? beforeMatch; Mode? parent; Mode? starts; + /// String String? lexemes; + /// Map | String dynamic keywords; String? beginKeywords; double? relevance; + /// String | List dynamic illegal; + /// List | Mode dynamic variants; List? cachedVariants; dynamic subLanguage; bool? isCompiled; String? label; + /// CompiledMode String? terminatorEnd; List? compilerExtensions; @@ -201,7 +215,7 @@ class Mode { endScope: mode?.endScope ?? endScope, contains: mode?.contains ?? contains, endsParent: mode?.endsParent ?? endsParent, - endsWithParent:mode?.endsWithParent ?? endsWithParent, + endsWithParent: mode?.endsWithParent ?? endsWithParent, endSameAsBegin: mode?.endSameAsBegin ?? endSameAsBegin, skip: mode?.skip ?? skip, excludeBegin: mode?.excludeBegin ?? excludeBegin, @@ -234,5 +248,4 @@ class Mode { data: mode?.data ?? data, ); } - } diff --git a/lib/src/plugins/fast_json_plugin.dart b/lib/src/plugins/fast_json_plugin.dart index 9c8e07e..6e38f50 100644 --- a/lib/src/plugins/fast_json_plugin.dart +++ b/lib/src/plugins/fast_json_plugin.dart @@ -1,7 +1,6 @@ -part of re_highlight; +part of '../re_highlight.dart'; class FastJsonPlugin implements HLPlugin { - const FastJsonPlugin(); @override @@ -17,8 +16,7 @@ class FastJsonPlugin implements HLPlugin { } @override - void afterHighlight(HighlightResult result) { - } + void afterHighlight(HighlightResult result) {} HighlightResult? _highlightJson(String code) { final _TokenTree tokenTree = _TokenTreeEmitter(); @@ -36,11 +34,9 @@ class FastJsonPlugin implements HLPlugin { emitter: tokenTree, ); } - } class _JsonParser { - final String _code; final _TokenTree _tokenTree; int _pos = 0; @@ -86,7 +82,9 @@ class _JsonParser { } else if (char == 'n') { _parseLiteral('null'); } else { - throw FormatException('Invalid character ${_code[_pos]} at position $_pos'); + throw FormatException( + 'Invalid character ${_code[_pos]} at position $_pos', + ); } } @@ -305,4 +303,4 @@ class _JsonParser { bool _isDigit(String char) { return char.codeUnitAt(0) >= 48 && char.codeUnitAt(0) <= 57; } -} \ No newline at end of file +} diff --git a/lib/src/re_highlight.dart b/lib/src/re_highlight.dart index 894fd69..2777aa9 100644 --- a/lib/src/re_highlight.dart +++ b/lib/src/re_highlight.dart @@ -1,24 +1,21 @@ -library re_highlight; - import 'dart:math'; + import 'package:collection/collection.dart'; import 'package:flutter/widgets.dart'; -part '_compiler_extensions.dart'; part '_compile_keywords.dart'; +part '_compiler_extensions.dart'; part '_html_renderer.dart'; part '_mode_compiler.dart'; part '_regex.dart'; part '_response.dart'; part '_text_span_render.dart'; - part 'ext/_multi_class.dart'; part 'exts/_before_match.dart'; part 'generated/modes.dart'; part 'highlight.dart'; part 'mode.dart'; +part 'plugins/fast_json_plugin.dart'; part 'renderer.dart'; part 'text_span_render.dart'; part 'token_tree.dart'; - -part 'plugins/fast_json_plugin.dart'; \ No newline at end of file diff --git a/lib/src/renderer.dart b/lib/src/renderer.dart index c0ed4e8..1e69767 100644 --- a/lib/src/renderer.dart +++ b/lib/src/renderer.dart @@ -1,11 +1,9 @@ -part of re_highlight; +part of 're_highlight.dart'; abstract class HighlightRenderer { - void addText(String text); void openNode(DataNode node); void closeNode(DataNode node); - -} \ No newline at end of file +} diff --git a/lib/src/text_span_render.dart b/lib/src/text_span_render.dart index 2b6972e..e127a8a 100644 --- a/lib/src/text_span_render.dart +++ b/lib/src/text_span_render.dart @@ -1,10 +1,8 @@ -part of re_highlight; +part of 're_highlight.dart'; abstract class TextSpanRenderer implements HighlightRenderer { - - factory TextSpanRenderer(TextStyle? base, Map theme) - => _TextSpanRenderer(base, theme); + factory TextSpanRenderer(TextStyle? base, Map theme) => + _TextSpanRenderer(base, theme); TextSpan? get span; - -} \ No newline at end of file +} diff --git a/lib/src/token_tree.dart b/lib/src/token_tree.dart index 8d4ac51..9f92afe 100644 --- a/lib/src/token_tree.dart +++ b/lib/src/token_tree.dart @@ -1,4 +1,4 @@ -part of re_highlight; +part of 're_highlight.dart'; class DataNode { String? scope; @@ -6,16 +6,10 @@ class DataNode { // DataNode | String final List children; - DataNode({ - this.scope, - this.language, - required this.children - }); - + DataNode({this.scope, this.language, required this.children}); } abstract class Emitter { - DataNode get root; void addText(String text); void startScope(String scope); @@ -25,17 +19,13 @@ abstract class Emitter { void openNode(String scope); void closeNode(); void _addSublanguage(Emitter emitter, String? subLanguageName); - } abstract class _TokenTree extends Emitter { - DataNode rootNode; late List stack; - _TokenTree() : rootNode = DataNode( - children: [] - ) { + _TokenTree() : rootNode = DataNode(children: []) { stack = [rootNode]; } @@ -50,10 +40,7 @@ abstract class _TokenTree extends Emitter { @override void openNode(String scope) { - final DataNode node = DataNode( - scope: scope, - children: [] - ); + final DataNode node = DataNode(scope: scope, children: []); add(node); stack.add(node); } @@ -85,11 +72,9 @@ abstract class _TokenTree extends Emitter { builder.closeNode(node); } } - } class _TokenTreeEmitter extends _TokenTree { - @override void addText(String text) { if (text.isEmpty) { @@ -122,4 +107,4 @@ class _TokenTreeEmitter extends _TokenTree { closeAllNodes(); return true; } -} \ No newline at end of file +} diff --git a/pubspec.yaml b/pubspec.yaml index 70078a4..3e99b80 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,16 +5,19 @@ homepage: https://reqable.com repository: https://github.com/reqable/re-highlight environment: - sdk: '>=2.17.0 <4.0.0' - flutter: ">=1.17.0" + sdk: ^3.7.2 dependencies: flutter: sdk: flutter - path: ^1.8.2 - collection: ^1.17.1 + path: ^1.9.1 + collection: ^1.19.1 -dev_dependencies: +dev_dependencies: + dart_code_metrics: + git: https://github.com/JonasWanke/dart-code-metrics + dfc_lints: + git: https://github.com/sgehrman/dfc_lints flutter_test: sdk: flutter - flutter_lints: ^2.0.0 \ No newline at end of file + flutter_lints: ^5.0.0 \ No newline at end of file From e39c87d6565647a2c0acd4601be4835a80a35df0 Mon Sep 17 00:00:00 2001 From: Steve Gehrman Date: Mon, 5 May 2025 12:52:24 +0900 Subject: [PATCH 02/10] fix json parser --- lib/src/plugins/fast_json_plugin.dart | 56 ++++++++++++++------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/lib/src/plugins/fast_json_plugin.dart b/lib/src/plugins/fast_json_plugin.dart index 6e38f50..32fa25e 100644 --- a/lib/src/plugins/fast_json_plugin.dart +++ b/lib/src/plugins/fast_json_plugin.dart @@ -41,11 +41,14 @@ class _JsonParser { final _TokenTree _tokenTree; int _pos = 0; int _lastPos = 0; + late final int _codeLength; - _JsonParser(this._code, this._tokenTree); + _JsonParser(this._code, this._tokenTree) { + _codeLength = _code.length; + } void parse() { - while (_pos < _code.length) { + while (_pos < _codeLength) { _parseValue(); _skipWhitespace(); _flushText(); @@ -61,7 +64,7 @@ class _JsonParser { void _parseValue() { _skipWhitespace(); - if (_pos >= _code.length) { + if (_pos >= _codeLength) { return; } @@ -97,11 +100,11 @@ class _JsonParser { _lastPos = _pos; bool first = true; - while (_pos < _code.length) { + while (_pos < _codeLength) { _skipWhitespace(); _flushText(); - if (_pos < _code.length && _code[_pos] == '}') { + if (_pos < _codeLength && _code[_pos] == '}') { _tokenTree.openNode('punctuation'); _tokenTree.addText('}'); _tokenTree.closeNode(); @@ -111,7 +114,7 @@ class _JsonParser { } if (!first) { - if (_pos < _code.length && _code[_pos] == ',') { + if (_pos < _codeLength && _code[_pos] == ',') { _tokenTree.openNode('punctuation'); _tokenTree.addText(','); _tokenTree.closeNode(); @@ -125,7 +128,7 @@ class _JsonParser { _skipWhitespace(); _flushText(); - if (_pos < _code.length && _code[_pos] == '"') { + if (_pos < _codeLength && _code[_pos] == '"') { _parseString(true); } else { throw FormatException('Key should be a string at position $_pos'); @@ -134,7 +137,7 @@ class _JsonParser { _skipWhitespace(); _flushText(); - if (_pos < _code.length && _code[_pos] == ':') { + if (_pos < _codeLength && _code[_pos] == ':') { _tokenTree.openNode('punctuation'); _tokenTree.addText(':'); _tokenTree.closeNode(); @@ -160,11 +163,11 @@ class _JsonParser { _lastPos = _pos; bool first = true; - while (_pos < _code.length) { + while (_pos < _codeLength) { _skipWhitespace(); _flushText(); - if (_pos < _code.length && _code[_pos] == ']') { + if (_pos < _codeLength && _code[_pos] == ']') { _tokenTree.openNode('punctuation'); _tokenTree.addText(']'); _tokenTree.closeNode(); @@ -174,7 +177,7 @@ class _JsonParser { } if (!first) { - if (_pos < _code.length && _code[_pos] == ',') { + if (_pos < _codeLength && _code[_pos] == ',') { _tokenTree.openNode('punctuation'); _tokenTree.addText(','); _tokenTree.closeNode(); @@ -200,7 +203,7 @@ class _JsonParser { _pos++; bool escaped = false; - while (_pos < _code.length) { + while (_pos < _codeLength) { final char = _code[_pos]; if (escaped) { escaped = false; @@ -214,7 +217,7 @@ class _JsonParser { _pos++; } - if (_pos >= _code.length) { + if (_pos >= _codeLength) { throw const FormatException('String not closed'); } @@ -232,40 +235,40 @@ class _JsonParser { _pos++; } - if (_pos < _code.length && _code[_pos] == '0') { + if (_pos < _codeLength && _code[_pos] == '0') { _pos++; - } else if (_pos < _code.length && _isDigit(_code[_pos])) { - while (_pos < _code.length && _isDigit(_code[_pos])) { + } else if (_pos < _codeLength && _isDigit(_code[_pos])) { + while (_pos < _codeLength && _isDigit(_code[_pos])) { _pos++; } } else { throw FormatException('Invalid number at position $_pos'); } - if (_pos < _code.length && _code[_pos] == '.') { + if (_pos < _codeLength && _code[_pos] == '.') { _pos++; - if (_pos >= _code.length || !_isDigit(_code[_pos])) { + if (_pos >= _codeLength || !_isDigit(_code[_pos])) { throw const FormatException('Decimal part should have digits'); } - while (_pos < _code.length && _isDigit(_code[_pos])) { + while (_pos < _codeLength && _isDigit(_code[_pos])) { _pos++; } } - if (_pos < _code.length && (_code[_pos] == 'e' || _code[_pos] == 'E')) { + if (_pos < _codeLength && (_code[_pos] == 'e' || _code[_pos] == 'E')) { _pos++; - if (_pos < _code.length && (_code[_pos] == '+' || _code[_pos] == '-')) { + if (_pos < _codeLength && (_code[_pos] == '+' || _code[_pos] == '-')) { _pos++; } - if (_pos >= _code.length || !_isDigit(_code[_pos])) { + if (_pos >= _codeLength || !_isDigit(_code[_pos])) { throw const FormatException('Exponential part should have digits'); } - while (_pos < _code.length && _isDigit(_code[_pos])) { + while (_pos < _codeLength && _isDigit(_code[_pos])) { _pos++; } } @@ -277,7 +280,7 @@ class _JsonParser { } void _parseLiteral(String literal) { - if (_pos + literal.length > _code.length || + if (_pos + literal.length > _codeLength || _code.substring(_pos, _pos + literal.length) != literal) { throw FormatException('Expected $literal at position $_pos'); } @@ -291,7 +294,7 @@ class _JsonParser { } void _skipWhitespace() { - while (_pos < _code.length && _isWhitespace(_code[_pos])) { + while (_pos < _codeLength && _isWhitespace(_code[_pos])) { _pos++; } } @@ -301,6 +304,7 @@ class _JsonParser { } bool _isDigit(String char) { - return char.codeUnitAt(0) >= 48 && char.codeUnitAt(0) <= 57; + final int code = char.codeUnitAt(0); + return code >= 48 && code <= 57; } } From 656ed5653753422238c3a820ae806f3cd1b04145 Mon Sep 17 00:00:00 2001 From: Steve Gehrman Date: Mon, 5 May 2025 12:58:35 +0900 Subject: [PATCH 03/10] regex fix --- lib/src/_regex.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/_regex.dart b/lib/src/_regex.dart index 558836b..79720eb 100644 --- a/lib/src/_regex.dart +++ b/lib/src/_regex.dart @@ -8,6 +8,7 @@ part of 're_highlight.dart'; // - non-matching or lookahead parentheses, which do not capture. These // follow the '(' with a '?'. const String _backrefRe = r'\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\.'; +RegExp backrefRegExp = RegExp(_backrefRe); String _concat(List values) { return values.join(); @@ -44,7 +45,7 @@ String _rewriteBackreferences( final int offset = numCaptures; String out = ''; while (regex.isNotEmpty) { - final RegExpMatch? match = RegExp(_backrefRe).firstMatch(regex); + final RegExpMatch? match = backrefRegExp.firstMatch(regex); if (match == null) { out += regex; break; From 341d2eb751bfe645e37970d7202a2522b609f06b Mon Sep 17 00:00:00 2001 From: Steve Gehrman Date: Mon, 5 May 2025 13:04:04 +0900 Subject: [PATCH 04/10] regex cache --- lib/src/_regex.dart | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/src/_regex.dart b/lib/src/_regex.dart index 79720eb..9ecbbbe 100644 --- a/lib/src/_regex.dart +++ b/lib/src/_regex.dart @@ -25,7 +25,17 @@ String _either(List args) { } int _countMatchGroups(String re) { - return RegExp('$re|').allMatches('').first.groupCount; + return _getRegExp('$re|').allMatches('').first.groupCount; +} + +Map _regExpCache = {}; +RegExp _getRegExp(String re) { + if (_regExpCache.containsKey(re)) { + return _regExpCache[re]!; + } + final RegExp result = RegExp(re); + _regExpCache[re] = result; + return result; } // **INTERNAL** Not intended for outside usage From 49ca075fdb0b8114bc153357275314700e1307ed Mon Sep 17 00:00:00 2001 From: Steve Gehrman Date: Mon, 5 May 2025 13:14:38 +0900 Subject: [PATCH 05/10] sdf --- lib/languages/lib/javascript.dart | 30 ++++++++++++++++++------------ lib/src/_html_renderer.dart | 8 ++++---- lib/src/_regex.dart | 4 ++-- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/lib/languages/lib/javascript.dart b/lib/languages/lib/javascript.dart index 1692032..e1f9cf8 100644 --- a/lib/languages/lib/javascript.dart +++ b/lib/languages/lib/javascript.dart @@ -1,20 +1,26 @@ import 'package:re_highlight/re_highlight.dart'; -ModeCallback callbackOnBegin = (EnhancedMatch match, ModeCallbackResponse response) { +ModeCallback callbackOnBegin = ( + EnhancedMatch match, + ModeCallbackResponse response, +) { final String? match0 = match[0]; if (match0 == null) { return; } final int afterMatchIndex = match0.length + match.index; - final String nextChar = match.input.substring(afterMatchIndex, afterMatchIndex + 1); + final String nextChar = match.input.substring( + afterMatchIndex, + afterMatchIndex + 1, + ); if ( - // HTML should not include another raw `<` inside a tag - // nested type? - // `>`, etc. - nextChar == "<" || - // the , gives away that this is not HTML - // `` - nextChar == ",") { + // HTML should not include another raw `<` inside a tag + // nested type? + // `>`, etc. + nextChar == "<" || + // the , gives away that this is not HTML + // `` + nextChar == ",") { response.ignoreMatch(); return; } @@ -35,7 +41,7 @@ ModeCallback callbackOnBegin = (EnhancedMatch match, ModeCallbackResponse respon // some more template typing stuff // (key?: string) => Modify< - if (RegExp(r'^\s*=').hasMatch(afterMatch)) { + if (getRegExp(r'^\s*=').hasMatch(afterMatch)) { response.ignoreMatch(); return; } @@ -43,7 +49,7 @@ ModeCallback callbackOnBegin = (EnhancedMatch match, ModeCallbackResponse respon // `` // technically this could be HTML, but it smells like a type // NOTE: This is ugh, but added specifically for https://github.com/highlightjs/highlight.js/issues/3276 - final RegExpMatch? m = RegExp(r'^\s+extends\s+').firstMatch(afterMatch); + final RegExpMatch? m = getRegExp(r'^\s+extends\s+').firstMatch(afterMatch); if (m != null) { if (m.start == 0) { response.ignoreMatch(); @@ -57,4 +63,4 @@ bool _hasClosingTag(EnhancedMatch match, int after) { final String tag = "'; String _escapeHTML(String value) { return value .replaceAll(r'&', '&') - .replaceAll(RegExp(r'<'), '<') - .replaceAll(RegExp(r'>'), '>') - .replaceAll(RegExp(r'"'), '"') - .replaceAll(RegExp(r"'"), '''); + .replaceAll(getRegExp(r'<'), '<') + .replaceAll(getRegExp(r'>'), '>') + .replaceAll(getRegExp(r'"'), '"') + .replaceAll(getRegExp(r"'"), '''); } /// Determines if a node needs to be wrapped in diff --git a/lib/src/_regex.dart b/lib/src/_regex.dart index 9ecbbbe..70dea9e 100644 --- a/lib/src/_regex.dart +++ b/lib/src/_regex.dart @@ -25,11 +25,11 @@ String _either(List args) { } int _countMatchGroups(String re) { - return _getRegExp('$re|').allMatches('').first.groupCount; + return getRegExp('$re|').allMatches('').first.groupCount; } Map _regExpCache = {}; -RegExp _getRegExp(String re) { +RegExp getRegExp(String re) { if (_regExpCache.containsKey(re)) { return _regExpCache[re]!; } From a2746b9f55c04d68f7d0b0cdff45b960fefc423a Mon Sep 17 00:00:00 2001 From: Steve Gehrman Date: Mon, 5 May 2025 15:46:49 +0900 Subject: [PATCH 06/10] sdf --- lib/src/_mode_compiler.dart | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/src/_mode_compiler.dart b/lib/src/_mode_compiler.dart index f2912cf..e09d60a 100644 --- a/lib/src/_mode_compiler.dart +++ b/lib/src/_mode_compiler.dart @@ -1,13 +1,23 @@ part of 're_highlight.dart'; +Map _cache = {}; +RegExp getLangRegEx(String value, Mode language) { + final String key = '${language.name}/$value'; + + if (_cache[key] == null) { + _cache[key] = RegExp( + value, + multiLine: true, + caseSensitive: !(language.caseInsensitive ?? false), + unicode: language.unicodeRegex ?? false, + ); + } + return _cache[value]!; +} + /// Builds a regex with the case sensitivity of the current language RegExp _langRe(Mode language, String value, bool global) { - return RegExp( - value, - multiLine: true, - caseSensitive: !(language.caseInsensitive ?? false), - unicode: language.unicodeRegex ?? false, - ); + return getLangRegEx(value, language); } /// Compiles a language definition result From 099a4aa0b3a0c4f16607f395fbc4db7ef6297167 Mon Sep 17 00:00:00 2001 From: Steve Gehrman Date: Mon, 5 May 2025 16:00:05 +0900 Subject: [PATCH 07/10] sdf --- lib/src/_mode_compiler.dart | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/lib/src/_mode_compiler.dart b/lib/src/_mode_compiler.dart index e09d60a..f2912cf 100644 --- a/lib/src/_mode_compiler.dart +++ b/lib/src/_mode_compiler.dart @@ -1,23 +1,13 @@ part of 're_highlight.dart'; -Map _cache = {}; -RegExp getLangRegEx(String value, Mode language) { - final String key = '${language.name}/$value'; - - if (_cache[key] == null) { - _cache[key] = RegExp( - value, - multiLine: true, - caseSensitive: !(language.caseInsensitive ?? false), - unicode: language.unicodeRegex ?? false, - ); - } - return _cache[value]!; -} - /// Builds a regex with the case sensitivity of the current language RegExp _langRe(Mode language, String value, bool global) { - return getLangRegEx(value, language); + return RegExp( + value, + multiLine: true, + caseSensitive: !(language.caseInsensitive ?? false), + unicode: language.unicodeRegex ?? false, + ); } /// Compiles a language definition result From ee9aa0b4e40e9a36f9859b3a0d86fcb89eae2e10 Mon Sep 17 00:00:00 2001 From: Steve Gehrman Date: Mon, 5 May 2025 16:11:49 +0900 Subject: [PATCH 08/10] sdf --- lib/src/_mode_compiler.dart | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/src/_mode_compiler.dart b/lib/src/_mode_compiler.dart index f2912cf..a6233be 100644 --- a/lib/src/_mode_compiler.dart +++ b/lib/src/_mode_compiler.dart @@ -1,13 +1,24 @@ part of 're_highlight.dart'; +Map _cache = {}; +RegExp getLangRegEx(String value, Mode language) { + final String key = + '${language.caseInsensitive ?? false}/${language.unicodeRegex ?? false}/$value'; + + if (_cache[key] == null) { + _cache[key] = RegExp( + value, + multiLine: true, + caseSensitive: !(language.caseInsensitive ?? false), + unicode: language.unicodeRegex ?? false, + ); + } + return _cache[value]!; +} + /// Builds a regex with the case sensitivity of the current language RegExp _langRe(Mode language, String value, bool global) { - return RegExp( - value, - multiLine: true, - caseSensitive: !(language.caseInsensitive ?? false), - unicode: language.unicodeRegex ?? false, - ); + return getLangRegEx(value, language); } /// Compiles a language definition result From 8514029a4599dd8193f506a25eb80152d55b6c8b Mon Sep 17 00:00:00 2001 From: Steve Gehrman Date: Mon, 5 May 2025 16:19:13 +0900 Subject: [PATCH 09/10] sdf --- lib/src/_mode_compiler.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/src/_mode_compiler.dart b/lib/src/_mode_compiler.dart index a6233be..39b8e35 100644 --- a/lib/src/_mode_compiler.dart +++ b/lib/src/_mode_compiler.dart @@ -3,7 +3,7 @@ part of 're_highlight.dart'; Map _cache = {}; RegExp getLangRegEx(String value, Mode language) { final String key = - '${language.caseInsensitive ?? false}/${language.unicodeRegex ?? false}/$value'; + '${language.name}/${language.caseInsensitive ?? false}/${language.unicodeRegex ?? false}/$value'; if (_cache[key] == null) { _cache[key] = RegExp( @@ -13,7 +13,8 @@ RegExp getLangRegEx(String value, Mode language) { unicode: language.unicodeRegex ?? false, ); } - return _cache[value]!; + + return _cache[key]!; } /// Builds a regex with the case sensitivity of the current language From 74718524705f95d975a5162c8247e940f99e21b5 Mon Sep 17 00:00:00 2001 From: Steve Gehrman Date: Mon, 16 Jun 2025 15:10:54 -0700 Subject: [PATCH 10/10] sdf --- pubspec.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index 3e99b80..a3f5e64 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,11 +13,11 @@ dependencies: path: ^1.9.1 collection: ^1.19.1 -dev_dependencies: +dev_dependencies: dart_code_metrics: - git: https://github.com/JonasWanke/dart-code-metrics + git: https://github.com/JonasWanke/dart-code-metrics.git dfc_lints: - git: https://github.com/sgehrman/dfc_lints + git: https://github.com/sgehrman/dfc_lints.git flutter_test: sdk: flutter - flutter_lints: ^5.0.0 \ No newline at end of file + flutter_lints: ^5.0.0