Skip to content

Commit 6ee1d63

Browse files
authored
User Tagging Linkifier (#38)
1 parent dfb3e43 commit 6ee1d63

File tree

4 files changed

+91
-1
lines changed

4 files changed

+91
-1
lines changed

lib/linkify.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import 'package:linkify/src/email.dart';
22
import 'package:linkify/src/url.dart';
3+
import 'package:linkify/src/user_tag.dart';
34

45
export 'package:linkify/src/email.dart' show EmailLinkifier, EmailElement;
56
export 'package:linkify/src/url.dart' show UrlLinkifier, UrlElement;
7+
export 'package:linkify/src/user_tag.dart'
8+
show UserTagLinkifier, UserTagElement;
69

710
abstract class LinkifyElement {
811
final String text;

lib/src/user_tag.dart

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import 'package:linkify/linkify.dart';
2+
3+
/// For details on how this RegEx works, go to this link.
4+
/// https://regex101.com/r/QN046t/1
5+
final _userTagRegex = RegExp(
6+
r'^(.*?)(?<![\w@])@([\w@]+(?:[.!][\w@]+)*)',
7+
caseSensitive: false,
8+
dotAll: true,
9+
);
10+
11+
class UserTagLinkifier extends Linkifier {
12+
const UserTagLinkifier();
13+
14+
@override
15+
List<LinkifyElement> parse(elements, options) {
16+
final list = <LinkifyElement>[];
17+
18+
elements.forEach((element) {
19+
if (element is TextElement) {
20+
final match = _userTagRegex.firstMatch(element.text);
21+
22+
if (match == null) {
23+
list.add(element);
24+
} else {
25+
final text = element.text.replaceFirst(match.group(0)!, '');
26+
27+
if (match.group(1)?.isNotEmpty == true) {
28+
list.add(TextElement(match.group(1)!));
29+
}
30+
31+
if (match.group(2)?.isNotEmpty == true) {
32+
list.add(UserTagElement('@${match.group(2)!}'));
33+
}
34+
35+
if (text.isNotEmpty) {
36+
list.addAll(parse([TextElement(text)], options));
37+
}
38+
}
39+
} else {
40+
list.add(element);
41+
}
42+
});
43+
44+
return list;
45+
}
46+
}
47+
48+
/// Represents an element containing an user tag
49+
class UserTagElement extends LinkableElement {
50+
final String userTag;
51+
52+
UserTagElement(this.userTag) : super(userTag, userTag);
53+
54+
@override
55+
String toString() {
56+
return "UserTagElement: '$userTag' ($text)";
57+
}
58+
59+
@override
60+
bool operator ==(other) => equals(other);
61+
62+
@override
63+
bool equals(other) =>
64+
other is UserTagElement &&
65+
super.equals(other) &&
66+
other.userTag == userTag;
67+
}

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: linkify
2-
description: Low-level link (text, URLs, emails) parsing library in Dart.
2+
description: Low-level link (text, URLs, emails, user tagging) parsing library in Dart.
33
version: 4.0.0
44
homepage: https://github.com/Cretezy/linkify
55

test/linkify_test.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,4 +212,24 @@ void main() {
212212
],
213213
);
214214
});
215+
216+
test('Parses user tag', () {
217+
expectListEqual(
218+
linkify("@example"),
219+
[UserTagElement("@example")],
220+
);
221+
});
222+
223+
test('Parses email, link, and user tag', () {
224+
expectListEqual(
225+
linkify("[email protected] at https://google.com @example"),
226+
[
227+
EmailElement("[email protected]"),
228+
TextElement(" at "),
229+
UrlElement("https://google.com", "google.com"),
230+
TextElement(" "),
231+
UserTagElement("@example")
232+
],
233+
);
234+
});
215235
}

0 commit comments

Comments
 (0)