117 lines
2.4 KiB
Go
117 lines
2.4 KiB
Go
|
package link
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"fmt"
|
||
|
"html/template"
|
||
|
"io"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
type Tree struct {
|
||
|
cur rng
|
||
|
children []Tree
|
||
|
}
|
||
|
|
||
|
func (t Tree) text(inp string) string {
|
||
|
return inp[t.cur.start:t.cur.end]
|
||
|
}
|
||
|
|
||
|
type rng struct {
|
||
|
start, end int
|
||
|
typ string
|
||
|
}
|
||
|
|
||
|
type Parser struct {
|
||
|
markers []int
|
||
|
ranges []rng
|
||
|
stack []Tree
|
||
|
}
|
||
|
|
||
|
func (p *Parser) pushMarker(start int) {
|
||
|
p.markers = append(p.markers, start)
|
||
|
p.stack = append(p.stack, Tree{})
|
||
|
}
|
||
|
|
||
|
func (p *Parser) popMarker(end int, typ string) {
|
||
|
start := p.markers[len(p.markers)-1]
|
||
|
p.markers = p.markers[:len(p.markers)-1]
|
||
|
cur := rng{start, end, typ}
|
||
|
top := p.stack[len(p.stack)-1]
|
||
|
top.cur = cur
|
||
|
p.stack = p.stack[:len(p.stack)-1]
|
||
|
p.stack[len(p.stack)-1].children = append(p.stack[len(p.stack)-1].children, top)
|
||
|
p.ranges = append(p.ranges, cur)
|
||
|
}
|
||
|
func (p *Parser) output() Tree {
|
||
|
return p.stack[0].children[0]
|
||
|
}
|
||
|
|
||
|
func ShowTree(input string, root Tree, indent int) {
|
||
|
if root.cur.start != root.cur.end {
|
||
|
fmt.Printf("%s + (%s, %d-%d) %q\n", strings.Repeat(" |", indent), root.cur.typ, root.cur.start, root.cur.end, input[root.cur.start:root.cur.end])
|
||
|
} else {
|
||
|
indent -= 1
|
||
|
}
|
||
|
for _, c := range root.children {
|
||
|
ShowTree(input, c, indent+1)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func formatTitle(w io.Writer, input string, root Tree, indent int) {
|
||
|
typ := root.cur.typ
|
||
|
text := input[root.cur.start:root.cur.end]
|
||
|
if typ == "text" {
|
||
|
w.Write([]byte(text))
|
||
|
}
|
||
|
if typ == "link" {
|
||
|
w.Write([]byte(fmt.Sprintf(`<a href="%s">`, root.children[1].text(input))))
|
||
|
w.Write([]byte(text))
|
||
|
w.Write([]byte(`</a>`))
|
||
|
return
|
||
|
}
|
||
|
|
||
|
for _, c := range root.children {
|
||
|
formatTitle(w, input, c, indent+1)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func FormatHtmlTitle(input string) template.HTML {
|
||
|
p := Parser{}
|
||
|
root := p.Parse(input)
|
||
|
sb := &bytes.Buffer{}
|
||
|
formatTitle(sb, input, root, 0)
|
||
|
return template.HTML(sb.String())
|
||
|
}
|
||
|
|
||
|
func (p *Parser) Parse(input string) Tree {
|
||
|
p.stack = append(p.stack, Tree{})
|
||
|
|
||
|
i := 0
|
||
|
|
||
|
p.pushMarker(i)
|
||
|
for i < len(input) {
|
||
|
p.pushMarker(i)
|
||
|
for i < len(input) && (input[i] != '[' && input[i] != ']') {
|
||
|
i++
|
||
|
}
|
||
|
p.popMarker(i, "text")
|
||
|
if i+2 <= len(input) && input[i:i+2] == "[[" {
|
||
|
p.pushMarker(i)
|
||
|
p.pushMarker(i)
|
||
|
i += 2
|
||
|
p.popMarker(i, "start link tag")
|
||
|
p.pushMarker(i)
|
||
|
} else if i+2 <= len(input) && input[i:i+2] == "]]" {
|
||
|
p.popMarker(i, "link text")
|
||
|
p.pushMarker(i)
|
||
|
i += 2
|
||
|
p.popMarker(i, "end link tag")
|
||
|
p.popMarker(i, "link")
|
||
|
}
|
||
|
}
|
||
|
p.popMarker(i, "full text")
|
||
|
|
||
|
return p.output()
|
||
|
}
|