LCOV - code coverage report
Current view: top level - lib/third_party/linkify - uri.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 1 65 1.5 %
Date: 2024-09-10 17:47:43 Functions: 0 0 -

          Line data    Source code
       1             : // Originally from linkify: https://github.com/Cretezy/linkify/blob/dfb3e43b0e56452bad584ddb0bf9b73d8db0589f/lib/src/url.dart
       2             : //
       3             : // Removed handling of `removeWWW` and `humanize`.
       4             : // Removed auto-appending of `http(s)://` to the readable url
       5             : //
       6             : // MIT License
       7             : //
       8             : // Copyright (c) 2019 Charles-William Crete
       9             : //
      10             : // Permission is hereby granted, free of charge, to any person obtaining a copy
      11             : // of this software and associated documentation files (the "Software"), to deal
      12             : // in the Software without restriction, including without limitation the rights
      13             : // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      14             : // copies of the Software, and to permit persons to whom the Software is
      15             : // furnished to do so, subject to the following conditions:
      16             : //
      17             : // The above copyright notice and this permission notice shall be included in all
      18             : // copies or substantial portions of the Software.
      19             : //
      20             : // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      21             : // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             : // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      23             : // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             : // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      25             : // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
      26             : // SOFTWARE.
      27             : 
      28             : import 'package:cwtch/config.dart';
      29             : 
      30             : import 'linkify.dart';
      31             : 
      32           0 : final _urlRegex = RegExp(
      33             :   r'^(.*?)((?:https?:\/\/|www\.)[^\s/$.?#].[^\s]*)',
      34             :   caseSensitive: false,
      35             :   dotAll: true,
      36             : );
      37             : 
      38           0 : final _looseUrlRegex = RegExp(
      39             :   r'^(.*?)((https?:\/\/)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,16}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*))',
      40             :   caseSensitive: false,
      41             :   dotAll: true,
      42             : );
      43             : 
      44             : class Formatter {
      45             :   final RegExp expression;
      46             :   final LinkifyElement Function(String) element;
      47             : 
      48           0 :   Formatter(this.expression, this.element);
      49             : }
      50             : 
      51             : // regex to match **bold**
      52           0 : final _boldRegex = RegExp(
      53             :   r'^(.*?)(\*\*([^*]+)\*\*)',
      54             :   caseSensitive: false,
      55             :   dotAll: true,
      56             : );
      57             : 
      58             : // regex to match *italic*
      59           0 : final _italicRegex = RegExp(
      60             :   r'^(.*?)(\*([^*]+)\*)',
      61             :   caseSensitive: false,
      62             :   dotAll: true,
      63             : );
      64             : 
      65             : // regex to match ^superscript^
      66           0 : final _superRegex = RegExp(
      67             :   r'^(.*?)(\^([^\^]*)\^)',
      68             :   caseSensitive: false,
      69             :   dotAll: true,
      70             : );
      71             : 
      72             : // regex to match ^subscript^
      73           0 : final _subRegex = RegExp(
      74             :   r'^(.*?)(\_([^\_]*)\_)',
      75             :   caseSensitive: false,
      76             :   dotAll: true,
      77             : );
      78             : 
      79             : // regex to match ~~strikethrough~~
      80           0 : final _strikeRegex = RegExp(
      81             :   r'^(.*?)(\~\~([^\~]*)\~\~)',
      82             :   caseSensitive: false,
      83             :   dotAll: true,
      84             : );
      85             : 
      86             : // regex to match `code`
      87           0 : final _codeRegex = RegExp(
      88             :   r'^(.*?)(\`([^\`]*)\`)',
      89             :   caseSensitive: false,
      90             :   dotAll: true,
      91             : );
      92             : 
      93             : class UrlLinkifier extends Linkifier {
      94           4 :   const UrlLinkifier();
      95             : 
      96           0 :   List<LinkifyElement> replaceAndParse(tle, TextElement element, RegExpMatch match, List<LinkifyElement> list, options) {
      97           0 :     final text = element.text.replaceFirst(match.group(0)!, '');
      98             : 
      99           0 :     if (match.group(1)?.isNotEmpty == true) {
     100           0 :       list.addAll(parse([TextElement(match.group(1)!)], options));
     101             :     }
     102             : 
     103           0 :     if (match.group(2)?.isNotEmpty == true) {
     104           0 :       list.add(tle(match.group(2)!));
     105             :     }
     106             : 
     107           0 :     if (text.isNotEmpty) {
     108           0 :       list.addAll(parse([TextElement(text)], options));
     109             :     }
     110             :     return list;
     111             :   }
     112             : 
     113           0 :   List<LinkifyElement> parseFormatting(element, options) {
     114           0 :     var list = <LinkifyElement>[];
     115             : 
     116             :     // code -> bold -> italic -> super -> sub -> strike
     117             :     // not we don't currently allow combinations of these elements the first
     118             :     // one to match a given set will be the only style applied - this will be fixed
     119           0 :     final formattingPrecedence = [
     120           0 :       Formatter(_codeRegex, CodeElement.new),
     121           0 :       Formatter(_boldRegex, BoldElement.new),
     122           0 :       Formatter(_italicRegex, ItalicElement.new),
     123           0 :       Formatter(_superRegex, SuperElement.new),
     124             :       // Formatter(_subRegex, SubElement.new),
     125           0 :       Formatter(_strikeRegex, StrikeElement.new)
     126             :     ];
     127             : 
     128             :     // Loop through the formatters in with precedence and break when something is found...
     129           0 :     for (var formatter in formattingPrecedence) {
     130           0 :       var formattingMatch = formatter.expression.firstMatch(element.text);
     131             :       if (formattingMatch != null) {
     132           0 :         list = replaceAndParse(formatter.element, element, formattingMatch, list, options);
     133             :         break;
     134             :       }
     135             :     }
     136             : 
     137             :     // catch all case where we didn't match anything and so need to return back
     138             :     // the unformatted text
     139             :     // conceptually this is Formatter((.*), TextElement.new)
     140           0 :     if (list.isEmpty) {
     141           0 :       list.add(element);
     142             :     }
     143             : 
     144             :     return list;
     145             :   }
     146             : 
     147           0 :   @override
     148             :   List<LinkifyElement> parse(elements, options) {
     149           0 :     var list = <LinkifyElement>[];
     150             : 
     151           0 :     elements.forEach((element) {
     152           0 :       if (element is TextElement) {
     153           0 :         if (options.parseLinks == false && options.messageFormatting == false) {
     154           0 :           list.add(element);
     155           0 :         } else if (options.parseLinks == true) {
     156             :           // check if there is a link...
     157           0 :           var match = options.looseUrl ? _looseUrlRegex.firstMatch(element.text) : _urlRegex.firstMatch(element.text);
     158             : 
     159             :           // if not then we only have to consider formatting...
     160             :           if (match == null) {
     161             :             // only do formatting if message formatting is enabled
     162           0 :             if (options.messageFormatting == false) {
     163           0 :               list.add(element);
     164             :             } else {
     165             :               // add all the formatting elements contained in this text
     166           0 :               list.addAll(parseFormatting(element, options));
     167             :             }
     168             :           } else {
     169           0 :             final text = element.text.replaceFirst(match.group(0)!, '');
     170             : 
     171           0 :             if (match.group(1)?.isNotEmpty == true) {
     172             :               // we match links first and the feed everything before the link
     173             :               // back through the parser
     174           0 :               list.addAll(parse([TextElement(match.group(1)!)], options));
     175             :             }
     176             : 
     177           0 :             if (match.group(2)?.isNotEmpty == true) {
     178           0 :               var originalUrl = match.group(2)!;
     179             :               String? end;
     180             : 
     181           0 :               if ((options.excludeLastPeriod) && originalUrl[originalUrl.length - 1] == ".") {
     182             :                 end = ".";
     183           0 :                 originalUrl = originalUrl.substring(0, originalUrl.length - 1);
     184             :               }
     185             : 
     186             :               var url = originalUrl;
     187             : 
     188             :               // If protocol has not been specified then append a protocol
     189             :               // to the start of the URL so that it can be opened...
     190           0 :               if (!url.startsWith("https://") && !url.startsWith("http://")) {
     191           0 :                 url = "https://" + url;
     192             :               }
     193             : 
     194           0 :               list.add(UrlElement(url, originalUrl));
     195             : 
     196             :               if (end != null) {
     197           0 :                 list.add(TextElement(end));
     198             :               }
     199             :             }
     200             : 
     201           0 :             if (text.isNotEmpty) {
     202           0 :               list.addAll(parse([TextElement(text)], options));
     203             :             }
     204             :           }
     205           0 :         } else if (options.messageFormatting == true) {
     206             :           // we can jump straight to message formatting...
     207           0 :           list.addAll(parseFormatting(element, options));
     208             :         } else {
     209             :           // unreachable - if we get here then there is something wrong in the above logic since every combination of
     210             :           // formatting options should have already been accounted for.
     211           0 :           EnvironmentConfig.debugLog("'unreachable' code path in formatting has been triggered. this is very likely a bug - please report $options");
     212             :         }
     213             :       }
     214             :     });
     215             : 
     216             :     return list;
     217             :   }
     218             : }
     219             : 
     220             : /// Represents an element containing a link
     221             : class UrlElement extends LinkableElement {
     222           0 :   UrlElement(String url, [String? text]) : super(text, url);
     223             : 
     224           0 :   @override
     225             :   String toString() {
     226           0 :     return "LinkElement: '$url' ($text)";
     227             :   }
     228             : 
     229           0 :   @override
     230           0 :   bool operator ==(other) => equals(other);
     231             : 
     232           0 :   @override
     233           0 :   bool equals(other) => other is UrlElement && super.equals(other);
     234             : }

Generated by: LCOV version 1.14