feat: support multiple links insertion on the go (#579)

pull/587/head
Karlo Verde 3 years ago committed by GitHub
parent a1822dd850
commit f20678cb95
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 105
      lib/src/models/rules/insert.dart
  2. 1
      lib/src/models/rules/rule.dart

@ -1,5 +1,6 @@
import 'package:tuple/tuple.dart';
import '../../../models/documents/document.dart';
import '../documents/attribute.dart';
import '../documents/style.dart';
import '../quill_delta.dart';
@ -286,6 +287,110 @@ class InsertEmbedsRule extends InsertRule {
}
}
/// Applies link format to text segments within the inserted text that matches
/// the URL pattern.
///
/// The link attribute is applied as the user types.
class AutoFormatMultipleLinksRule extends InsertRule {
const AutoFormatMultipleLinksRule();
/// Link pattern.
///
/// This pattern is used to match a links within a text segment.
static const _linkPattern =
r'(https?:\/\/|www\.)[\w-\.]+\.[\w-\.]+(\/([\S]+)?)?';
static final _linkRegExp = RegExp(_linkPattern);
@override
Delta? applyRule(
Delta document,
int index, {
int? len,
Object? data,
Attribute? attribute,
}) {
// Only format when inserting text.
if (data is! String) return null;
// Get current text.
final entireText = Document.fromDelta(document).toPlainText();
// Get word before insertion.
final leftWordPart = entireText
// Keep all text before insertion.
.substring(0, index)
// Keep last paragraph.
.split('\n')
.last
// Keep last word.
.split(' ')
.last
.trimLeft();
// Get word after insertion.
final rightWordPart = entireText
// Keep all text after insertion.
.substring(index)
// Keep first paragraph.
.split('\n')
.first
// Keep first word.
.split(' ')
.first
.trimRight();
// Build the segment of affected words.
final affectedWords = '$leftWordPart$data$rightWordPart';
// Check for URL pattern.
final matches = _linkRegExp.allMatches(affectedWords);
// If there are no matches, do not apply any format.
if (matches.isEmpty) return null;
// Build base delta.
// The base delta is a simple insertion delta.
final baseDelta = Delta()
..retain(index)
..insert(data);
// Get unchanged text length.
final unmodifiedLength = index - leftWordPart.length;
// Create formatter delta.
// The formatter delta will only include links formatting when needed.
final formatterDelta = Delta()..retain(unmodifiedLength);
var previousLinkEndRelativeIndex = 0;
for (final match in matches) {
// Get the size of the leading segment of text that is not part of the
// link.
final separationLength = match.start - previousLinkEndRelativeIndex;
// Get the identified link.
final link = affectedWords.substring(match.start, match.end);
// Keep the leading segment of text and add link with its proper
// attribute.
formatterDelta
..retain(separationLength, LinkAttribute(null).toJson())
..retain(link.length, LinkAttribute(link).toJson());
// Update reference index.
previousLinkEndRelativeIndex = match.end;
}
// Get remaining text length.
final remainingLength = affectedWords.length - previousLinkEndRelativeIndex;
// Remove links from remaining non-link text.
formatterDelta.retain(remainingLength, LinkAttribute(null).toJson());
// Build and return resulting change delta.
return baseDelta.compose(formatterDelta);
}
}
/// Applies link format to text segment (which looks like a link) when user
/// inserts space character after it.
class AutoFormatLinksRule extends InsertRule {

@ -43,6 +43,7 @@ class Rules {
const PreserveLineStyleOnSplitRule(),
const ResetLineFormatOnNewLineRule(),
const AutoFormatLinksRule(),
const AutoFormatMultipleLinksRule(),
const PreserveInlineStylesRule(),
const CatchAllInsertRule(),
const EnsureEmbedLineRule(),

Loading…
Cancel
Save