Add auto links: Try to render unmarked urls as links
This commit is contained in:
parent
a570fc736f
commit
d2d9dc0fc8
4 changed files with 37 additions and 3 deletions
|
@ -24,8 +24,12 @@ type RegularLink struct {
|
||||||
Protocol string
|
Protocol string
|
||||||
Description []Node
|
Description []Node
|
||||||
URL string
|
URL string
|
||||||
|
AutoLink bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var validURLCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&'()*+,;="
|
||||||
|
var autolinkProtocols = regexp.MustCompile(`(https?|ftp|file)`)
|
||||||
|
|
||||||
var redundantSpaces = regexp.MustCompile("[ \t]+")
|
var redundantSpaces = regexp.MustCompile("[ \t]+")
|
||||||
var subScriptSuperScriptRegexp = regexp.MustCompile(`([_^])\{(.*?)\}`)
|
var subScriptSuperScriptRegexp = regexp.MustCompile(`([_^])\{(.*?)\}`)
|
||||||
var footnoteRegexp = regexp.MustCompile(`\[fn:([\w-]+?)(:(.*?))?\]`)
|
var footnoteRegexp = regexp.MustCompile(`\[fn:([\w-]+?)(:(.*?))?\]`)
|
||||||
|
@ -33,7 +37,7 @@ var footnoteRegexp = regexp.MustCompile(`\[fn:([\w-]+?)(:(.*?))?\]`)
|
||||||
func (d *Document) parseInline(input string) (nodes []Node) {
|
func (d *Document) parseInline(input string) (nodes []Node) {
|
||||||
previous, current := 0, 0
|
previous, current := 0, 0
|
||||||
for current < len(input) {
|
for current < len(input) {
|
||||||
consumed, node := 0, (Node)(nil)
|
rewind, consumed, node := 0, 0, (Node)(nil)
|
||||||
switch input[current] {
|
switch input[current] {
|
||||||
case '^':
|
case '^':
|
||||||
consumed, node = d.parseSubOrSuperScript(input, current)
|
consumed, node = d.parseSubOrSuperScript(input, current)
|
||||||
|
@ -45,6 +49,9 @@ func (d *Document) parseInline(input string) (nodes []Node) {
|
||||||
consumed, node = d.parseRegularLinkOrFootnoteReference(input, current)
|
consumed, node = d.parseRegularLinkOrFootnoteReference(input, current)
|
||||||
case '\\':
|
case '\\':
|
||||||
consumed, node = d.parseExplicitLineBreak(input, current)
|
consumed, node = d.parseExplicitLineBreak(input, current)
|
||||||
|
case ':':
|
||||||
|
rewind, consumed, node = d.parseAutoLink(input, current)
|
||||||
|
current -= rewind
|
||||||
}
|
}
|
||||||
if consumed != 0 {
|
if consumed != 0 {
|
||||||
if current > previous {
|
if current > previous {
|
||||||
|
@ -125,6 +132,29 @@ func (d *Document) parseFootnoteReference(input string, start int) (int, Node) {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Document) parseAutoLink(input string, start int) (int, int, Node) {
|
||||||
|
if len(input[start:]) < 3 || input[start+1] != '/' || input[start+2] != '/' {
|
||||||
|
return 0, 0, nil
|
||||||
|
}
|
||||||
|
protocolStart, protocol := start-1, ""
|
||||||
|
for ; protocolStart > 0 && unicode.IsLetter(rune(input[protocolStart])); protocolStart-- {
|
||||||
|
}
|
||||||
|
if m := autolinkProtocols.FindStringSubmatch(input[protocolStart:start]); m != nil {
|
||||||
|
protocol = m[1]
|
||||||
|
} else {
|
||||||
|
return 0, 0, nil
|
||||||
|
}
|
||||||
|
end := start
|
||||||
|
for ; end < len(input) && strings.ContainsRune(validURLCharacters, rune(input[end])); end++ {
|
||||||
|
}
|
||||||
|
path := input[start:end]
|
||||||
|
if path == "://" {
|
||||||
|
return 0, 0, nil
|
||||||
|
}
|
||||||
|
link := RegularLink{protocol, []Node{Text{protocol + path}}, protocol + path, true}
|
||||||
|
return len(protocol), len(path + protocol), link
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Document) parseRegularLink(input string, start int) (int, Node) {
|
func (d *Document) parseRegularLink(input string, start int) (int, Node) {
|
||||||
if len(input[start:]) == 0 || input[start+1] != '[' {
|
if len(input[start:]) == 0 || input[start+1] != '[' {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
|
@ -147,7 +177,7 @@ func (d *Document) parseRegularLink(input string, start int) (int, Node) {
|
||||||
if len(parts) == 2 {
|
if len(parts) == 2 {
|
||||||
protocol = parts[0]
|
protocol = parts[0]
|
||||||
}
|
}
|
||||||
return consumed, RegularLink{protocol, description, link}
|
return consumed, RegularLink{protocol, description, link, false}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Document) parseEmphasis(input string, start int) (int, Node) {
|
func (d *Document) parseEmphasis(input string, start int) (int, Node) {
|
||||||
|
|
|
@ -246,7 +246,9 @@ func (w *OrgWriter) writeRegularLink(l RegularLink) {
|
||||||
descriptionWriter := w.emptyClone()
|
descriptionWriter := w.emptyClone()
|
||||||
descriptionWriter.writeNodes(l.Description...)
|
descriptionWriter.writeNodes(l.Description...)
|
||||||
description := descriptionWriter.String()
|
description := descriptionWriter.String()
|
||||||
if l.URL != description {
|
if l.AutoLink {
|
||||||
|
w.WriteString(l.URL)
|
||||||
|
} else if l.URL != description {
|
||||||
w.WriteString(fmt.Sprintf("[[%s][%s]]", l.URL, description))
|
w.WriteString(fmt.Sprintf("[[%s][%s]]", l.URL, description))
|
||||||
} else {
|
} else {
|
||||||
w.WriteString(fmt.Sprintf("[[%s]]", l.URL))
|
w.WriteString(fmt.Sprintf("[[%s]]", l.URL))
|
||||||
|
|
1
org/testdata/example.html
vendored
1
org/testdata/example.html
vendored
|
@ -58,6 +58,7 @@
|
||||||
<li>regular link <a href="https://example.com">https://example.com</a> link without description</li>
|
<li>regular link <a href="https://example.com">https://example.com</a> link without description</li>
|
||||||
<li>regular link <a href="https://example.com">example.com</a> link with description</li>
|
<li>regular link <a href="https://example.com">example.com</a> link with description</li>
|
||||||
<li>regular link to a file (image) <img src="my-img.png" alt="file:my-img.png" title="file:my-img.png" /></li>
|
<li>regular link to a file (image) <img src="my-img.png" alt="file:my-img.png" title="file:my-img.png" /></li>
|
||||||
|
<li>auto link, i.e. not inside <code class="verbatim">\[[square brackets]\]</code> <a href="https://www.example.com">https://www.example.com</a></li>
|
||||||
</ol>
|
</ol>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
1
org/testdata/example.org
vendored
1
org/testdata/example.org
vendored
|
@ -50,6 +50,7 @@ this one is cheating a little as tags are ALWAYS printed right aligned to a give
|
||||||
1. regular link [[https://example.com]] link without description
|
1. regular link [[https://example.com]] link without description
|
||||||
2. regular link [[https://example.com][example.com]] link with description
|
2. regular link [[https://example.com][example.com]] link with description
|
||||||
3. regular link to a file (image) [[file:my-img.png]]
|
3. regular link to a file (image) [[file:my-img.png]]
|
||||||
|
4. auto link, i.e. not inside =\[[square brackets]\]= https://www.example.com
|
||||||
** Footnotes
|
** Footnotes
|
||||||
- normal footnote reference [fn:1]
|
- normal footnote reference [fn:1]
|
||||||
- further references to the same footnote should not [fn:1] render duplicates in the footnote list
|
- further references to the same footnote should not [fn:1] render duplicates in the footnote list
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue