Handle code blocks that use > 3 backticks in syntax highlighting (#10021) * Add code blocks with more than 3 backticks * Handle backticks correctly * Update NEXT_RELEASE_NOTES.md * Address Gemini's comment It won't happen here, but if the grammar somehow referenced a match that didn't exist
diff --git a/packages/devtools_app/assets/dart_syntax.json b/packages/devtools_app/assets/dart_syntax.json index f05d62d..f746ff4 100644 --- a/packages/devtools_app/assets/dart_syntax.json +++ b/packages/devtools_app/assets/dart_syntax.json
@@ -1,6 +1,6 @@ { "name": "Dart", - "version": "1.6.0", + "version": "1.8.0", "fileTypes": [ "dart" ], @@ -76,12 +76,12 @@ "repository": { "dartdoc-codeblock-triple": { - "begin": "^\\s*///\\s*(?!\\s*```)", + "begin": "^\\s*///\\s*", "end": "$", "contentName": "variable.other.source.dart" }, "dartdoc-codeblock-block": { - "begin": "^\\s*\\*\\s*(?!(\\s*```|\/))", + "begin": "^\\s*\\*\\s*", "end": "$", "contentName": "variable.other.source.dart" }, @@ -96,8 +96,8 @@ } }, { - "begin": "^\\s*///\\s*(```)", - "end": "^\\s*///\\s*(```)|^(?!\\s*///)", + "begin": "^\\s*///\\s*(`{3,})", + "end": "^\\s*///\\s*(?=\\1)`*|^(?!\\s*///)", "patterns": [ { "include": "#dartdoc-codeblock-triple" @@ -105,8 +105,8 @@ ] }, { - "begin": "^\\s*\\*\\s*(```)", - "end": "^\\s*\\*\\s*(```)|^(?=\\s*\\*\/)", + "begin": "^\\s*\\*\\s*(`{3,})", + "end": "^\\s*\\*\\s*(?=\\1)`*|^(?=\\s*\\*\/)", "patterns": [ { "include": "#dartdoc-codeblock-block" @@ -118,7 +118,7 @@ "name": "variable.other.source.dart" }, { - "match": "(?:\\*|\\/\\/)\\s{4,}(.*?)(?=($|\\*\\/))", + "match": "(?:\\*|\\/\\/)\\s{5,}(.*?)(?=($|\\*\\/))", "captures": { "1": { "name": "variable.other.source.dart"
diff --git a/packages/devtools_app/lib/src/screens/debugger/span_parser.dart b/packages/devtools_app/lib/src/screens/debugger/span_parser.dart index 062abf0..872aef3 100644 --- a/packages/devtools_app/lib/src/screens/debugger/span_parser.dart +++ b/packages/devtools_app/lib/src/screens/debugger/span_parser.dart
@@ -403,10 +403,9 @@ Grammar grammar, LineScanner scanner, ScopeStack scopeStack, + RegExp resolvedEnd, ) { - while (!scanner.isDone && - end != null && - !scanner.matchesOnCurrentLine(end!)) { + while (!scanner.isDone && !scanner.matchesOnCurrentLine(resolvedEnd)) { bool foundMatch = false; for (final pattern in patterns ?? <GrammarMatcher>[]) { if (pattern.scan(grammar, scanner, scopeStack)) { @@ -421,14 +420,43 @@ } } - void _scanEnd(Grammar grammar, LineScanner scanner, ScopeStack scopeStack) { + void _scanEnd( + Grammar grammar, + LineScanner scanner, + ScopeStack scopeStack, + RegExp resolvedEnd, + ) { final location = scanner.location; - if (end != null && !scanner.scanOnCurrentLine(end!)) { + if (!scanner.scanOnCurrentLine(resolvedEnd)) { return; } _processCaptureHelper(grammar, scanner, scopeStack, endCaptures, location); } + /// Returns an updated `end` regex that handles references in the form `\\1` + /// to reference matches from the `begin` regex. + /// + /// For example, this allows matching the correct number of backticks to close + /// a code block as were used to open it. + RegExp _resolveEnd(Match beginMatch) { + final resolvedPattern = end!.pattern.replaceAllMapped( + RegExp(r'\\([0-9]+)'), + (match) { + final groupIndex = int.parse(match.group(1)!); + final capturedText = groupIndex <= beginMatch.groupCount + ? beginMatch.group(groupIndex) + : null; + return capturedText == null + // No capture, keep the original text. + ? match.group(0)! + // Otherwise, escape the referenced text in case it contains regex + // chars. + : RegExp.escape(capturedText); + }, + ); + return RegExp(resolvedPattern, multiLine: true); + } + void _processCaptureHelper( Grammar grammar, LineScanner scanner, @@ -456,10 +484,13 @@ scopeStack.push(name, scanner.location); _scanBegin(grammar, scanner, scopeStack); if (end != null) { + // Resolve any references in the end regex that reference captures from + // the begin match. + final resolvedEnd = _resolveEnd(scanner.lastMatch!); scopeStack.push(contentName, scanner.location); - _scanUpToEndMatch(grammar, scanner, scopeStack); + _scanUpToEndMatch(grammar, scanner, scopeStack, resolvedEnd); scopeStack.pop(contentName, scanner.location); - _scanEnd(grammar, scanner, scopeStack); + _scanEnd(grammar, scanner, scopeStack, resolvedEnd); } else if (whileCond != null) { // Find the range of the string that is matched by the while condition. final start = scanner.position;
diff --git a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md index 993227c..b309745 100644 --- a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md +++ b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
@@ -62,6 +62,9 @@ narrow, such as when DevTools is embedded in an IDE side panel. The controls now scroll horizontally instead of overflowing. [#9949](https://github.com/flutter/devtools/pull/9949) +* Improved syntax highlighting of fenced code blocks that use more than three + backticks. + [#10021](https://github.com/flutter/devtools/pull/10021) ## Network profiler updates
diff --git a/packages/devtools_app/test/screens/debugger/span_parser_test.dart b/packages/devtools_app/test/screens/debugger/span_parser_test.dart index 42f6464..f542740 100644 --- a/packages/devtools_app/test/screens/debugger/span_parser_test.dart +++ b/packages/devtools_app/test/screens/debugger/span_parser_test.dart
@@ -108,7 +108,7 @@ // These goldens are updated using the usual Flutter --update-goldens // flag: // - // flutter test test/shared/span_parser_test.dart --update-goldens + // flutter test test/screens/debugger/span_parser_test.dart --update-goldens final testFiles = testDataDirectory.listSync().whereType<File>().where( (file) => path.extension(file.path) == '.dart', );
diff --git a/packages/devtools_app/test/test_infra/goldens/syntax_highlighting/comments.dart.golden b/packages/devtools_app/test/test_infra/goldens/syntax_highlighting/comments.dart.golden index d010c2f..de79f28 100644 --- a/packages/devtools_app/test/test_infra/goldens/syntax_highlighting/comments.dart.golden +++ b/packages/devtools_app/test/test_infra/goldens/syntax_highlighting/comments.dart.golden
@@ -100,3 +100,59 @@ >var h; #^^^ storage.type.primitive.dart # ^ punctuation.terminator.dart +> +>/// Multiline dartdoc comment with more than three backticks. +#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ comment.block.documentation.dart +>/// +#^^^ comment.block.documentation.dart +>/// `````` +#^^^^^^^^^^ comment.block.documentation.dart +>/// code in six backticks +#^^^^ comment.block.documentation.dart +# ^^^^^^^^^^^^^^^^^^^^^ comment.block.documentation.dart variable.other.source.dart +>/// ``` +#^^^^ comment.block.documentation.dart +# ^^^ comment.block.documentation.dart variable.other.source.dart +>/// three backticks is nested code +#^^^^ comment.block.documentation.dart +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ comment.block.documentation.dart variable.other.source.dart +>/// ``` +#^^^^ comment.block.documentation.dart +# ^^^ comment.block.documentation.dart variable.other.source.dart +>/// more code +#^^^^ comment.block.documentation.dart +# ^^^^^^^^^ comment.block.documentation.dart variable.other.source.dart +>/// `````` +#^^^^^^^^^^ comment.block.documentation.dart +>/// +#^^^ comment.block.documentation.dart +>/// Text +#^^^^^^^^ comment.block.documentation.dart +>var moreBackticks; +#^^^ storage.type.primitive.dart +# ^ punctuation.terminator.dart +> +>/// Multiline dartdoc comment with more closing backticks. +#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ comment.block.documentation.dart +>/// +#^^^ comment.block.documentation.dart +>/// `````` +#^^^^^^^^^^ comment.block.documentation.dart +>/// code in six backticks +#^^^^ comment.block.documentation.dart +# ^^^^^^^^^^^^^^^^^^^^^ comment.block.documentation.dart variable.other.source.dart +>/// ``` +#^^^^ comment.block.documentation.dart +# ^^^ comment.block.documentation.dart variable.other.source.dart +>/// still code +#^^^^ comment.block.documentation.dart +# ^^^^^^^^^^ comment.block.documentation.dart variable.other.source.dart +>/// `````````````````` +#^^^^^^^^^^^^^^^^^^^^^^ comment.block.documentation.dart +>/// +#^^^ comment.block.documentation.dart +>/// Text +#^^^^^^^^ comment.block.documentation.dart +>var moreClosingBackticks; +#^^^ storage.type.primitive.dart +# ^ punctuation.terminator.dart
diff --git a/packages/devtools_app/test/test_infra/test_data/syntax_highlighting/comments.dart b/packages/devtools_app/test/test_infra/test_data/syntax_highlighting/comments.dart index 44e03b1..a052b8d 100644 --- a/packages/devtools_app/test/test_infra/test_data/syntax_highlighting/comments.dart +++ b/packages/devtools_app/test/test_infra/test_data/syntax_highlighting/comments.dart
@@ -46,3 +46,27 @@ /// Dartdoc with reference to [a]. /// And a link to [example.org](http://example.org/). var h; + +/// Multiline dartdoc comment with more than three backticks. +/// +/// `````` +/// code in six backticks +/// ``` +/// three backticks is nested code +/// ``` +/// more code +/// `````` +/// +/// Text +var moreBackticks; + +/// Multiline dartdoc comment with more closing backticks. +/// +/// `````` +/// code in six backticks +/// ``` +/// still code +/// `````````````````` +/// +/// Text +var moreClosingBackticks;