Skip to content

Commit 2a4f4c4

Browse files
authored
Merge pull request #406 from rdestefa/issue-298-www-links
2 parents 24ddb2e + 889709c commit 2a4f4c4

File tree

5 files changed

+153
-7
lines changed

5 files changed

+153
-7
lines changed

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,24 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
66
This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
77
with the exception that 0.x versions can break between minor versions.
88

9+
## Unreleased
10+
### Added
11+
- Autolink extension: Now supports configuration of different link types that
12+
should be recognized and converted to links. See `AutolinkExtension#builder`
13+
14+
| Type | Default? | Description |
15+
|---------|----------|--------------------------------------------------------|
16+
| `URL` | Yes | URL with a protocol such as `https://example.com` |
17+
| `EMAIL` | Yes | Email address such as `[email protected]` |
18+
| `WWW` | Yes | Address beginning with `www` such as `www.example.com` |
19+
20+
Note that this changes the behavior of `AutolinkExtension.create()` to now also
21+
include `WWW` links by default. To re-enable the previous behavior, use:
22+
23+
```java
24+
AutolinkExtension.builder().linkTypes(AutolinkType.URL, AutolinkType.EMAIL).build();
25+
```
26+
927
## [0.26.0] - 2025-09-13
1028
### Changed
1129
- A `LinkProcessor` using `replaceWith` now also stops outer links from being

commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/AutolinkExtension.java

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package org.commonmark.ext.autolink;
22

3+
import java.util.EnumSet;
4+
import java.util.Set;
5+
36
import org.commonmark.Extension;
47
import org.commonmark.ext.autolink.internal.AutolinkPostProcessor;
58
import org.commonmark.parser.Parser;
@@ -18,16 +21,71 @@
1821
*/
1922
public class AutolinkExtension implements Parser.ParserExtension {
2023

21-
private AutolinkExtension() {
24+
private final Set<AutolinkType> linkTypes;
25+
26+
private AutolinkExtension(Builder builder) {
27+
this.linkTypes = builder.linkTypes;
2228
}
2329

30+
/**
31+
* @return the extension with default options
32+
*/
2433
public static Extension create() {
25-
return new AutolinkExtension();
34+
return builder().build();
35+
}
36+
37+
/**
38+
* @return a builder to configure the behavior of the extension.
39+
*/
40+
public static Builder builder() {
41+
return new Builder();
2642
}
2743

2844
@Override
2945
public void extend(Parser.Builder parserBuilder) {
30-
parserBuilder.postProcessor(new AutolinkPostProcessor());
46+
parserBuilder.postProcessor(new AutolinkPostProcessor(linkTypes));
3147
}
3248

49+
public static class Builder {
50+
51+
private Set<AutolinkType> linkTypes = EnumSet.allOf(AutolinkType.class);
52+
53+
/**
54+
* @param linkTypes the link types that should be converted. By default,
55+
* all {@link AutolinkType}s are converted.
56+
* @return {@code this}
57+
*/
58+
public Builder linkTypes(AutolinkType... linkTypes) {
59+
if (linkTypes == null) {
60+
throw new NullPointerException("linkTypes must not be null");
61+
}
62+
63+
return this.linkTypes(Set.of(linkTypes));
64+
}
65+
66+
/**
67+
* @param linkTypes the link types that should be converted. By default,
68+
* all {@link AutolinkType}s are converted.
69+
* @return {@code this}
70+
*/
71+
public Builder linkTypes(Set<AutolinkType> linkTypes) {
72+
if (linkTypes == null) {
73+
throw new NullPointerException("linkTypes must not be null");
74+
}
75+
76+
if (linkTypes.isEmpty()) {
77+
throw new IllegalArgumentException("linkTypes must not be empty");
78+
}
79+
80+
this.linkTypes = EnumSet.copyOf(linkTypes);
81+
return this;
82+
}
83+
84+
/**
85+
* @return a configured extension
86+
*/
87+
public Extension build() {
88+
return new AutolinkExtension(this);
89+
}
90+
}
3391
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.commonmark.ext.autolink;
2+
3+
/**
4+
* The types of strings that can be automatically turned into links.
5+
*/
6+
public enum AutolinkType {
7+
/**
8+
* URL such as {@code http://example.com}
9+
*/
10+
URL,
11+
/**
12+
* Email address such as {@code [email protected]}
13+
*/
14+
EMAIL,
15+
/**
16+
* URL such as {@code www.example.com}
17+
*/
18+
WWW
19+
}

commonmark-ext-autolink/src/main/java/org/commonmark/ext/autolink/internal/AutolinkPostProcessor.java

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.commonmark.ext.autolink.internal;
22

3+
import org.commonmark.ext.autolink.AutolinkType;
34
import org.commonmark.node.*;
45
import org.commonmark.parser.PostProcessor;
56
import org.nibor.autolink.LinkExtractor;
@@ -11,9 +12,36 @@
1112

1213
public class AutolinkPostProcessor implements PostProcessor {
1314

14-
private LinkExtractor linkExtractor = LinkExtractor.builder()
15-
.linkTypes(EnumSet.of(LinkType.URL, LinkType.EMAIL))
16-
.build();
15+
private final LinkExtractor linkExtractor;
16+
17+
public AutolinkPostProcessor(Set<AutolinkType> linkTypes) {
18+
if (linkTypes == null) {
19+
throw new NullPointerException("linkTypes must not be null");
20+
}
21+
22+
if (linkTypes.isEmpty()) {
23+
throw new IllegalArgumentException("linkTypes must not be empty");
24+
}
25+
26+
var types = EnumSet.noneOf(LinkType.class);
27+
for (AutolinkType linkType : linkTypes) {
28+
switch (linkType) {
29+
case URL:
30+
types.add(LinkType.URL);
31+
break;
32+
case EMAIL:
33+
types.add(LinkType.EMAIL);
34+
break;
35+
case WWW:
36+
types.add(LinkType.WWW);
37+
break;
38+
}
39+
}
40+
41+
this.linkExtractor = LinkExtractor.builder()
42+
.linkTypes(types)
43+
.build();
44+
}
1745

1846
@Override
1947
public Node process(Node node) {
@@ -67,8 +95,13 @@ private static Text createTextNode(String literal, Span span, SourceSpan sourceS
6795
}
6896

6997
private static String getDestination(LinkSpan linkSpan, String linkText) {
70-
if (linkSpan.getType() == LinkType.EMAIL) {
98+
var type = linkSpan.getType();
99+
100+
if (type == LinkType.EMAIL) {
71101
return "mailto:" + linkText;
102+
} else if (type == LinkType.WWW) {
103+
// Use http instead of https (see https://github.github.com/gfm/#extended-www-autolink)
104+
return "http://" + linkText;
72105
} else {
73106
return linkText;
74107
}

commonmark-ext-autolink/src/test/java/org/commonmark/ext/autolink/AutolinkTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ public class AutolinkTest extends RenderingTestCase {
1919
private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS).build();
2020
private static final HtmlRenderer RENDERER = HtmlRenderer.builder().extensions(EXTENSIONS).build();
2121

22+
private static final Set<Extension> NO_WWW_EXTENSIONS = Set.of(AutolinkExtension.builder()
23+
.linkTypes(AutolinkType.URL, AutolinkType.EMAIL)
24+
.build());
25+
private static final Parser NO_WWW_PARSER = Parser.builder().extensions(NO_WWW_EXTENSIONS).build();
26+
private static final HtmlRenderer NO_WWW_RENDERER = HtmlRenderer.builder().extensions(NO_WWW_EXTENSIONS).build();
27+
2228
@Test
2329
public void oneTextNode() {
2430
assertRendering("foo http://one.org/ bar http://two.org/",
@@ -57,6 +63,18 @@ public void dontLinkTextWithinLinks() {
5763
"<p><a href=\"http://example.com\">http://example.com</a></p>\n");
5864
}
5965

66+
@Test
67+
public void wwwLinks() {
68+
assertRendering("www.example.com",
69+
"<p><a href=\"http://www.example.com\">www.example.com</a></p>\n");
70+
}
71+
72+
@Test
73+
public void noWwwLinks() {
74+
String html = NO_WWW_RENDERER.render(NO_WWW_PARSER.parse("www.example.com"));
75+
assertThat(html).isEqualTo("<p>www.example.com</p>\n");
76+
}
77+
6078
@Test
6179
public void sourceSpans() {
6280
Parser parser = Parser.builder()

0 commit comments

Comments
 (0)